Skip to content

Instantly share code, notes, and snippets.

@ivandrofly
Last active September 10, 2023 04:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ivandrofly/b1542455afa1dee3cfb5e9299d3cb228 to your computer and use it in GitHub Desktop.
Save ivandrofly/b1542455afa1dee3cfb5e9299d3cb228 to your computer and use it in GitHub Desktop.
Keep an Eye on SRP - That Might Just Save Your Broken Code Base
// https://youtu.be/vTnlFdRf1Kc
var martinFowler = new Person(10, "Martin Fowler);
var server = new DemoDiscountServer(person);
var book = new Book("Design Patern", new List<Person>{martinFowler});
var discountApplications = server.GetDiscountAmounts(10, new DiscountContext(book))
public interface IDiscount
{
IEnumerable<DiscountApplication> GetDiscountAmounts(Money price, DiscountContext context);
}
public record DiscountApplication(string Label, Money amount);
public record Book(string Title, List<Person> Authors);
public record DiscountContext(Book? book);
public record Person(int Id, string Name);
public class DemoDiscountServer : IDiscountServer
{
private readonly Person _martinFowler;
public DemoDiscountServer(Person author) => _martinFowler = author);
public IDiscount GetDiscounts() =>
GetPatternBooksDiscount(.30M).WhenBookAuthorBy(_martinFowler)
.OrElse(GetPatternBooksDiscount(.20M))
.OrElse(new RelativeDiscount(.10M).WhenPositivePrice().OnAllBooks());
private Discount GetPatternBooksDiscount(decimal factor) =>
new RelativeDiscount(factor).WhenPositivePrice().WhenTitleContains("pattern");
}
public static class DiscountExtensions
{
public static IDiscount WhenBookAuthorBy(this IDiscount discount, Person author)
{
return new BookAuthorDiscount(person, discount)
}
public static IDiscount WhenPositivePrice(this IDiscount discount) // relativeDiscount
{
return new PositivePriceDiscount(discount)
}
public static IDiscount WhenTitleContains(this IDiscount discount, string pattern) // relativeDiscount
{
// the registration is done is this order
// RelativeDiscount => PositivePriceDiscount => TitleContentDiscount
// the call will be done in this order
// RelativeDiscount <= PositivePriceDiscount <= TitleContentDiscount
return new TitleContentDiscount(pattner, discount) //
}
public static IDiscount GetPatternBook(this IDiscount)
{
return new RelativeDiscount(factory).WhenPositivePrice().WhenTitleContains("pattern");
}
public static Discount OrElse(this IDiscount discount, IDiscount elseDiscount)
{
// TODO:
// has to return something that will be a ElseDiscount which will call its
// GetDiscountAmounts(..) if `discount`.GetDiscountAmounts return empty set
return elseDiscount;
}
public static Enumerable<DiscountApplication> OnAllBooks(IDiscount discount) // PositivePriceDiscount
{
return discount.GetDiscountAmounts().WithSuffix(" on all books)";
}
}
internal static class DisocuntApplicationExtensions
{
internal static IEnumerable<DiscountApplication> WithSuffix(this IEnumerable<DiscountApplication> discounts, string suffix)
{
return discounts.Select(discount => discount with {Label = discount.Label + suffix});
}
}
public class RelativeDiscount : IDiscount
{
public RelativeDiscountd(decimal factor) => _factor = factory > 0 && factor < 1 ? factory
: throw new ArgumentException("Multiplying factor must be positive and smaller than 1.");
public IEnumerable<DiscountApplication> GetDiscountAmounts(Money price, DiscountContext context) =>
price == Money.Zero ? Enumerable.Empty<DisocuntApplication>() // can remove?
: new[] { new DiscountApplication($"{_factor:P2} discount", price * _factor) };
}
public abstract class RestrictedDiscount : IDiscount
{
private readonly IDiscount _other;
private readonly string _labelSuffix;
protected RestrictedDiscount(IDiscount other, string labelSuffix) => (_other, _labelSuffix) => (other, labelSuffix);
public IEnumerable<DiscountApplication> GetDiscountAmounts(Money price, DiscountContext context) =>
AppliesTo(context) ? ApplyTo(price, context).WithSuffix(_labelSuffix)
: IEnumerable.Empty<DiscountApplication>();
protected IEnumerable<DiscountApplication> ApplyTo(Money price, DiscountContext context) =>
_other.GetDiscountAmounts(price, context);
protected abstract bool AppliesTo(DiscountContext context);
}
public class NoDiscount : IDiscount
{
public IEnumerable<DiscountApplication> GetDiscountAmounts(Money price, DiscountContext context) =>
Enumerable.Empty<DiscountAppliction>();
}
public class ElseDiscount : IDiscount
{
private readonly IDiscount _ifDiscount;
private readonly IDiscount _elseDicount;
ElseDiscount(IDiscount ifDiscount, IDiscount elseDiscount) => (_ifDiscount, _elseDicount) = (ifDiscount, elseDiscount)
public IEnumerable<DiscountApplication> GetDiscountAmounts(Money price, DiscountContext context)
{
// NOTE: I suspect this is the implementation
var appDiscounts = ifDiscount.GetDiscountAmounts(price, context).ToList();
appDiscounts.Count > 0 ? appDiscounts : elseDiscount.GetDiscountAmounts(price, context);
}
}
public class TitleContentDiscount : RestrictedDiscount
{
private readonly IDiscount _other;
public BookAuthorDiscount(string titleSubstring, IDiscount other)
:base(other, $" when by '{titleSubstring}' in the title") => _titleSubstring != string.IsNullOrEmpty(titleString) ? titleString
: throw new ArgumentException("....");
protected override bool AppliesTo(DiscountContext context) =>
context.Book?.Title.Contains(_titleSubstring, StringComparison.InvariantCultureIgnoreCase) ?? false;
}
public class BookAuthorDiscount : RestrictedDiscount
{
private readonly IDiscount _other;
public BookAuthorDiscount(Person author, IDiscount other)
:base(other, $" authored by {author.FullName}") => _author = author;
protected override bool AppliesTo(DiscountContext context) =>
context.Book?.Authors.Any(author => author.Id => _author.Id) ?? false;
}
public class PositivePriceDiscount : IDiscount
{
private readonly IDiscount _other;
public PositivePriceDiscount(IDiscount other) => _other = other;
public IEnumerable<DiscountApplication> GetDiscountAmounts(Money price, DiscountContext context) =>
price == Money.Zero ? Enumerable.Empty<DisocuntApplication>()
: _other.GetDiscountAmounts(price, context);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment