diff --git a/Ch3Beverage/Beverage.cs b/Ch3Beverage/Beverage.cs index 05e7236..1d78396 100644 --- a/Ch3Beverage/Beverage.cs +++ b/Ch3Beverage/Beverage.cs @@ -6,6 +6,11 @@ public abstract class Beverage { public abstract double Cost(); } +// public static class BeveragePrintExtension { +// public static void Print(this Beverage value) { +// Console.WriteLine($"{value.Description} ${value.Cost()}"); +// } +// } /* The only real benefit of putting the constructor in CondimentDecorator is if you need to: Add validation logic (e.g., null checks) once for all decorators @@ -18,7 +23,15 @@ public abstract class Beverage { but not a hard rule. Your implementation follows the Decorator pattern correctly. */ -public abstract class CondimentDecorator : Beverage; +public abstract class CondimentDecorator(Beverage beverage) : Beverage { + protected Beverage Beverage { get; } = beverage; + /* + * Adding { get; } this Beverage is not a Field but a read-only Property + * providing a contract that child classes cannot change this value. + * Leaving a normal field `protected Beverage Beverage = beverage` allows + * child classes to freely reassign it. + */ +} public class Espresso : Beverage { public override string Description => "Espresso"; @@ -44,8 +57,21 @@ public class Decaf : Beverage { public override double Cost() => 1.05; } -public class Mocha(Beverage beverage) : CondimentDecorator { - public override string Description => $"{beverage.Description}, Mocha"; +// Yes, this approach is really uglier because it relies aggressively +// on inheritance (protected "invisible" variable) instead of basic explicit composition. +// But it allows code reuse at the level of the decorator base class... +public class Mocha(Beverage beverage) : CondimentDecorator(beverage) { + public override string Description => $"{Beverage.Description}, Mocha"; - public override double Cost() => beverage.Cost() + 0.20; + public override double Cost() => Beverage.Cost() + 0.20; +} + +public class Soy(Beverage beverage) : CondimentDecorator(beverage) { + public override string Description => $"{Beverage.Description}, Soy"; + public override double Cost() => Beverage.Cost() + 0.15; +} + +public class Whip(Beverage beverage) : CondimentDecorator(beverage) { + public override string Description => $"{Beverage.Description}, Whipped"; + public override double Cost() => Beverage.Cost() + 0.10; } \ No newline at end of file diff --git a/Ch3Beverage/Program.cs b/Ch3Beverage/Program.cs index e5dff12..ee58472 100644 --- a/Ch3Beverage/Program.cs +++ b/Ch3Beverage/Program.cs @@ -1,3 +1,24 @@ // See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); \ No newline at end of file +using Ch3Beverage; + + +Beverage beverage = new Espresso(); +beverage.Print(); +Beverage beverage2 = new DarkRoast(); +beverage2 = new Mocha(new Mocha(beverage2)); +beverage2 = new Whip(beverage2); +beverage2.Print(); +Beverage beverage3 = new HouseBlend(); +beverage3 = new Soy(beverage3); +beverage3 = new Mocha(beverage3); +beverage3 = new Whip(beverage3); +beverage3.Print(); + +public static class BeveragePrintExtension { + // Bored, I've mode an extension method to easily print a Beverage. LOL. + // Because Bored, I didn't wrap this in a namespace... in real code it's necessary. + public static void Print(this Beverage value) { + Console.WriteLine($"{value.Description} ${value.Cost()}"); + } +} \ No newline at end of file