Last active
September 10, 2023 04:23
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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