├── .gitattributes ├── 9781484261798.jpg ├── Behavioral ├── ChainOfResponsibility │ ├── BrokerChain.cs │ ├── Exercise.cs │ └── MethodChain.cs ├── Command │ ├── Command.cs │ ├── CompositeCommand.cs │ ├── Exercise.cs │ ├── FunctionalCommand.cs │ ├── GeneratedCommands.cs │ └── GeneratedCommands.tt ├── Interpreter │ ├── Exercise.cs │ └── Handmade.cs ├── Iterator │ ├── ArrayBackedProperties.cs │ ├── Exercise.cs │ └── TreeTraversal.cs ├── Mediator │ ├── ChatRoom.cs │ ├── Exercise.cs │ ├── MediatorWithEvents.cs │ ├── MediatorWithRx.cs │ └── MediatrDemo │ │ ├── App.config │ │ ├── MediatrDemo.csproj │ │ ├── Program.cs │ │ ├── Properties │ │ └── AssemblyInfo.cs │ │ ├── bin │ │ └── Debug │ │ │ ├── Autofac.dll │ │ │ ├── Autofac.xml │ │ │ ├── JetBrains.Annotations.dll │ │ │ ├── JetBrains.Annotations.xml │ │ │ ├── MediatR.dll │ │ │ ├── MediatrDemo.exe │ │ │ ├── MediatrDemo.exe.config │ │ │ └── MediatrDemo.pdb │ │ ├── obj │ │ └── Debug │ │ │ ├── DesignTimeResolveAssemblyReferencesInput.cache │ │ │ ├── MediatrDemo.csproj.CopyComplete │ │ │ ├── MediatrDemo.csproj.CoreCompileInputs.cache │ │ │ ├── MediatrDemo.csproj.FileListAbsolute.txt │ │ │ ├── MediatrDemo.csprojAssemblyReference.cache │ │ │ ├── MediatrDemo.exe │ │ │ ├── MediatrDemo.pdb │ │ │ ├── TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs │ │ │ ├── TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs │ │ │ └── TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs │ │ └── packages.config ├── Memento │ ├── Exercise.cs │ ├── Memento.cs │ └── UndoRedo.cs ├── NullObject │ ├── DynamicNullObject.cs │ ├── Exercise.cs │ └── NullObject.cs ├── Observer │ ├── BidirectionalObserver.cs │ ├── ContainerWireup.cs │ ├── Events.cs │ ├── Exercise.cs │ ├── ObserverInterfaces.cs │ ├── PropertyDependencies.cs │ └── WeakEventPattern.cs ├── State │ ├── Classic.cs │ ├── Exercise.cs │ ├── Handmade.cs │ ├── Stateless.cs │ ├── StatelessLightSwitch.cs │ ├── SwitchBased.cs │ └── SwitchExpressions.cs ├── Strategy │ ├── ComparisonStrategies.cs │ ├── Dynamic.cs │ ├── Exercise.cs │ └── Static.cs ├── TemplateMethod │ ├── Exercise.cs │ ├── FunctionalTemplateMethod.cs │ └── TemplateMethod.cs └── Visitor │ ├── Acyclic.cs │ ├── Classic.cs │ ├── Dispatch.cs │ ├── Dynamic.cs │ ├── Exercise.cs │ ├── ExtensionMethods.cs │ ├── Intrusive.cs │ ├── Reflective.cs │ └── ReflectiveExtension.cs ├── Contributing.md ├── Creational ├── Builder │ ├── Builder.cs │ ├── BuilderFacets.cs │ ├── BuilderInheritance.cs │ ├── BuilderParameter.cs │ ├── Exercise.cs │ └── FunctionalBuilder.cs ├── Factories │ ├── AbstractFactory.cs │ ├── AsyncFactory.cs │ ├── Exercise.cs │ ├── Factory.cs │ └── FactoryFamilies.cs ├── Prototype │ ├── CopyConstructors.cs │ ├── CopyThroughSerialization.cs │ ├── Exercise.cs │ ├── ICloneableIsBad.cs │ └── PrototypeFactory.cs └── Singleton │ ├── AmbientContext.cs │ ├── Exercise.cs │ ├── Monostate.cs │ ├── Multiton.cs │ ├── PerThreadSingleton.cs │ ├── Singleton.cs │ ├── SingletonInDI.cs │ └── capitals.txt ├── Functional └── FunctionalDesignPatternDemos │ ├── FunctionalBuilder.fs │ ├── FunctionalDecorator.fs │ ├── FunctionalDesignPatternDemos.fsproj │ ├── FunctionalFactory.fs │ ├── FunctionalStrategy.fs │ ├── Interpreter.fs │ ├── Program.fs │ ├── TemplateMethod.fs │ ├── bin │ └── Debug │ │ └── netcoreapp2.1 │ │ ├── FunctionalDesignPatternDemos.deps.json │ │ ├── FunctionalDesignPatternDemos.dll │ │ ├── FunctionalDesignPatternDemos.pdb │ │ ├── FunctionalDesignPatternDemos.runtimeconfig.dev.json │ │ └── FunctionalDesignPatternDemos.runtimeconfig.json │ └── obj │ ├── Debug │ └── netcoreapp2.1 │ │ ├── FunctionalDesignPatternDemos.AssemblyInfo.fs │ │ ├── FunctionalDesignPatternDemos.AssemblyInfoInputs.cache │ │ ├── FunctionalDesignPatternDemos.assets.cache │ │ ├── FunctionalDesignPatternDemos.dll │ │ ├── FunctionalDesignPatternDemos.fsproj.CoreCompileInputs.cache │ │ ├── FunctionalDesignPatternDemos.fsproj.FileListAbsolute.txt │ │ ├── FunctionalDesignPatternDemos.fsprojAssemblyReference.cache │ │ └── FunctionalDesignPatternDemos.pdb │ ├── FunctionalDesignPatternDemos.fsproj.nuget.cache │ ├── FunctionalDesignPatternDemos.fsproj.nuget.dgspec.json │ ├── FunctionalDesignPatternDemos.fsproj.nuget.g.props │ ├── FunctionalDesignPatternDemos.fsproj.nuget.g.targets │ ├── project.assets.json │ ├── project.nuget.cache │ └── project.packagespec.json ├── LICENSE.txt ├── README.md ├── SOLID ├── DIP.cs ├── ISP.cs ├── LSP.cs ├── OCP.cs └── SRP.cs └── Structural ├── Adapter ├── AdapterDI.cs ├── Exercise.cs ├── GenericValueAdapter.cs ├── Lazy.cs ├── NoCaching.cs ├── PropertySurrogate.cs ├── WithCaching.cs └── str.cs ├── Bridge ├── Bridge.cs └── Exercise.cs ├── Composite ├── Exercise.cs ├── GeometricShapes.cs └── NeuralNetworks.cs ├── Decorator ├── AdapterDecorator.cs ├── CodeBuilder.cs ├── Decorator.cs ├── Exercise.cs ├── Mixins.cs └── MultipleInheritance.cs ├── Facade ├── Exercise.cs └── MagicSquare.cs ├── Flyweight ├── Exercise.cs ├── TextFormatting.cs └── Users.cs └── Proxy ├── BitFragging.cs ├── CompositeProxy.cs ├── DynamicProxy.cs ├── Exercise.cs ├── PropertyProxy.cs ├── ProtectionProxy.cs ├── ResultOfT.cs ├── SoACompositeProxy.cs ├── ValueProxy.cs ├── ViewModel.cs └── VirtualProxy.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /9781484261798.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/9781484261798.jpg -------------------------------------------------------------------------------- /Behavioral/ChainOfResponsibility/MethodChain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using static System.Console; 4 | 5 | namespace DotNetDesignPatternDemos.Behavioral.ChainOfResponsibility.MethodChain 6 | { 7 | public class Creature 8 | { 9 | public string Name; 10 | public int Attack, Defense; 11 | 12 | public Creature(string name, int attack, int defense) 13 | { 14 | Name = name; 15 | Attack = attack; 16 | Defense = defense; 17 | } 18 | 19 | public override string ToString() 20 | { 21 | return $"{nameof(Name)}: {Name}, {nameof(Attack)}: {Attack}, {nameof(Defense)}: {Defense}"; 22 | } 23 | } 24 | 25 | public class CreatureModifier 26 | { 27 | protected Creature creature; 28 | protected CreatureModifier next; 29 | 30 | public CreatureModifier(Creature creature) 31 | { 32 | this.creature = creature; 33 | } 34 | 35 | public void Add(CreatureModifier cm) 36 | { 37 | if (next != null) next.Add(cm); 38 | else next = cm; 39 | } 40 | 41 | public virtual void Handle() => next?.Handle(); 42 | } 43 | 44 | public class NoBonusesModifier : CreatureModifier 45 | { 46 | public NoBonusesModifier(Creature creature) 47 | : base(creature) {} 48 | 49 | public override void Handle() 50 | { 51 | // nothing 52 | WriteLine("No bonuses for you!"); 53 | } 54 | } 55 | 56 | public class DoubleAttackModifier : CreatureModifier 57 | { 58 | public DoubleAttackModifier(Creature creature) 59 | : base(creature) {} 60 | 61 | public override void Handle() 62 | { 63 | WriteLine($"Doubling {creature.Name}'s attack"); 64 | creature.Attack *= 2; 65 | base.Handle(); 66 | } 67 | } 68 | 69 | public class IncreaseDefenseModifier : CreatureModifier 70 | { 71 | public IncreaseDefenseModifier(Creature creature) 72 | : base(creature) {} 73 | 74 | public override void Handle() 75 | { 76 | if (creature.Attack <= 2) 77 | { 78 | WriteLine($"Increasing {creature.Name}'s defense"); 79 | creature.Defense++; 80 | } 81 | 82 | base.Handle(); 83 | } 84 | } 85 | 86 | public class Demo 87 | { 88 | static void Main(string[] args) 89 | { 90 | var goblin = new Creature("Goblin", 1, 1); 91 | WriteLine(goblin); 92 | 93 | var root = new CreatureModifier(goblin); 94 | 95 | //root.Add(new NoBonusesModifier(goblin)); 96 | 97 | root.Add(new DoubleAttackModifier(goblin)); 98 | root.Add(new DoubleAttackModifier(goblin)); 99 | 100 | root.Add(new IncreaseDefenseModifier(goblin)); 101 | 102 | // eventually... 103 | root.Handle(); 104 | WriteLine(goblin); 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /Behavioral/Command/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace DotNetDesignPatternDemos.Behavioral.Command 5 | { 6 | namespace Coding.Exercise 7 | { 8 | public class Command 9 | { 10 | public enum Action 11 | { 12 | Deposit, 13 | Withdraw 14 | } 15 | 16 | public Action TheAction; 17 | public int Amount; 18 | public bool Success; 19 | } 20 | 21 | public class Account 22 | { 23 | public int Balance { get; set; } 24 | 25 | public void Process(Command c) 26 | { 27 | switch (c.TheAction) 28 | { 29 | case Command.Action.Deposit: 30 | Balance += c.Amount; 31 | c.Success = true; 32 | break; 33 | case Command.Action.Withdraw: 34 | c.Success = Balance >= c.Amount; 35 | if (c.Success) Balance -= c.Amount; 36 | break; 37 | default: 38 | throw new ArgumentOutOfRangeException(); 39 | } 40 | } 41 | } 42 | } 43 | 44 | namespace Coding.Exercise.Tests 45 | { 46 | [TestFixture] 47 | public class TestSuite 48 | { 49 | [Test] 50 | public void Test() 51 | { 52 | var a = new Account(); 53 | 54 | var command = new Command{Amount = 100, TheAction = Command.Action.Deposit}; 55 | a.Process(command); 56 | 57 | Assert.That(a.Balance, Is.EqualTo(100)); 58 | Assert.IsTrue(command.Success); 59 | 60 | command = new Command{Amount = 50, TheAction = Command.Action.Withdraw}; 61 | a.Process(command); 62 | 63 | Assert.That(a.Balance, Is.EqualTo(50)); 64 | Assert.IsTrue(command.Success); 65 | 66 | command = new Command { Amount = 150, TheAction = Command.Action.Withdraw }; 67 | a.Process(command); 68 | 69 | Assert.That(a.Balance, Is.EqualTo(50)); 70 | Assert.IsFalse(command.Success); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /Behavioral/Command/FunctionalCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DotNetDesignPatternDemos.Behavioral.FunctionalCommand 5 | { 6 | public class FunctionalCommand 7 | { 8 | public class BankAccount 9 | { 10 | public int Balance; 11 | } 12 | 13 | public void Deposit(BankAccount account, int amount) 14 | { 15 | account.Balance += amount; 16 | } 17 | 18 | public void Withdraw(BankAccount account, int amount) 19 | { 20 | if (account.Balance >= amount) 21 | account.Balance -= amount; 22 | } 23 | 24 | FunctionalCommand() 25 | { 26 | var ba = new BankAccount(); 27 | var commands = new List(); 28 | 29 | commands.Add(() => Deposit(ba, 100)); 30 | commands.Add(() => Withdraw(ba, 100)); 31 | 32 | commands.ForEach(c => c()); 33 | } 34 | 35 | public static void Main(string[] args) 36 | { 37 | new FunctionalCommand(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Behavioral/Command/GeneratedCommands.cs: -------------------------------------------------------------------------------- 1 | namespace DotNetDesignPatternDemos.Command.Generated 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class Person 6 | { 7 | public long Id; 8 | public string Name; 9 | } 10 | 11 | public class Repository 12 | { 13 | public List Persons = new List(); 14 | } 15 | 16 | public class CreatePersonCommand 17 | { 18 | public string Name { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /Behavioral/Command/GeneratedCommands.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="false" language="C#" 2 | compilerOptions="/langversion:6" #> 3 | <#@ output extension=".cs" #> 4 | <#@ import namespace="System.Collections.Generic" #> 5 | <#@ import namespace="System.Text" #> 6 | <# 7 | Entities.Add(new Entity 8 | { 9 | Name = "Person", 10 | Properties = new List<(string name, string type)> 11 | { 12 | ("Name", "string") 13 | } 14 | }); 15 | #> 16 | namespace DotNetDesignPatternDemos.Command.Generated 17 | { 18 | using System.Collections.Generic; 19 | <# foreach (var e in Entities) { #> 20 | public class <#= e.Name #> 21 | { 22 | <# foreach (var (name, type) in e.Properties) { #> 23 | public long Id; 24 | public <#= type #> <#= name #>; 25 | <# } #> 26 | }<# } #> 27 | 28 | public class Repository 29 | {<# foreach (var e in Entities) { #> 30 | public List<<#= e.Name #>> <#= e.Name#>s = new List<<#= e.Name #>>(); 31 | <# } #> 32 | } 33 | 34 | <# foreach (var e in Entities) { #> 35 | public class Create<#= e.Name #>Command 36 | { 37 | <# foreach (var (name, type) in e.Properties) { #> 38 | public <#= type #> <#= name #> { get; set; } 39 | <# }#> 40 | } 41 | <# } #> 42 | } 43 | 44 | <#+ 45 | public class Entity 46 | { 47 | public string Name; 48 | public List<(string name, string type)> Properties; 49 | } 50 | 51 | public List Entities = new List(); 52 | 53 | #> -------------------------------------------------------------------------------- /Behavioral/Interpreter/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | using NUnit.Framework; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.Interpreter 7 | { 8 | namespace Coding.Exercise 9 | { 10 | public class ExpressionProcessor 11 | { 12 | public Dictionary Variables = new Dictionary(); 13 | 14 | public enum NextOp 15 | { 16 | Nothing, 17 | Plus, 18 | Minus 19 | } 20 | 21 | public int Calculate(string expression) 22 | { 23 | int current = 0; 24 | var nextOp = NextOp.Nothing; 25 | 26 | var parts = Regex.Split(expression, @"(?<=[+-])"); 27 | 28 | foreach (var part in parts) 29 | { 30 | var noOp = part.Split(new[] {"+", "-"}, StringSplitOptions.RemoveEmptyEntries); 31 | var first = noOp[0]; 32 | int value, z; 33 | 34 | if (int.TryParse(first, out z)) 35 | value = z; 36 | else if (first.Length == 1 && Variables.ContainsKey(first[0])) 37 | value = Variables[first[0]]; 38 | else return 0; 39 | 40 | switch (nextOp) 41 | { 42 | case NextOp.Nothing: 43 | current = value; 44 | break; 45 | case NextOp.Plus: 46 | current += value; 47 | break; 48 | case NextOp.Minus: 49 | current -= value; 50 | break; 51 | } 52 | 53 | if (part.EndsWith("+")) nextOp = NextOp.Plus; 54 | else if (part.EndsWith("-")) nextOp = NextOp.Minus; 55 | } 56 | return current; 57 | } 58 | } 59 | 60 | namespace Coding.Exercise.Tests 61 | { 62 | [TestFixture] 63 | public class TestSuite 64 | { 65 | [Test] 66 | public void Test() 67 | { 68 | var ep = new ExpressionProcessor(); 69 | ep.Variables.Add('x', 5); 70 | 71 | Assert.That(ep.Calculate("1"), Is.EqualTo(1)); 72 | 73 | Assert.That(ep.Calculate("1+2"), Is.EqualTo(3)); 74 | 75 | Assert.That(ep.Calculate("1+x"), Is.EqualTo(6)); 76 | 77 | Assert.That(ep.Calculate("1+xy"), Is.EqualTo(0)); 78 | } 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Behavioral/Iterator/ArrayBackedProperties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.Iterator.ArrayBackedProperties 7 | { 8 | public class Creature : IEnumerable 9 | { 10 | private int [] stats = new int[3]; 11 | 12 | public IEnumerable Stats => stats; 13 | 14 | private const int strength = 0; 15 | 16 | public int Strength 17 | { 18 | get => stats[strength]; 19 | set => stats[strength] = value; 20 | } 21 | 22 | public int Agility { get; set; } 23 | public int Intelligence { get; set; } 24 | 25 | 26 | 27 | public double AverageStat => stats.Average(); 28 | 29 | //public double AverageStat => SumOfStats / 3.0; 30 | 31 | //public double SumOfStats => Strength + Agility + Intelligence; 32 | public double SumOfStats => stats.Sum(); 33 | 34 | //public double MaxStat => Math.Max( 35 | // Math.Max(Strength, Agility), Intelligence); 36 | 37 | public double MaxStat => stats.Max(); 38 | 39 | public IEnumerator GetEnumerator() 40 | { 41 | return stats.AsEnumerable().GetEnumerator(); 42 | } 43 | 44 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 45 | 46 | public int this[int index] 47 | { 48 | get => stats[index]; 49 | set => stats[index] = value; 50 | } 51 | } 52 | 53 | public class Demo 54 | { 55 | static void Main(string[] args) 56 | { 57 | var creature = new Creature(); 58 | creature.Strength = 10; 59 | creature.Intelligence = 11; 60 | creature.Agility = 12; 61 | Console.WriteLine($"Creature has average stat = {creature.AverageStat}, " + 62 | $"max stat = {creature.MaxStat}, " + 63 | $"sum of stats = {creature.SumOfStats}."); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Behavioral/Iterator/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | 5 | namespace DotNetDesignPatternDemos.Behavioral.Iterator 6 | { 7 | namespace Coding.Exercise 8 | { 9 | public class Node 10 | { 11 | public T Value; 12 | public Node Left, Right; 13 | public Node Parent; 14 | 15 | public Node(T value) 16 | { 17 | Value = value; 18 | } 19 | 20 | public Node(T value, Node left, Node right) 21 | { 22 | Value = value; 23 | Left = left; 24 | Right = right; 25 | 26 | left.Parent = right.Parent = this; 27 | } 28 | 29 | private IEnumerable> Traverse(Node current) 30 | { 31 | yield return current; 32 | if (current.Left != null) 33 | { 34 | foreach (var left in Traverse(current.Left)) 35 | yield return left; 36 | } 37 | if (current.Right != null) 38 | { 39 | foreach (var right in Traverse(current.Right)) 40 | yield return right; 41 | } 42 | } 43 | 44 | public IEnumerable PreOrder 45 | { 46 | get 47 | { 48 | foreach (var node in Traverse(this)) 49 | yield return node.Value; 50 | } 51 | } 52 | } 53 | } 54 | 55 | namespace Coding.Exercise.Tests 56 | { 57 | [TestFixture] 58 | public class TestSuite 59 | { 60 | [Test] 61 | public void Test() 62 | { 63 | var node = new Node('a', 64 | new Node('b', 65 | new Node('c'), 66 | new Node('d')), 67 | new Node('e')); 68 | Assert.That(new string(node.PreOrder.ToArray()), Is.EqualTo("abcde")); 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /Behavioral/Mediator/ChatRoom.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using static System.Console; 4 | 5 | namespace DotNetDesignPatternDemos.Behavioral.Mediator.ChatRoom 6 | { 7 | public class Person 8 | { 9 | public string Name; 10 | public ChatRoom Room; 11 | private List chatLog = new List(); 12 | 13 | public Person(string name) => Name = name; 14 | 15 | public void Receive(string sender, string message) 16 | { 17 | string s = $"{sender}: '{message}'"; 18 | WriteLine($"[{Name}'s chat session] {s}"); 19 | chatLog.Add(s); 20 | } 21 | 22 | public void Say(string message) => Room.Broadcast(Name, message); 23 | 24 | public void PrivateMessage(string who, string message) 25 | { 26 | Room.Message(Name, who, message); 27 | } 28 | } 29 | 30 | public class ChatRoom 31 | { 32 | private List people = new List(); 33 | 34 | public void Broadcast(string source, string message) 35 | { 36 | foreach (var p in people) 37 | if (p.Name != source) 38 | p.Receive(source, message); 39 | } 40 | 41 | public void Join(Person p) 42 | { 43 | string joinMsg = $"{p.Name} joins the chat"; 44 | Broadcast("room", joinMsg); 45 | 46 | p.Room = this; 47 | people.Add(p); 48 | } 49 | 50 | public void Message(string source, string destination, string message) 51 | { 52 | people.FirstOrDefault(p => p.Name == destination)?.Receive(source, message); 53 | } 54 | } 55 | 56 | public class Demo 57 | { 58 | static void Main(string[] args) 59 | { 60 | var room = new ChatRoom(); 61 | 62 | var john = new Person("John"); 63 | var jane = new Person("Jane"); 64 | 65 | room.Join(john); 66 | room.Join(jane); 67 | 68 | john.Say("hi room"); 69 | jane.Say("oh, hey john"); 70 | 71 | var simon = new Person("Simon"); 72 | room.Join(simon); 73 | simon.Say("hi everyone!"); 74 | 75 | jane.PrivateMessage("Simon", "glad you could join us!"); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Behavioral/Mediator/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace DotNetDesignPatternDemos.Behavioral.Mediator 5 | { 6 | namespace Coding.Exercise 7 | { 8 | public class Participant 9 | { 10 | private readonly Mediator mediator; 11 | public int Value { get; set; } 12 | 13 | public Participant(Mediator mediator) 14 | { 15 | this.mediator = mediator; 16 | mediator.Alert += Mediator_Alert; 17 | } 18 | 19 | private void Mediator_Alert(object sender, int e) 20 | { 21 | if (sender != this) 22 | Value += e; 23 | } 24 | 25 | public void Say(int n) 26 | { 27 | mediator.Broadcast(this, n); 28 | } 29 | } 30 | 31 | public class Mediator 32 | { 33 | public void Broadcast(object sender, int n) 34 | { 35 | Alert?.Invoke(sender, n); 36 | } 37 | 38 | public event EventHandler Alert; 39 | } 40 | } 41 | 42 | namespace Coding.Exercise.Tests 43 | { 44 | [TestFixture] 45 | public class TestSuite 46 | { 47 | [Test] 48 | public void Test() 49 | { 50 | Mediator mediator = new Mediator(); 51 | var p1 = new Participant(mediator); 52 | var p2 = new Participant(mediator); 53 | 54 | Assert.That(p1.Value, Is.EqualTo(0)); 55 | Assert.That(p2.Value, Is.EqualTo(0)); 56 | 57 | p1.Say(2); 58 | 59 | Assert.That(p1.Value, Is.EqualTo(0)); 60 | Assert.That(p2.Value, Is.EqualTo(2)); 61 | 62 | p2.Say(4); 63 | 64 | Assert.That(p1.Value, Is.EqualTo(4)); 65 | Assert.That(p2.Value, Is.EqualTo(2)); 66 | } 67 | } 68 | } 69 | 70 | 71 | } -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatorWithEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Activities.Statements; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using static System.Console; 8 | 9 | namespace DotNetDesignPatternDemos.Behavioral.MediatorWithEvents 10 | { 11 | abstract class GameEventArgs : EventArgs 12 | { 13 | public abstract void Print(); 14 | } 15 | 16 | class PlayerScoredEventArgs : GameEventArgs 17 | { 18 | public string PlayerName; 19 | public int GoalsScoredSoFar; 20 | 21 | public PlayerScoredEventArgs 22 | (string playerName, int goalsScoredSoFar) 23 | { 24 | PlayerName = playerName; 25 | GoalsScoredSoFar = goalsScoredSoFar; 26 | } 27 | 28 | public override void Print() 29 | { 30 | WriteLine($"{PlayerName} has scored! " + 31 | $"(their {GoalsScoredSoFar} goal)"); 32 | } 33 | } 34 | 35 | class Game 36 | { 37 | public event EventHandler Events; 38 | 39 | public void Fire(GameEventArgs args) 40 | { 41 | Events?.Invoke(this, args); 42 | } 43 | } 44 | 45 | class Player 46 | { 47 | private string name; 48 | private int goalsScored = 0; 49 | private Game game; 50 | 51 | public Player(string name, Game game) 52 | { 53 | this.name = name; 54 | this.game = game; 55 | } 56 | 57 | public void Score() 58 | { 59 | goalsScored++; 60 | var args = new PlayerScoredEventArgs(name, goalsScored); 61 | game.Fire(args); 62 | } 63 | } 64 | 65 | class Coach 66 | { 67 | private Game game; 68 | 69 | public Coach(Game game) 70 | { 71 | this.game = game; 72 | 73 | // celebrate if player has scored <3 goals 74 | game.Events += (sender, args) => 75 | { 76 | if (args is PlayerScoredEventArgs scored 77 | && scored.GoalsScoredSoFar < 3) 78 | { 79 | WriteLine($"coach says: well done, {scored.PlayerName}"); 80 | } 81 | }; 82 | } 83 | } 84 | 85 | 86 | class Demo 87 | { 88 | public static void Main(string[] args) 89 | { 90 | var game = new Game(); 91 | var player = new Player("Sam", game); 92 | var coach = new Coach(game); 93 | 94 | player.Score(); // coach says: well done, Sam 95 | player.Score(); // coach says: well done, Sam 96 | player.Score(); // ignored by coach 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/MediatrDemo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2B3EB10C-370D-4CBE-843C-73F7CB8040D2} 8 | Exe 9 | MediatrDemo 10 | MediatrDemo 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | latest 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\..\..\packages\Autofac.4.4.0\lib\net45\Autofac.dll 39 | 40 | 41 | ..\..\..\packages\JetBrains.Annotations.2020.1.0-eap6\lib\net20\JetBrains.Annotations.dll 42 | True 43 | 44 | 45 | ..\..\..\packages\MediatR.7.0.0\lib\net461\MediatR.dll 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Autofac; 5 | using JetBrains.Annotations; 6 | using MediatR; 7 | 8 | namespace MediatrDemo 9 | { 10 | public class PongResponse 11 | { 12 | public DateTime Timestamp; 13 | 14 | public PongResponse(DateTime timestamp) 15 | { 16 | Timestamp = timestamp; 17 | } 18 | } 19 | 20 | public class PingCommand : IRequest 21 | { 22 | // nothing here 23 | } 24 | 25 | [UsedImplicitly] 26 | public class PingCommandHandler 27 | : IRequestHandler 28 | { 29 | public async Task Handle(PingCommand request, 30 | CancellationToken cancellationToken) 31 | { 32 | return await Task 33 | .FromResult(new PongResponse(DateTime.UtcNow)) 34 | .ConfigureAwait(false); 35 | } 36 | } 37 | 38 | public class Demo 39 | { 40 | public static async Task Main() 41 | { 42 | var builder = new ContainerBuilder(); 43 | builder.RegisterType() 44 | .As() 45 | .InstancePerLifetimeScope(); // singleton 46 | 47 | builder.Register(context => 48 | { 49 | var c = context.Resolve(); 50 | return t => c.Resolve(t); 51 | }); 52 | 53 | builder.RegisterAssemblyTypes(typeof(Demo).Assembly) 54 | .AsImplementedInterfaces(); 55 | 56 | var container = builder.Build(); 57 | var mediator = container.Resolve(); 58 | var response = await mediator.Send(new PingCommand()); 59 | Console.WriteLine($"We got a pong at {response.Timestamp}"); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MediatrDemo")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MediatrDemo")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2b3eb10c-370d-4cbe-843c-73f7cb8040d2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/bin/Debug/Autofac.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/bin/Debug/Autofac.dll -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/bin/Debug/JetBrains.Annotations.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/bin/Debug/JetBrains.Annotations.dll -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/bin/Debug/MediatR.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/bin/Debug/MediatR.dll -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/bin/Debug/MediatrDemo.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/bin/Debug/MediatrDemo.exe -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/bin/Debug/MediatrDemo.exe.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/bin/Debug/MediatrDemo.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/bin/Debug/MediatrDemo.pdb -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.csproj.CopyComplete: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.csproj.CopyComplete -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.csproj.CoreCompileInputs.cache: -------------------------------------------------------------------------------- 1 | f034b28f380bcb5179a7aa6f484bc5c5a76af046 2 | -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.csproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\obj\Debug\MediatrDemo.csprojAssemblyReference.cache 2 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\MediatrDemo.exe.config 3 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\MediatrDemo.exe 4 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\MediatrDemo.pdb 5 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\Autofac.dll 6 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\MediatR.dll 7 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\Autofac.xml 8 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\obj\Debug\MediatrDemo.csproj.CopyComplete 9 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\obj\Debug\MediatrDemo.exe 10 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\obj\Debug\MediatrDemo.pdb 11 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\obj\Debug\MediatrDemo.exe 12 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\obj\Debug\MediatrDemo.pdb 13 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\MediatrDemo.exe.config 14 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\MediatrDemo.exe 15 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\MediatrDemo.pdb 16 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\Autofac.dll 17 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\JetBrains.Annotations.dll 18 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\MediatR.dll 19 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\Autofac.xml 20 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\JetBrains.Annotations.xml 21 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\obj\Debug\MediatrDemo.csprojAssemblyReference.cache 22 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\obj\Debug\MediatrDemo.csproj.CopyComplete 23 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\JetBrains.Annotations.dll 24 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Behavioral\Mediator\MediatrDemo\bin\Debug\JetBrains.Annotations.xml 25 | -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.csprojAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.csprojAssemblyReference.cache -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.exe -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/obj/Debug/MediatrDemo.pdb -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Behavioral/Mediator/MediatrDemo/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs -------------------------------------------------------------------------------- /Behavioral/Mediator/MediatrDemo/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Behavioral/Memento/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.Memento.Exercise 7 | { 8 | namespace Coding.Exercise 9 | { 10 | public class Token 11 | { 12 | public int Value = 0; 13 | 14 | public Token(int value) 15 | { 16 | Value = value; 17 | } 18 | } 19 | 20 | public class Memento 21 | { 22 | public List Tokens = new List(); 23 | } 24 | 25 | public class TokenMachine 26 | { 27 | public List Tokens = new List(); 28 | 29 | public Memento AddToken(int value) 30 | { 31 | return AddToken(new Token(value)); 32 | } 33 | 34 | public Memento AddToken(Token token) 35 | { 36 | Tokens.Add(token); 37 | var m = new Memento(); 38 | // a rather roundabout way of cloning 39 | m.Tokens = Tokens.Select(t => new Token(t.Value)).ToList(); 40 | return m; 41 | } 42 | 43 | public void Revert(Memento m) 44 | { 45 | Tokens = m.Tokens.Select(mm => new Token(mm.Value)).ToList(); 46 | } 47 | } 48 | } 49 | 50 | namespace Coding.Exercise.Tests 51 | { 52 | [TestFixture] 53 | public class Tests 54 | { 55 | [Test] 56 | public void SingleTokenTest() 57 | { 58 | var tm = new TokenMachine(); 59 | var m = tm.AddToken(123); 60 | tm.AddToken(456); 61 | tm.Revert(m); 62 | Assert.That(tm.Tokens.Count, Is.EqualTo(1)); 63 | Assert.That(tm.Tokens[0].Value, Is.EqualTo(123)); 64 | } 65 | 66 | [Test] 67 | public void TwoTokenTest() 68 | { 69 | var tm = new TokenMachine(); 70 | tm.AddToken(1); 71 | var m = tm.AddToken(2); 72 | tm.AddToken(3); 73 | tm.Revert(m); 74 | Assert.That(tm.Tokens.Count, Is.EqualTo(2)); 75 | Assert.That(tm.Tokens[0].Value, Is.EqualTo(1), 76 | $"First token should have value 1, you got {tm.Tokens[0].Value}"); 77 | Assert.That(tm.Tokens[1].Value, Is.EqualTo(2)); 78 | } 79 | 80 | [Test] 81 | public void FiddledTokenTest() 82 | { 83 | var tm = new TokenMachine(); 84 | Console.WriteLine("Made a token with value 111 and kept a reference"); 85 | var token = new Token(111); 86 | Console.WriteLine("Added this token to the list"); 87 | tm.AddToken(token); 88 | var m = tm.AddToken(222); 89 | Console.WriteLine("Changed this token's value to 333 :)"); 90 | token.Value = 333; 91 | tm.Revert(m); 92 | 93 | Assert.That(tm.Tokens.Count, Is.EqualTo(2), 94 | $"At this point, token machine should have exactly two tokens, you got {tm.Tokens.Count}"); 95 | 96 | Assert.That(tm.Tokens[0].Value, Is.EqualTo(111), 97 | $"You got the token value wrong here. Hint: did you init the memento by value?"); 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /Behavioral/Memento/Memento.cs: -------------------------------------------------------------------------------- 1 | using static System.Console; 2 | 3 | namespace DotNetDesignPatternDemos.Behavioral.Memento 4 | { 5 | public class Memento 6 | { 7 | public int Balance { get; } 8 | 9 | public Memento(int balance) 10 | { 11 | Balance = balance; 12 | } 13 | } 14 | 15 | public class BankAccount 16 | { 17 | private int balance; 18 | 19 | public BankAccount(int balance) 20 | { 21 | this.balance = balance; 22 | } 23 | 24 | public Memento Deposit(int amount) 25 | { 26 | balance += amount; 27 | return new Memento(balance); 28 | } 29 | 30 | public void Restore(Memento m) 31 | { 32 | balance = m.Balance; 33 | } 34 | 35 | public override string ToString() 36 | { 37 | return $"{nameof(balance)}: {balance}"; 38 | } 39 | } 40 | 41 | public class Demo 42 | { 43 | static void Main(string[] args) 44 | { 45 | var ba = new BankAccount(100); 46 | var m1 = ba.Deposit(50); 47 | var m2 = ba.Deposit(25); 48 | WriteLine(ba); // 175 49 | 50 | // restore to m1 51 | ba.Restore(m1); 52 | WriteLine(ba); // 150 53 | 54 | // restore to m2 55 | ba.Restore(m2); 56 | WriteLine(ba); // 175 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Behavioral/Memento/UndoRedo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using static System.Console; 3 | 4 | namespace DotNetDesignPatternDemos.Behavioral.Memento.UndoRedo 5 | { 6 | public class Memento 7 | { 8 | public int Balance { get; } 9 | 10 | public Memento(int balance) 11 | { 12 | Balance = balance; 13 | } 14 | } 15 | 16 | public class BankAccount // supports undo/redo 17 | { 18 | private int balance; 19 | private List changes = new List(); 20 | private int current; 21 | 22 | public BankAccount(int balance) 23 | { 24 | this.balance = balance; 25 | changes.Add(new Memento(balance)); 26 | } 27 | 28 | public Memento Deposit(int amount) 29 | { 30 | balance += amount; 31 | var m = new Memento(balance); 32 | changes.Add(m); 33 | ++current; 34 | return m; 35 | } 36 | 37 | public void Restore(Memento m) 38 | { 39 | if (m != null) 40 | { 41 | balance = m.Balance; 42 | changes.Add(m); 43 | current = changes.Count - 1; 44 | } 45 | } 46 | 47 | public Memento Undo() 48 | { 49 | if (current > 0) 50 | { 51 | var m = changes[--current]; 52 | balance = m.Balance; 53 | return m; 54 | } 55 | return null; 56 | } 57 | 58 | public Memento Redo() 59 | { 60 | if (current + 1 < changes.Count) 61 | { 62 | var m = changes[++current]; 63 | balance = m.Balance; 64 | return m; 65 | } 66 | return null; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return $"{nameof(balance)}: {balance}"; 72 | } 73 | } 74 | 75 | public class Demo 76 | { 77 | static void Main(string[] args) 78 | { 79 | var ba = new BankAccount(100); 80 | ba.Deposit(50); 81 | ba.Deposit(25); 82 | WriteLine(ba); 83 | 84 | ba.Undo(); 85 | WriteLine($"Undo 1: {ba}"); 86 | ba.Undo(); 87 | WriteLine($"Undo 2: {ba}"); 88 | ba.Redo(); 89 | WriteLine($"Redo 2: {ba}"); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Behavioral/NullObject/DynamicNullObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | using ImpromptuInterface; 4 | using static System.Console; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.NullObject.Dynamic 7 | { 8 | public interface ILog 9 | { 10 | void Info(string msg); 11 | void Warn(string msg); 12 | } 13 | 14 | class ConsoleLog : ILog 15 | { 16 | public void Info(string msg) 17 | { 18 | WriteLine(msg); 19 | } 20 | 21 | public void Warn(string msg) 22 | { 23 | WriteLine("WARNING: " + msg); 24 | } 25 | } 26 | 27 | class OptionalLog: ILog 28 | { 29 | private ILog impl; 30 | public static ILog NoLogging = null; 31 | 32 | public OptionalLog(ILog impl) 33 | { 34 | this.impl = impl; 35 | } 36 | 37 | public void Info(string msg) 38 | { 39 | impl?.Info(msg); 40 | } 41 | 42 | public void Warn(string msg) 43 | { 44 | throw new NotImplementedException(); 45 | } 46 | } 47 | 48 | public class BankAccount 49 | { 50 | private ILog log; 51 | private int balance; 52 | 53 | public BankAccount(ILog log) 54 | { 55 | this.log = new OptionalLog(log); 56 | } 57 | 58 | public void Deposit(int amount) 59 | { 60 | balance += amount; 61 | // check for null everywhere 62 | log?.Info($"Deposited ${amount}, balance is now {balance}"); 63 | } 64 | 65 | public void Withdraw(int amount) 66 | { 67 | if (balance >= amount) 68 | { 69 | balance -= amount; 70 | log?.Info($"Withdrew ${amount}, we have ${balance} left"); 71 | } 72 | else 73 | { 74 | log?.Warn($"Could not withdraw ${amount} because " + 75 | $"balance is only ${balance}"); 76 | } 77 | } 78 | } 79 | 80 | public class Null : DynamicObject where T:class 81 | { 82 | public static T Instance 83 | { 84 | get 85 | { 86 | if (!typeof(T).IsInterface) 87 | throw new ArgumentException("I must be an interface type"); 88 | 89 | return new Null().ActLike(); 90 | } 91 | } 92 | 93 | public override bool TryInvokeMember(InvokeMemberBinder binder, 94 | object[] args, out object result) 95 | { 96 | var name = binder.Name; 97 | result = Activator.CreateInstance(binder.ReturnType); 98 | return true; 99 | } 100 | } 101 | 102 | public class Demo 103 | { 104 | static void Main() 105 | { 106 | var log = Null.Instance; 107 | var ba = new BankAccount(log); 108 | ba.Deposit(100); 109 | ba.Withdraw(200); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /Behavioral/NullObject/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace DotNetDesignPatternDemos.Behavioral.NullObject 5 | { 6 | namespace Coding.Exercise 7 | { 8 | public interface ILog 9 | { 10 | // maximum # of elements in the log 11 | int RecordLimit { get; } 12 | 13 | // number of elements already in the log 14 | int RecordCount { get; set; } 15 | 16 | // expected to increment RecordCount 17 | void LogInfo(string message); 18 | } 19 | 20 | public class Account 21 | { 22 | private ILog log; 23 | 24 | public Account(ILog log) 25 | { 26 | this.log = log; 27 | } 28 | 29 | public void SomeOperation() 30 | { 31 | int c = log.RecordCount; 32 | log.LogInfo("Performing an operation"); 33 | if (c+1 != log.RecordCount) 34 | throw new Exception(); 35 | if (log.RecordCount >= log.RecordLimit) 36 | throw new Exception(); 37 | } 38 | } 39 | 40 | public class NullLog : ILog 41 | { 42 | public int RecordLimit { get; } = int.MaxValue; 43 | public int RecordCount { get; set; } = int.MinValue; 44 | public void LogInfo(string message) 45 | { 46 | ++RecordCount; 47 | } 48 | } 49 | } 50 | 51 | namespace Coding.Exercise.Tests 52 | { 53 | [TestFixture] 54 | public class Tests 55 | { 56 | [Test] 57 | public void SingleCallTest() 58 | { 59 | var a = new Account(new NullLog()); 60 | a.SomeOperation(); 61 | } 62 | 63 | [Test] 64 | public void ManyCallsTest() 65 | { 66 | var a = new Account(new NullLog()); 67 | for (int i = 0; i < 100; ++i) 68 | a.SomeOperation(); 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /Behavioral/NullObject/NullObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | using DotNetDesignPatternDemos.Annotations; 4 | using ImpromptuInterface; 5 | using static System.Console; 6 | 7 | namespace DotNetDesignPatternDemos.Behavioral.NullObject 8 | { 9 | public interface ILog 10 | { 11 | void Info(string msg); 12 | void Warn(string msg); 13 | } 14 | 15 | class ConsoleLog : ILog 16 | { 17 | public void Info(string msg) 18 | { 19 | WriteLine(msg); 20 | } 21 | 22 | public void Warn(string msg) 23 | { 24 | WriteLine("WARNING: " + msg); 25 | } 26 | } 27 | 28 | class OptionalLog: ILog 29 | { 30 | private ILog impl; 31 | 32 | public OptionalLog(ILog impl) 33 | { 34 | this.impl = impl; 35 | } 36 | 37 | public void Info(string msg) 38 | { 39 | impl?.Info(msg); 40 | } 41 | 42 | public void Warn(string msg) 43 | { 44 | impl?.Warn(msg); 45 | } 46 | } 47 | 48 | public class BankAccount 49 | { 50 | private readonly ILog log; 51 | private int balance; 52 | 53 | private const ILog NoLogging = null; 54 | 55 | public BankAccount([CanBeNull] ILog log = NoLogging) 56 | { 57 | this.log = new OptionalLog(log); 58 | } 59 | 60 | public void Deposit(int amount) 61 | { 62 | balance += amount; 63 | // check for null everywhere 64 | log?.Info($"Deposited ${amount}, balance is now {balance}"); 65 | } 66 | 67 | public void Withdraw(int amount) 68 | { 69 | if (balance >= amount) 70 | { 71 | balance -= amount; 72 | log?.Info($"Withdrew ${amount}, we have ${balance} left"); 73 | } 74 | else 75 | { 76 | log?.Warn($"Could not withdraw ${amount} because " + 77 | $"balance is only ${balance}"); 78 | } 79 | } 80 | } 81 | 82 | public sealed class NullLog : ILog 83 | { 84 | public void Info(string msg) { } 85 | public void Warn(string msg) { } 86 | } 87 | 88 | public class Null : DynamicObject where T:class 89 | { 90 | public static T Instance 91 | { 92 | get 93 | { 94 | if (!typeof(T).IsInterface) 95 | throw new ArgumentException("I must be an interface type"); 96 | 97 | return new Null().ActLike(); 98 | } 99 | } 100 | 101 | public override bool TryInvokeMember(InvokeMemberBinder binder, 102 | object[] args, out object result) 103 | { 104 | var name = binder.Name; 105 | result = Activator.CreateInstance(binder.ReturnType); 106 | return true; 107 | } 108 | } 109 | 110 | public class Demo 111 | { 112 | static void Main() 113 | { 114 | //var log = new ConsoleLog(); 115 | //ILog log = null; 116 | var log = new NullLog(); 117 | var ba = new BankAccount(log); 118 | ba.Deposit(100); 119 | ba.Withdraw(200); 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /Behavioral/Observer/Events.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DotNetDesignPatternDemos.Behavioral.Observer 4 | { 5 | public class FallsIllEventArgs : EventArgs 6 | { 7 | public string Address; 8 | } 9 | 10 | public class Person 11 | { 12 | public void CatchACold() 13 | { 14 | FallsIll?.Invoke(this, 15 | new FallsIllEventArgs { Address = "123 London Road" }); 16 | } 17 | 18 | public event EventHandler FallsIll; 19 | } 20 | 21 | public class Demo 22 | { 23 | static void Main() 24 | { 25 | var person = new Person(); 26 | 27 | person.FallsIll += CallDoctor; 28 | 29 | person.CatchACold(); 30 | } 31 | 32 | private static void CallDoctor(object sender, FallsIllEventArgs eventArgs) 33 | { 34 | Console.WriteLine($"A doctor has been called to {eventArgs.Address}"); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Behavioral/Observer/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Remoting.Channels; 4 | using NUnit.Framework; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.Observer 7 | { 8 | namespace Coding.Exercise 9 | { 10 | public class Game 11 | { 12 | public event EventHandler RatEnters, RatDies; 13 | public event EventHandler NotifyRat; 14 | 15 | public void FireRatEnters(object sender) 16 | { 17 | RatEnters?.Invoke(sender, EventArgs.Empty); 18 | } 19 | 20 | public void FireRatDies(object sender) 21 | { 22 | RatDies?.Invoke(sender, EventArgs.Empty); 23 | } 24 | 25 | public void FireNotifyRat(object sender, Rat whichRat) 26 | { 27 | NotifyRat?.Invoke(sender, whichRat); 28 | } 29 | } 30 | 31 | public class Rat : IDisposable 32 | { 33 | private readonly Game game; 34 | public int Attack = 1; 35 | 36 | public Rat(Game game) 37 | { 38 | this.game = game; 39 | game.RatEnters += (sender, args) => 40 | { 41 | if (sender != this) 42 | { 43 | ++Attack; 44 | game.FireNotifyRat(this, (Rat)sender); 45 | } 46 | }; 47 | game.NotifyRat += (sender, rat) => 48 | { 49 | if (rat == this) ++Attack; 50 | }; 51 | game.RatDies += (sender, args) => --Attack; 52 | game.FireRatEnters(this); 53 | } 54 | 55 | 56 | public void Dispose() 57 | { 58 | game.FireRatDies(this); 59 | } 60 | } 61 | } 62 | 63 | namespace Coding.Exercise.Tests 64 | { 65 | [TestFixture] 66 | public class Tests 67 | { 68 | [Test] 69 | public void PlayingByTheRules() 70 | { 71 | Assert.That(typeof(Game).GetFields(), Is.Empty); 72 | Assert.That(typeof(Game).GetProperties(), Is.Empty); 73 | } 74 | 75 | [Test] 76 | public void SingleRatTest() 77 | { 78 | var game = new Game(); 79 | var rat = new Rat(game); 80 | Assert.That(rat.Attack, Is.EqualTo(1)); 81 | } 82 | 83 | [Test] 84 | public void TwoRatTest() 85 | { 86 | var game = new Game(); 87 | var rat = new Rat(game); 88 | var rat2 = new Rat(game); 89 | Assert.That(rat.Attack, Is.EqualTo(2)); 90 | Assert.That(rat2.Attack, Is.EqualTo(2)); 91 | } 92 | 93 | [Test] 94 | public void ThreeRatsOneDies() 95 | { 96 | var game = new Game(); 97 | 98 | var rat = new Rat(game); 99 | Assert.That(rat.Attack, Is.EqualTo(1)); 100 | 101 | var rat2 = new Rat(game); 102 | Assert.That(rat.Attack, Is.EqualTo(2)); 103 | Assert.That(rat2.Attack, Is.EqualTo(2)); 104 | 105 | using (var rat3 = new Rat(game)) 106 | { 107 | Assert.That(rat.Attack, Is.EqualTo(3)); 108 | Assert.That(rat2.Attack, Is.EqualTo(3)); 109 | Assert.That(rat3.Attack, Is.EqualTo(3)); 110 | } 111 | 112 | Assert.That(rat.Attack, Is.EqualTo(2)); 113 | Assert.That(rat2.Attack, Is.EqualTo(2)); 114 | } 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /Behavioral/Observer/ObserverInterfaces.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Linq; 4 | using static System.Console; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.Observer.Interfaces 7 | { 8 | public class Event 9 | { 10 | // something that can happen 11 | } 12 | 13 | public class FallsIllEvent : Event 14 | { 15 | public string Address; 16 | } 17 | 18 | public class Person : IObservable 19 | { 20 | private readonly HashSet subscriptions 21 | = new HashSet(); 22 | 23 | public IDisposable Subscribe(IObserver observer) 24 | { 25 | var subscription = new Subscription(this,observer); 26 | subscriptions.Add(subscription); 27 | return subscription; 28 | } 29 | 30 | public void CatchACold() 31 | { 32 | foreach (var sub in subscriptions) 33 | sub.Observer.OnNext(new FallsIllEvent {Address = "123 London Road"}); 34 | } 35 | 36 | private class Subscription : IDisposable 37 | { 38 | private Person person; 39 | public IObserver Observer; 40 | 41 | public Subscription(Person person, IObserver observer) 42 | { 43 | this.person = person; 44 | Observer = observer; 45 | } 46 | 47 | public void Dispose() 48 | { 49 | person.subscriptions.Remove(this); 50 | } 51 | } 52 | } 53 | 54 | public class Demo : IObserver 55 | { 56 | static void Main(string[] args) 57 | { 58 | new Demo(); 59 | } 60 | 61 | public Demo() 62 | { 63 | var person = new Person(); 64 | var sub = person.Subscribe(this); 65 | 66 | person.OfType() 67 | .Subscribe(args => WriteLine($"A doctor has been called to {args.Address}")); 68 | } 69 | 70 | public void OnNext(Event value) 71 | { 72 | if (value is FallsIllEvent args) 73 | WriteLine($"A doctor has been called to {args.Address}"); 74 | } 75 | 76 | public void OnError(Exception error){} 77 | public void OnCompleted(){} 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Behavioral/Observer/WeakEventPattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Activities.Statements; 3 | using System.Threading; 4 | using System.Windows; 5 | using static System.Console; 6 | 7 | namespace DotNetDesignPatternDemos.Behavioral.Observer.WeakEventPattern 8 | { 9 | // an event subscription can lead to a memory 10 | // leak if you hold on to it past the object's 11 | // lifetime 12 | 13 | // weak events help with this 14 | 15 | public class Button 16 | { 17 | public event EventHandler Clicked; 18 | 19 | public void Fire() 20 | { 21 | Clicked?.Invoke(this, EventArgs.Empty); 22 | } 23 | } 24 | 25 | public class Window 26 | { 27 | public Window(Button button) 28 | { 29 | button.Clicked += ButtonOnClicked; 30 | } 31 | 32 | private void ButtonOnClicked(object sender, EventArgs eventArgs) 33 | { 34 | WriteLine("Button clicked (Window handler)"); 35 | } 36 | 37 | ~Window() 38 | { 39 | WriteLine("Window finalized"); 40 | } 41 | } 42 | 43 | public class Window2 44 | { 45 | public Window2(Button button) 46 | { 47 | WeakEventManager 48 | .AddHandler(button, "Clicked", ButtonOnClicked); 49 | } 50 | 51 | private void ButtonOnClicked(object sender, EventArgs eventArgs) 52 | { 53 | WriteLine("Button clicked (Window2 handler)"); 54 | } 55 | 56 | ~Window2() 57 | { 58 | WriteLine("Window2 finalized"); 59 | } 60 | } 61 | 62 | public class Demo 63 | { 64 | static void Main(string[] args) 65 | { 66 | var btn = new Button(); 67 | //var window = new Window(btn); 68 | var window = new Window2(btn); 69 | var windowRef = new WeakReference(window); 70 | btn.Fire(); 71 | 72 | WriteLine("Setting window to null"); 73 | window = null; 74 | 75 | FireGC(); 76 | WriteLine($"Window alive? {windowRef.IsAlive}"); 77 | 78 | btn.Fire(); 79 | 80 | WriteLine("Setting button to null"); 81 | btn = null; 82 | 83 | FireGC(); 84 | 85 | WriteLine($"Window alive? {windowRef.IsAlive}"); 86 | } 87 | 88 | private static void FireGC() 89 | { 90 | WriteLine("Starting GC"); 91 | GC.Collect(); 92 | GC.WaitForPendingFinalizers(); 93 | GC.Collect(); 94 | WriteLine("GC is done!"); 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /Behavioral/State/Classic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Activities.Statements; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace DotNetDesignPatternDemos.Behavioral.State.Classic 9 | { 10 | public class Switch 11 | { 12 | public State State = new OffState(); 13 | public void On() { State.On(this); } 14 | public void Off() { State.Off(this); } 15 | } 16 | 17 | public abstract class State 18 | { 19 | public virtual void On(Switch sw) 20 | { 21 | Console.WriteLine("Light is already on."); 22 | } 23 | 24 | public virtual void Off(Switch sw) 25 | { 26 | Console.WriteLine("Light is already off."); 27 | } 28 | } 29 | 30 | public class OnState : State 31 | { 32 | public OnState() 33 | { 34 | Console.WriteLine("Light turned on."); 35 | } 36 | public override void Off(Switch sw) 37 | { 38 | Console.WriteLine("Turning light off..."); 39 | sw.State = new OffState(); 40 | } 41 | } 42 | 43 | public class OffState : State 44 | { 45 | public OffState() 46 | { 47 | Console.WriteLine("Light turned off."); 48 | } 49 | 50 | public override void On(Switch sw) 51 | { 52 | Console.WriteLine("Turning light on..."); 53 | sw.State = new OnState(); 54 | } 55 | } 56 | 57 | class Program 58 | { 59 | public static void Main(string[] args) 60 | { 61 | var ls = new Switch(); 62 | ls.On(); 63 | ls.Off(); 64 | ls.Off(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Behavioral/State/Exercise.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DotNetDesignPatternDemos.Behavioral.State 4 | { 5 | namespace Coding.Exercise 6 | { 7 | public class CombinationLock 8 | { 9 | private readonly int[] combination; 10 | 11 | public CombinationLock(int [] combination) 12 | { 13 | this.combination = combination; 14 | Reset(); 15 | } 16 | 17 | private void Reset() 18 | { 19 | Status = "LOCKED"; 20 | digitsEntered = 0; 21 | failed = false; 22 | } 23 | 24 | public string Status; 25 | 26 | private int digitsEntered = 0; 27 | private bool failed = false; 28 | 29 | public void EnterDigit(int digit) 30 | { 31 | if (Status == "LOCKED") Status = string.Empty; 32 | Status += digit.ToString(); 33 | if (combination[digitsEntered] != digit) 34 | { 35 | failed = true; 36 | } 37 | digitsEntered++; 38 | 39 | if (digitsEntered == combination.Length) 40 | Status = failed ? "ERROR" : "OPEN"; 41 | } 42 | } 43 | } 44 | 45 | namespace Coding.Exercise.Tests 46 | { 47 | [TestFixture] 48 | public class Tests 49 | { 50 | [Test] 51 | public void TestSuccess() 52 | { 53 | var cl = new CombinationLock(new[] {1, 2, 3, 4, 5}); 54 | Assert.That(cl.Status, Is.EqualTo("LOCKED")); 55 | cl.EnterDigit(1); 56 | Assert.That(cl.Status, Is.EqualTo("1")); 57 | cl.EnterDigit(2); 58 | Assert.That(cl.Status, Is.EqualTo("12")); 59 | cl.EnterDigit(3); 60 | Assert.That(cl.Status, Is.EqualTo("123")); 61 | cl.EnterDigit(4); 62 | Assert.That(cl.Status, Is.EqualTo("1234")); 63 | cl.EnterDigit(5); 64 | Assert.That(cl.Status, Is.EqualTo("OPEN")); 65 | } 66 | 67 | [Test] 68 | public void TestFailure() 69 | { 70 | var cl = new CombinationLock(new[]{1,2,3}); 71 | Assert.That(cl.Status, Is.EqualTo("LOCKED")); 72 | cl.EnterDigit(1); 73 | Assert.That(cl.Status, Is.EqualTo("1")); 74 | cl.EnterDigit(2); 75 | Assert.That(cl.Status, Is.EqualTo("12")); 76 | cl.EnterDigit(5); 77 | Assert.That(cl.Status, Is.EqualTo("ERROR")); 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /Behavioral/State/Handmade.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.State.Handmade 7 | { 8 | public enum State 9 | { 10 | OffHook, 11 | Connecting, 12 | Connected, 13 | OnHold, 14 | OnHook 15 | } 16 | 17 | public enum Trigger 18 | { 19 | CallDialed, 20 | HungUp, 21 | CallConnected, 22 | PlacedOnHold, 23 | TakenOffHold, 24 | LeftMessage 25 | } 26 | 27 | public class Demo 28 | { 29 | private static Dictionary> rules 30 | = new Dictionary> 31 | { 32 | [State.OffHook] = new List<(Trigger, State)> 33 | { 34 | (Trigger.CallDialed, State.Connecting) 35 | }, 36 | [State.Connecting] = new List<(Trigger, State)> 37 | { 38 | (Trigger.HungUp, State.OnHook), 39 | (Trigger.CallConnected, State.Connected) 40 | }, 41 | [State.Connected] = new List<(Trigger, State)> 42 | { 43 | (Trigger.LeftMessage, State.OnHook), 44 | (Trigger.HungUp, State.OnHook), 45 | (Trigger.PlacedOnHold, State.OnHold) 46 | }, 47 | [State.OnHold] = new List<(Trigger, State)> 48 | { 49 | (Trigger.TakenOffHold, State.Connected), 50 | (Trigger.HungUp, State.OnHook) 51 | } 52 | }; 53 | 54 | public static void MainInteractive(string[] args) 55 | { 56 | State state = State.OffHook, exitState = State.OnHook; 57 | do 58 | { 59 | Console.WriteLine($"The phone is currently {state}"); 60 | Console.WriteLine("Select a trigger:"); 61 | 62 | for (var i = 0; i < rules[state].Count; i++) 63 | { 64 | var (t, _) = rules[state][i]; 65 | Console.WriteLine($"{i}. {t}"); 66 | } 67 | 68 | int input = int.Parse(Console.ReadLine()); 69 | 70 | var (_, s) = rules[state][input]; 71 | state = s; 72 | } while (state != exitState); 73 | Console.WriteLine("We are done using the phone."); 74 | } 75 | 76 | public static void Main(string[] args) 77 | { 78 | State state = State.OffHook, exitState = State.OnHook; 79 | 80 | var queue = new Queue(new[]{0, 1, 2, 0, 0}); 81 | 82 | do 83 | { 84 | Console.WriteLine($"The phone is currently {state}"); 85 | Console.WriteLine("Select a trigger:"); 86 | 87 | for (var i = 0; i < rules[state].Count; i++) 88 | { 89 | var (t, _) = rules[state][i]; 90 | Console.WriteLine($"{i}. {t}"); 91 | } 92 | 93 | int input = queue.Dequeue(); 94 | Console.WriteLine(input); 95 | 96 | var (_, s) = rules[state][input]; 97 | state = s; 98 | } while (state != exitState); 99 | Console.WriteLine("We are done using the phone."); 100 | } 101 | 102 | } 103 | } -------------------------------------------------------------------------------- /Behavioral/State/Stateless.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DesignPatterns.Behavioral.State.Stateless 8 | { 9 | public enum State 10 | { 11 | OffHook, 12 | CallDialed, 13 | Ringing, 14 | OnHold, 15 | OnHook 16 | } 17 | 18 | 19 | } -------------------------------------------------------------------------------- /Behavioral/State/StatelessLightSwitch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Stateless; 7 | using static System.Console; 8 | 9 | namespace DotNetDesignPatternDemos.Behavioral.State.StatelessLightSwitch 10 | { 11 | class Program 12 | { 13 | enum Trigger 14 | { 15 | On, Off 16 | } 17 | 18 | public static void Main(string[] args) 19 | { 20 | // false = off, true = on 21 | 22 | var light = new StateMachine(false); 23 | 24 | light.Configure(false) // if the light is off... 25 | .Permit(Trigger.On, true) // we can turn it on 26 | .OnEntry(transition => 27 | { 28 | if (transition.IsReentry) 29 | WriteLine("Light is already off!"); 30 | else 31 | WriteLine("Switching light off"); 32 | }) 33 | .PermitReentry(Trigger.Off); 34 | // .Ignore(Trigger.Off) // but if it's already off we do nothing 35 | 36 | // same for when the light is on 37 | light.Configure(true) 38 | .Permit(Trigger.Off, false) 39 | .OnEntry(() => WriteLine("Turning light on")) 40 | .Ignore(Trigger.On); 41 | 42 | light.Fire(Trigger.On); // Turning light on 43 | light.Fire(Trigger.Off); // Turning light off 44 | light.Fire(Trigger.Off); // Light is already off! 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Behavioral/State/SwitchBased.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DotNetDesignPatternDemos.Behavioral.State 6 | { 7 | enum State 8 | { 9 | Locked, 10 | Failed, 11 | Unlocked 12 | } 13 | 14 | public class SwitchBasedDemo 15 | { 16 | static void Main(string[] args) 17 | { 18 | string code = "1234"; 19 | var data = new Queue(new[] {1, 2, 3, 4}); 20 | var state = State.Locked; 21 | var entry = new StringBuilder(); 22 | 23 | while (true) 24 | { 25 | switch (state) 26 | { 27 | case State.Locked: 28 | var value = data.Dequeue(); 29 | Console.WriteLine(value); 30 | entry.Append( 31 | //Console.ReadKey().KeyChar 32 | value 33 | ); 34 | 35 | if (entry.ToString() == code) 36 | { 37 | state = State.Unlocked; 38 | break; 39 | } 40 | 41 | if (!code.StartsWith(entry.ToString())) 42 | { 43 | // the code is blatantly wrong 44 | state = State.Failed; 45 | } 46 | break; 47 | case State.Failed: 48 | Console.WriteLine("FAILED"); 49 | return; 50 | //break; 51 | case State.Unlocked: 52 | //Console.CursorLeft = 0; 53 | Console.WriteLine("UNLOCKED"); 54 | return; 55 | } 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Behavioral/State/SwitchExpressions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DotNetDesignPatternDemos.Behavioral.State.SwitchExpressions 4 | { 5 | enum Chest 6 | { 7 | Open, Closed, Locked 8 | } 9 | 10 | enum Action 11 | { 12 | Open, Close 13 | } 14 | 15 | public class SwitchExpressions 16 | { 17 | static Chest Manipulate(Chest chest, 18 | Action action, bool haveKey) => 19 | (chest, action, haveKey) switch 20 | { 21 | (Chest.Closed, Action.Open, _) => Chest.Open, 22 | (Chest.Locked, Action.Open, true) => Chest.Open, 23 | (Chest.Open, Action.Close, true) => Chest.Locked, 24 | (Chest.Open, Action.Close, false) => Chest.Closed, 25 | 26 | _ => chest 27 | }; 28 | 29 | static Chest Manipulate2(Chest chest, 30 | Action action, bool haveKey) 31 | { 32 | switch (chest, action, haveKey) 33 | { 34 | case (Chest.Closed, Action.Open, _): 35 | return Chest.Open; 36 | case (Chest.Locked, Action.Open, true): 37 | return Chest.Open; 38 | case (Chest.Open, Action.Close, true): 39 | return Chest.Locked; 40 | case (Chest.Open, Action.Close, false): 41 | return Chest.Closed; 42 | default: 43 | Console.WriteLine("Chest unchanged"); 44 | return chest; 45 | } 46 | } 47 | 48 | public static void Main(string[] args) 49 | { 50 | Chest chest = Chest.Locked; 51 | Console.WriteLine($"Chest is {chest}"); 52 | 53 | // unlock with key 54 | chest = Manipulate(chest, Action.Open, true); 55 | Console.WriteLine($"Chest is now {chest}"); 56 | 57 | // close it! 58 | chest = Manipulate(chest, Action.Close, false); 59 | Console.WriteLine($"Chest is now {chest}"); 60 | 61 | // close it again! 62 | chest = Manipulate(chest, Action.Close, false); 63 | Console.WriteLine($"Chest is now {chest}"); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Behavioral/Strategy/ComparisonStrategies.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | // todo: this is somehow buggy in recording 6 | namespace DotNetDesignPatternDemos.Behavioral.Strategy 7 | { 8 | class Person : IEquatable, IComparable 9 | { 10 | public int Id; 11 | public string Name; 12 | public int Age; 13 | 14 | public int CompareTo(Person other) 15 | { 16 | if (ReferenceEquals(this, other)) return 0; 17 | if (ReferenceEquals(null, other)) return 1; 18 | return Id.CompareTo(other.Id); 19 | } 20 | 21 | public Person(int id, string name, int age) 22 | { 23 | Id = id; 24 | Name = name; 25 | Age = age; 26 | } 27 | 28 | public bool Equals(Person other) 29 | { 30 | if (ReferenceEquals(null, other)) return false; 31 | if (ReferenceEquals(this, other)) return true; 32 | return Id == other.Id; 33 | } 34 | 35 | public override bool Equals(object obj) 36 | { 37 | if (ReferenceEquals(null, obj)) return false; 38 | if (ReferenceEquals(this, obj)) return true; 39 | if (obj.GetType() != this.GetType()) return false; 40 | return Equals((Person) obj); 41 | } 42 | 43 | public override int GetHashCode() 44 | { 45 | return Id; 46 | } 47 | 48 | public static bool operator ==(Person left, Person right) 49 | { 50 | return Equals(left, right); 51 | } 52 | 53 | public static bool operator !=(Person left, Person right) 54 | { 55 | return !Equals(left, right); 56 | } 57 | 58 | private sealed class NameRelationalComparer : IComparer 59 | { 60 | public int Compare(Person x, Person y) 61 | { 62 | if (ReferenceEquals(x, y)) return 0; 63 | if (ReferenceEquals(null, y)) return 1; 64 | if (ReferenceEquals(null, x)) return -1; 65 | return string.Compare(x.Name, y.Name, 66 | StringComparison.Ordinal); 67 | } 68 | } 69 | 70 | 71 | public static IComparer NameComparer { get; } 72 | = new NameRelationalComparer(); 73 | } 74 | 75 | public class ComparisonStrategies 76 | { 77 | public static void Main(string[] args) 78 | { 79 | var people = new List(); 80 | 81 | // equality == != and comparison < = > 82 | 83 | people.Sort(); // meaningless by default 84 | 85 | // sort by name with a lambda 86 | people.Sort((x, y) => x.Name.CompareTo(y.Name)); 87 | 88 | people.Sort(Person.NameComparer); 89 | 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Behavioral/Strategy/Dynamic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using static System.Console; 5 | 6 | namespace DesignPatterns.Behavioral.Strategy.Dynamic 7 | { 8 | public enum OutputFormat 9 | { 10 | Markdown, 11 | Html 12 | } 13 | 14 | public interface IListStrategy 15 | { 16 | void Start(StringBuilder sb); 17 | void End(StringBuilder sb); 18 | void AddListItem(StringBuilder sb, string item); 19 | } 20 | 21 | public class MarkdownListStrategy : IListStrategy 22 | { 23 | public void Start(StringBuilder sb) 24 | { 25 | // markdown doesn't require a list preamble 26 | } 27 | 28 | public void End(StringBuilder sb) 29 | { 30 | 31 | } 32 | 33 | public void AddListItem(StringBuilder sb, string item) 34 | { 35 | sb.AppendLine($" * {item}"); 36 | } 37 | } 38 | 39 | public class HtmlListStrategy : IListStrategy 40 | { 41 | public void Start(StringBuilder sb) => sb.AppendLine("
    "); 42 | 43 | public void End(StringBuilder sb) => sb.AppendLine("
"); 44 | 45 | public void AddListItem(StringBuilder sb, string item) 46 | { 47 | sb.AppendLine($"
  • {item}
  • "); 48 | } 49 | } 50 | 51 | public class TextProcessor 52 | { 53 | private StringBuilder sb = new StringBuilder(); 54 | private IListStrategy listStrategy; 55 | 56 | public void SetOutputFormat(OutputFormat format) 57 | { 58 | switch (format) { 59 | case OutputFormat.Markdown: 60 | listStrategy = new MarkdownListStrategy(); 61 | break; 62 | case OutputFormat.Html: 63 | listStrategy = new HtmlListStrategy(); 64 | break; 65 | default: 66 | throw new ArgumentOutOfRangeException(nameof(format), format, null); 67 | } 68 | } 69 | 70 | public void AppendList(IEnumerable items) 71 | { 72 | listStrategy.Start(sb); 73 | foreach (var item in items) 74 | listStrategy.AddListItem(sb, item); 75 | listStrategy.End(sb); 76 | } 77 | 78 | public StringBuilder Clear() 79 | { 80 | return sb.Clear(); 81 | } 82 | 83 | public override string ToString() => sb.ToString(); 84 | } 85 | 86 | class Demo 87 | { 88 | static void Main(string[] args) 89 | { 90 | var tp = new TextProcessor(); 91 | tp.SetOutputFormat(OutputFormat.Markdown); 92 | tp.AppendList(new []{"foo", "bar", "baz"}); 93 | WriteLine(tp); 94 | 95 | tp.Clear(); 96 | tp.SetOutputFormat(OutputFormat.Html); 97 | tp.AppendList(new[] { "foo", "bar", "baz" }); 98 | WriteLine(tp); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /Behavioral/Strategy/Static.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Numerics; 4 | using System.Text; 5 | using static System.Console; 6 | 7 | namespace DesignPatterns.Behavioral.Strategy.Static 8 | { 9 | public enum OutputFormat 10 | { 11 | Markdown, 12 | Html 13 | } 14 | 15 | public interface IListStrategy 16 | { 17 | void Start(StringBuilder sb); 18 | void End(StringBuilder sb); 19 | void AddListItem(StringBuilder sb, string item); 20 | } 21 | 22 | public class MarkdownListStrategy : IListStrategy 23 | { 24 | public void Start(StringBuilder sb) 25 | { 26 | // markdown doesn't require a list preamble 27 | } 28 | 29 | public void End(StringBuilder sb) 30 | { 31 | 32 | } 33 | 34 | public void AddListItem(StringBuilder sb, string item) 35 | { 36 | sb.AppendLine($" * {item}"); 37 | } 38 | } 39 | 40 | public class HtmlListStrategy : IListStrategy 41 | { 42 | public void Start(StringBuilder sb) 43 | { 44 | sb.AppendLine("
      "); 45 | } 46 | 47 | public void End(StringBuilder sb) 48 | { 49 | sb.AppendLine("
    "); 50 | } 51 | 52 | public void AddListItem(StringBuilder sb, string item) 53 | { 54 | sb.AppendLine($"
  • {item}
  • "); 55 | } 56 | } 57 | 58 | // a.k.a. policy 59 | public class TextProcessor 60 | where LS : IListStrategy, new() 61 | { 62 | private StringBuilder sb = new StringBuilder(); 63 | private IListStrategy listStrategy = new LS(); 64 | 65 | public void AppendList(IEnumerable items) 66 | { 67 | listStrategy.Start(sb); 68 | foreach (var item in items) 69 | listStrategy.AddListItem(sb, item); 70 | listStrategy.End(sb); 71 | } 72 | 73 | public override string ToString() 74 | { 75 | return sb.ToString(); 76 | } 77 | } 78 | 79 | class Demo 80 | { 81 | static void Main(string[] args) 82 | { 83 | var tp = new TextProcessor(); 84 | tp.AppendList(new []{"foo", "bar", "baz"}); 85 | WriteLine(tp); 86 | 87 | var tp2 = new TextProcessor(); 88 | tp2.AppendList(new[] { "foo", "bar", "baz" }); 89 | WriteLine(tp2); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Behavioral/TemplateMethod/FunctionalTemplateMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DotNetDesignPatternDemos.Behavioral.TemplateMethod 4 | { 5 | using static System.Console; 6 | 7 | public static class GameTemplate 8 | { 9 | public static void Run( 10 | Action start, 11 | Action takeTurn, 12 | Func haveWinner, 13 | Func winningPlayer) 14 | { 15 | start(); 16 | while (!haveWinner()) 17 | takeTurn(); 18 | WriteLine($"Player {winningPlayer()} wins."); 19 | } 20 | } 21 | 22 | public class Demo2 23 | { 24 | static void Main(string[] args) 25 | { 26 | var numberOfPlayers = 2; 27 | int currentPlayer = 0; 28 | int turn = 1, maxTurns = 10; 29 | 30 | void Start() 31 | { 32 | WriteLine($"Starting a game of chess with {numberOfPlayers} players."); 33 | } 34 | 35 | bool HaveWinner() 36 | { 37 | return turn == maxTurns; 38 | } 39 | 40 | void TakeTurn() 41 | { 42 | WriteLine($"Turn {turn++} taken by player {currentPlayer}."); 43 | currentPlayer = (currentPlayer + 1) % numberOfPlayers; 44 | } 45 | 46 | int WinningPlayer() { 47 | return currentPlayer; 48 | } 49 | 50 | GameTemplate.Run(Start, TakeTurn, HaveWinner, WinningPlayer); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Behavioral/TemplateMethod/TemplateMethod.cs: -------------------------------------------------------------------------------- 1 | using static System.Console; 2 | 3 | namespace DotNetDesignPatternDemos.Behavioral.TemplateMethod 4 | { 5 | public abstract class Game 6 | { 7 | public void Run() 8 | { 9 | Start(); 10 | while (!HaveWinner) 11 | TakeTurn(); 12 | WriteLine($"Player {WinningPlayer} wins."); 13 | } 14 | 15 | protected abstract void Start(); 16 | protected abstract bool HaveWinner { get; } 17 | protected abstract void TakeTurn(); 18 | protected abstract int WinningPlayer { get; } 19 | 20 | public Game(int numberOfPlayers) 21 | { 22 | this.numberOfPlayers = numberOfPlayers; 23 | } 24 | 25 | protected int currentPlayer; 26 | 27 | protected readonly int numberOfPlayers; 28 | } 29 | 30 | // simulate a game of chess 31 | public class Chess : Game 32 | { 33 | public Chess() : base(2) 34 | { 35 | } 36 | 37 | protected override void Start() 38 | { 39 | WriteLine($"Starting a game of chess with {numberOfPlayers} players."); 40 | } 41 | 42 | protected override bool HaveWinner => turn == maxTurns; 43 | 44 | protected override void TakeTurn() 45 | { 46 | WriteLine($"Turn {turn++} taken by player {currentPlayer}."); 47 | currentPlayer = (currentPlayer + 1) % numberOfPlayers; 48 | } 49 | 50 | protected override int WinningPlayer => currentPlayer; 51 | 52 | private int maxTurns = 10; 53 | private int turn = 1; 54 | } 55 | 56 | public class Demo 57 | { 58 | static void Main(string[] args) 59 | { 60 | var chess = new Chess(); 61 | chess.Run(); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Behavioral/Visitor/Acyclic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using static System.Console; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.Visitor.Acyclic 7 | { 8 | public interface IVisitor 9 | { 10 | void Visit(TVisitable obj); 11 | } 12 | 13 | public interface IVisitor {} // marker interface 14 | 15 | public abstract class Expression 16 | { 17 | public virtual void Accept(IVisitor visitor) 18 | { 19 | if (visitor is IVisitor typed) 20 | typed.Visit(this); 21 | } 22 | } 23 | 24 | public class DoubleExpression : Expression 25 | { 26 | public double Value; 27 | 28 | public DoubleExpression(double value) 29 | { 30 | Value = value; 31 | } 32 | 33 | public override void Accept(IVisitor visitor) 34 | { 35 | if (visitor is IVisitor typed) 36 | typed.Visit(this); 37 | } 38 | } 39 | 40 | public class AdditionExpression : Expression 41 | { 42 | public Expression Left; 43 | public Expression Right; 44 | 45 | public AdditionExpression(Expression left, Expression right) 46 | { 47 | Left = left; 48 | Right = right; 49 | } 50 | 51 | public override void Accept(IVisitor visitor) 52 | { 53 | if (visitor is IVisitor typed) 54 | typed.Visit(this); 55 | } 56 | } 57 | 58 | public class ExpressionPrinter : IVisitor, 59 | IVisitor, 60 | IVisitor, 61 | IVisitor 62 | { 63 | StringBuilder sb = new StringBuilder(); 64 | 65 | public void Visit(DoubleExpression de) 66 | { 67 | sb.Append(de.Value); 68 | } 69 | 70 | public void Visit(AdditionExpression ae) 71 | { 72 | sb.Append("("); 73 | ae.Left.Accept(this); 74 | sb.Append("+"); 75 | ae.Right.Accept(this); 76 | sb.Append(")"); 77 | } 78 | 79 | public void Visit(Expression obj) 80 | { 81 | // default handler? 82 | } 83 | 84 | public override string ToString() => sb.ToString(); 85 | } 86 | 87 | public class Demo 88 | { 89 | public static void Main() 90 | { 91 | var e = new AdditionExpression( 92 | new DoubleExpression(1), 93 | new AdditionExpression( 94 | new DoubleExpression(2), 95 | new DoubleExpression(3))); 96 | var ep = new ExpressionPrinter(); 97 | ep.Visit(e); 98 | WriteLine(ep.ToString()); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /Behavioral/Visitor/Classic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using static System.Console; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.Visitor.Classic 7 | { 8 | public abstract class Expression 9 | { 10 | public abstract void Accept(IExpressionVisitor visitor); 11 | } 12 | 13 | public class DoubleExpression : Expression 14 | { 15 | public double Value; 16 | 17 | public DoubleExpression(double value) 18 | { 19 | Value = value; 20 | } 21 | 22 | public override void Accept(IExpressionVisitor visitor) 23 | { 24 | visitor.Visit(this); 25 | } 26 | } 27 | 28 | public class AdditionExpression : Expression 29 | { 30 | public Expression Left; 31 | public Expression Right; 32 | 33 | public AdditionExpression(Expression left, Expression right) 34 | { 35 | Left = left; 36 | Right = right; 37 | } 38 | 39 | public override void Accept(IExpressionVisitor visitor) 40 | { 41 | visitor.Visit(this); 42 | } 43 | } 44 | 45 | public interface IExpressionVisitor 46 | { 47 | void Visit(DoubleExpression de); 48 | void Visit(AdditionExpression ae); 49 | } 50 | 51 | public class ExpressionPrinter : IExpressionVisitor 52 | { 53 | StringBuilder sb = new StringBuilder(); 54 | 55 | public void Visit(DoubleExpression de) 56 | { 57 | sb.Append(de.Value); 58 | } 59 | 60 | public void Visit(AdditionExpression ae) 61 | { 62 | sb.Append("("); 63 | ae.Left.Accept(this); 64 | sb.Append("+"); 65 | ae.Right.Accept(this); 66 | sb.Append(")"); 67 | } 68 | 69 | public override string ToString() => sb.ToString(); 70 | } 71 | 72 | public class ExpressionCalculator : IExpressionVisitor 73 | { 74 | public double Result; 75 | 76 | // what you really want is double Visit(...) 77 | 78 | public void Visit(DoubleExpression de) 79 | { 80 | Result = de.Value; 81 | } 82 | 83 | public void Visit(AdditionExpression ae) 84 | { 85 | ae.Left.Accept(this); 86 | var a = Result; 87 | ae.Right.Accept(this); 88 | var b = Result; 89 | Result = a + b; 90 | } 91 | } 92 | 93 | public static class ExtensionMethods 94 | { 95 | } 96 | 97 | public class Demo 98 | { 99 | public static void Main() 100 | { 101 | var e = new AdditionExpression( 102 | left: new DoubleExpression(1), 103 | right: new AdditionExpression( 104 | left: new DoubleExpression(2), 105 | right: new DoubleExpression(3))); 106 | var ep = new ExpressionPrinter(); 107 | ep.Visit(e); 108 | WriteLine(ep.ToString()); 109 | 110 | var calc = new ExpressionCalculator(); 111 | calc.Visit(e); 112 | WriteLine($"{ep} = {calc.Result}"); 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /Behavioral/Visitor/Dispatch.cs: -------------------------------------------------------------------------------- 1 | namespace DotNetDesignPatternDemos.Behavioral.Visitor 2 | { 3 | interface IStuff { } 4 | class Foo : IStuff { } 5 | class Bar : IStuff { } 6 | 7 | public class Something 8 | { 9 | static void func(Foo foo) { } 10 | static void func(Bar bar) { } 11 | 12 | static void Main(string[] args) 13 | { 14 | IStuff i = new Foo(); 15 | func((dynamic)i); // cannot resolve w/o (dynamic) 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Behavioral/Visitor/Dynamic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using static System.Console; 5 | 6 | namespace DotNetDesignPatternDemos.Behavioral.Visitor.Dynamic 7 | { 8 | public abstract class Expression 9 | { 10 | } 11 | 12 | public class DoubleExpression : Expression 13 | { 14 | public double Value; 15 | 16 | public DoubleExpression(double value) 17 | { 18 | Value = value; 19 | } 20 | } 21 | 22 | public class AdditionExpression : Expression 23 | { 24 | public Expression Left; 25 | public Expression Right; 26 | 27 | public AdditionExpression(Expression left, Expression right) 28 | { 29 | Left = left ?? throw new ArgumentNullException(paramName: nameof(left)); 30 | Right = right ?? throw new ArgumentNullException(paramName: nameof(right)); 31 | } 32 | } 33 | 34 | public class ExpressionPrinter 35 | { 36 | public void Print(AdditionExpression ae, StringBuilder sb) 37 | { 38 | sb.Append("("); 39 | Print((dynamic)ae.Left, sb); 40 | sb.Append("+"); 41 | Print((dynamic)ae.Right, sb); 42 | sb.Append(")"); 43 | } 44 | 45 | public void Print(DoubleExpression de, StringBuilder sb) 46 | { 47 | sb.Append(de.Value); 48 | } 49 | } 50 | 51 | public class Demo 52 | { 53 | public static void Main() 54 | { 55 | var e = new AdditionExpression( 56 | left: new DoubleExpression(1), 57 | right: new AdditionExpression( 58 | left: new DoubleExpression(2), 59 | right: new DoubleExpression(3))); 60 | var ep = new ExpressionPrinter(); 61 | var sb = new StringBuilder(); 62 | ep.Print((dynamic)e, sb); 63 | WriteLine(sb); 64 | 65 | // disadvantages: 66 | 67 | // 1) Performance penalty 68 | // 2) Runtime error on missing visitor 69 | // 3) Problematic w.r.t. inheritance 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /Behavioral/Visitor/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using static System.Console; 7 | 8 | namespace DotNetDesignPatternDemos.Behavioral.Visitor.ExtensionMethods 9 | { 10 | public abstract class Expression 11 | { 12 | } 13 | 14 | public class DoubleExpression : Expression 15 | { 16 | public double Value; 17 | 18 | public DoubleExpression(double value) 19 | { 20 | Value = value; 21 | } 22 | } 23 | 24 | public class AdditionExpression : Expression 25 | { 26 | public Expression Left; 27 | public Expression Right; 28 | 29 | public AdditionExpression(Expression left, Expression right) 30 | { 31 | Left = left; 32 | Right = right; 33 | } 34 | } 35 | 36 | public static class ExpressionPrinter 37 | { 38 | private static readonly Dictionary methods 39 | = new Dictionary(); 40 | 41 | static ExpressionPrinter() 42 | { 43 | var a = typeof(Expression).Assembly; 44 | var classes = a.GetTypes() 45 | .Where(t => t.IsSubclassOf(typeof(Expression))); 46 | var printMethods = typeof(ExpressionPrinter).GetMethods(); 47 | foreach (var c in classes) 48 | { 49 | // find extension method that takes this class 50 | var pm = printMethods.FirstOrDefault(m => 51 | m.Name.Equals(nameof(Print)) && 52 | m.GetParameters()?[0]?.ParameterType == c); 53 | 54 | methods.Add(c, pm); 55 | } 56 | } 57 | 58 | public static void Print(this Expression e, StringBuilder sb) 59 | { 60 | methods[e.GetType()].Invoke(null, new object[] {e, sb}); 61 | // switch (e) 62 | // { 63 | // case DoubleExpression de: 64 | // de.Print(sb); 65 | // break; 66 | // case AdditionExpression ae: 67 | // ae.Print(sb); 68 | // break; 69 | // // and so on 70 | // } 71 | } 72 | 73 | public static void Print(this DoubleExpression de, StringBuilder sb) 74 | { 75 | sb.Append(de.Value); 76 | } 77 | 78 | public static void Print(this AdditionExpression ae, StringBuilder sb) 79 | { 80 | sb.Append("("); 81 | ae.Left.Print(sb); 82 | sb.Append("+"); 83 | ae.Right.Print(sb); 84 | sb.Append(")"); 85 | } 86 | } 87 | 88 | public class Demo 89 | { 90 | private static void Main(string[] args) 91 | { 92 | var e = new AdditionExpression( 93 | left: new DoubleExpression(1), 94 | right: new AdditionExpression( 95 | left: new DoubleExpression(2), 96 | right: new DoubleExpression(3))); 97 | var sb = new StringBuilder(); 98 | e.Print(sb); 99 | WriteLine(sb); 100 | 101 | // what is more likely: new type or new operation 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /Behavioral/Visitor/Intrusive.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using static System.Console; 4 | 5 | namespace DotNetDesignPatternDemos.Behavioral.Visitor.Intrusive 6 | { 7 | public abstract class Expression 8 | { 9 | // adding a new operation 10 | public abstract void Print(StringBuilder sb); 11 | } 12 | 13 | public class DoubleExpression : Expression 14 | { 15 | private double value; 16 | 17 | public DoubleExpression(double value) 18 | { 19 | this.value = value; 20 | } 21 | 22 | public override void Print(StringBuilder sb) 23 | { 24 | sb.Append(value); 25 | } 26 | } 27 | 28 | public class AdditionExpression : Expression 29 | { 30 | private Expression left, right; 31 | 32 | public AdditionExpression(Expression left, Expression right) 33 | { 34 | this.left = left ?? throw new ArgumentNullException(paramName: nameof(left)); 35 | this.right = right ?? throw new ArgumentNullException(paramName: nameof(right)); 36 | } 37 | 38 | public override void Print(StringBuilder sb) 39 | { 40 | sb.Append(value: "("); 41 | left.Print(sb); 42 | sb.Append(value: "+"); 43 | right.Print(sb); 44 | sb.Append(value: ")"); 45 | } 46 | } 47 | 48 | public class Demo 49 | { 50 | private static void Main(string[] args) 51 | { 52 | var e = new AdditionExpression( 53 | left: new DoubleExpression(1), 54 | right: new AdditionExpression( 55 | left: new DoubleExpression(2), 56 | right: new DoubleExpression(3))); 57 | var sb = new StringBuilder(); 58 | e.Print(sb); 59 | WriteLine(sb); 60 | 61 | // what is more likely: new type or new operation 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Behavioral/Visitor/Reflective.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using static System.Console; 5 | 6 | 7 | namespace DotNetDesignPatternDemos.Behavioral.Visitor.Reflective 8 | { 9 | using DictType = Dictionary>; 10 | 11 | public abstract class Expression 12 | { 13 | } 14 | 15 | public class DoubleExpression : Expression 16 | { 17 | public double Value; 18 | 19 | public DoubleExpression(double value) 20 | { 21 | Value = value; 22 | } 23 | } 24 | 25 | public class AdditionExpression : Expression 26 | { 27 | public Expression Left; 28 | public Expression Right; 29 | 30 | public AdditionExpression(Expression left, Expression right) 31 | { 32 | Left = left ?? throw new ArgumentNullException(paramName: nameof(left)); 33 | Right = right ?? throw new ArgumentNullException(paramName: nameof(right)); 34 | } 35 | } 36 | 37 | 38 | 39 | public static class ExpressionPrinter 40 | { 41 | private static DictType actions = new DictType 42 | { 43 | [typeof(DoubleExpression)] = (e, sb) => 44 | { 45 | var de = (DoubleExpression) e; 46 | sb.Append(de.Value); 47 | }, 48 | [typeof(AdditionExpression)] = (e, sb) => 49 | { 50 | var ae = (AdditionExpression) e; 51 | sb.Append("("); 52 | Print(ae.Left, sb); 53 | sb.Append("+"); 54 | Print(ae.Right, sb); 55 | sb.Append(")"); 56 | } 57 | }; 58 | 59 | public static void Print2(this Expression e, StringBuilder sb) 60 | { 61 | actions[e.GetType()](e, sb); 62 | } 63 | 64 | public static void Print(Expression e, StringBuilder sb) 65 | { 66 | if (e is DoubleExpression de) 67 | { 68 | sb.Append(de.Value); 69 | } 70 | else 71 | if (e is AdditionExpression ae) 72 | { 73 | sb.Append("("); 74 | Print(ae.Left, sb); 75 | sb.Append("+"); 76 | Print(ae.Right, sb); 77 | sb.Append(")"); 78 | } 79 | // breaks open-closed principle 80 | // will work incorrectly on missing case 81 | } 82 | } 83 | 84 | public class Demo 85 | { 86 | public static void Main() 87 | { 88 | var e = new AdditionExpression( 89 | left: new DoubleExpression(1), 90 | right: new AdditionExpression( 91 | left: new DoubleExpression(2), 92 | right: new DoubleExpression(3))); 93 | var sb = new StringBuilder(); 94 | e.Print2(sb); // extension method goodness! 95 | WriteLine(sb); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /Behavioral/Visitor/ReflectiveExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using static System.Console; 5 | 6 | 7 | namespace DotNetDesignPatternDemos.Behavioral.Visitor.ReflectiveExtension 8 | { 9 | using DictType = Dictionary>; 10 | 11 | public abstract class Expression 12 | { 13 | } 14 | 15 | public class DoubleExpression : Expression 16 | { 17 | public double Value; 18 | 19 | public DoubleExpression(double value) 20 | { 21 | Value = value; 22 | } 23 | } 24 | 25 | public class AdditionExpression : Expression 26 | { 27 | public Expression Left; 28 | public Expression Right; 29 | 30 | public AdditionExpression(Expression left, Expression right) 31 | { 32 | Left = left ?? throw new ArgumentNullException(paramName: nameof(left)); 33 | Right = right ?? throw new ArgumentNullException(paramName: nameof(right)); 34 | } 35 | } 36 | 37 | 38 | 39 | public static class ExpressionPrinter 40 | { 41 | private static DictType actions = new DictType 42 | { 43 | [typeof(DoubleExpression)] = (e, sb) => 44 | { 45 | var de = (DoubleExpression) e; 46 | sb.Append(de.Value); 47 | }, 48 | [typeof(AdditionExpression)] = (e, sb) => 49 | { 50 | var ae = (AdditionExpression) e; 51 | sb.Append("("); 52 | Print(ae.Left, sb); 53 | sb.Append("+"); 54 | Print(ae.Right, sb); 55 | sb.Append(")"); 56 | } 57 | }; 58 | 59 | public static string Print3(this Expression e) 60 | { 61 | var sb = new StringBuilder(); 62 | Print2(e, sb); 63 | return sb.ToString(); 64 | } 65 | 66 | private static void Print2(Expression e, StringBuilder sb) 67 | { 68 | actions[e.GetType()](e, sb); 69 | } 70 | 71 | private static void Print(Expression e, StringBuilder sb) 72 | { 73 | if (e is DoubleExpression de) 74 | { 75 | sb.Append(de.Value); 76 | } 77 | else 78 | if (e is AdditionExpression ae) 79 | { 80 | sb.Append("("); 81 | Print(ae.Left, sb); 82 | sb.Append("+"); 83 | Print(ae.Right, sb); 84 | sb.Append(")"); 85 | } 86 | // breaks open-closed principle 87 | // will work incorrectly on missing case 88 | } 89 | } 90 | 91 | public class Demo 92 | { 93 | public static void Main() 94 | { 95 | var e = new AdditionExpression( 96 | left: new DoubleExpression(1), 97 | right: new AdditionExpression( 98 | left: new DoubleExpression(2), 99 | right: new DoubleExpression(3))); 100 | var sb = new StringBuilder(); 101 | //ExpressionPrinter.Print2(e, sb); 102 | WriteLine(e.Print3()); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /Creational/Builder/BuilderFacets.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using static System.Console; 7 | 8 | namespace DotNetDesignPatternDemos.Creational.BuilderFacets 9 | { 10 | public class Person 11 | { 12 | public Person() 13 | { 14 | WriteLine("Creating an instance of Person"); 15 | } 16 | 17 | // address 18 | public string StreetAddress, Postcode, City; 19 | 20 | // employment info 21 | public string CompanyName, Position; 22 | 23 | public int AnnualIncome; 24 | 25 | public override string ToString() 26 | { 27 | return $"{nameof(StreetAddress)}: {StreetAddress}, {nameof(Postcode)}: {Postcode}," + 28 | $" {nameof(City)}: {City}, {nameof(CompanyName)}: {CompanyName}," + 29 | $" {nameof(Position)}: {Position}, {nameof(AnnualIncome)}: {AnnualIncome}"; 30 | } 31 | } 32 | 33 | public class PersonBuilder // facade 34 | { 35 | // the object we're going to build 36 | protected Person person; 37 | 38 | public PersonBuilder() => person = new Person(); 39 | protected PersonBuilder(Person person) => this.person = person; 40 | 41 | public PersonAddressBuilder Lives => new PersonAddressBuilder(person); 42 | public PersonJobBuilder Works => new PersonJobBuilder(person); 43 | 44 | public static implicit operator Person(PersonBuilder pb) 45 | { 46 | return pb.person; 47 | } 48 | } 49 | 50 | public class PersonJobBuilder : PersonBuilder 51 | { 52 | public PersonJobBuilder(Person person) : base(person) {} 53 | 54 | public PersonJobBuilder At(string companyName) 55 | { 56 | person.CompanyName = companyName; 57 | return this; 58 | } 59 | 60 | public PersonJobBuilder AsA(string position) 61 | { 62 | person.Position = position; 63 | return this; 64 | } 65 | 66 | public PersonJobBuilder Earning(int annualIncome) 67 | { 68 | person.AnnualIncome = annualIncome; 69 | return this; 70 | } 71 | } 72 | 73 | public class PersonAddressBuilder : PersonBuilder 74 | { 75 | public PersonAddressBuilder(Person person) : base(person) {} 76 | 77 | public PersonAddressBuilder At(string streetAddress) 78 | { 79 | person.StreetAddress = streetAddress; 80 | return this; 81 | } 82 | 83 | public PersonAddressBuilder WithPostcode(string postcode) 84 | { 85 | person.Postcode = postcode; 86 | return this; 87 | } 88 | 89 | public PersonAddressBuilder In(string city) 90 | { 91 | person.City = city; 92 | return this; 93 | } 94 | } 95 | 96 | 97 | public class Demo 98 | { 99 | static void Main(string[] args) 100 | { 101 | var pb = new PersonBuilder(); 102 | Person person = pb 103 | .Lives 104 | .At("123 London Road") 105 | .In("London") 106 | .WithPostcode("SW12BC") 107 | .Works 108 | .At("Fabrikam") 109 | .AsA("Engineer") 110 | .Earning(123000); 111 | 112 | WriteLine(person); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Creational/Builder/BuilderInheritance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace DesignPatterns 6 | { 7 | public class Person 8 | { 9 | public string Name; 10 | 11 | public string Position; 12 | 13 | public DateTime DateOfBirth; 14 | 15 | public class Builder : PersonBirthDateBuilder 16 | { 17 | internal Builder() {} 18 | } 19 | 20 | public static Builder New => new Builder(); 21 | 22 | public override string ToString() 23 | { 24 | return $"{nameof(Name)}: {Name}, {nameof(Position)}: {Position}"; 25 | } 26 | } 27 | 28 | public abstract class PersonBuilder 29 | { 30 | protected Person person = new Person(); 31 | 32 | public Person Build() 33 | { 34 | return person; 35 | } 36 | } 37 | 38 | public class PersonInfoBuilder : PersonBuilder 39 | where SELF : PersonInfoBuilder 40 | { 41 | public SELF Called(string name) 42 | { 43 | person.Name = name; 44 | return (SELF) this; 45 | } 46 | } 47 | 48 | public class PersonJobBuilder 49 | : PersonInfoBuilder*/SELF> 50 | where SELF : PersonJobBuilder 51 | { 52 | public SELF WorksAsA(string position) 53 | { 54 | person.Position = position; 55 | return (SELF) this; 56 | } 57 | } 58 | 59 | // here's another inheritance level 60 | // note there's no PersonInfoBuilder>>! 61 | 62 | public class PersonBirthDateBuilder 63 | : PersonJobBuilder 64 | where SELF : PersonBirthDateBuilder 65 | { 66 | public SELF Born(DateTime dateOfBirth) 67 | { 68 | person.DateOfBirth = dateOfBirth; 69 | return (SELF)this; 70 | } 71 | } 72 | 73 | internal class Program 74 | { 75 | class SomeBuilder : PersonBirthDateBuilder 76 | { 77 | 78 | } 79 | 80 | public static void Main(string[] args) 81 | { 82 | var me = Person.New 83 | .Called("Dmitri") 84 | .WorksAsA("Quant") 85 | .Born(DateTime.UtcNow) 86 | .Build(); 87 | Console.WriteLine(me); 88 | 89 | 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Creational/Builder/BuilderParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DotNetDesignPatternDemos.Creational.Builder.BuilderParameter 8 | { 9 | public class MailService 10 | { 11 | public class EmailBuilder 12 | { 13 | public class Email 14 | { 15 | public string From, To, Subject, Body; 16 | } 17 | 18 | private readonly Email email; 19 | 20 | public EmailBuilder(Email email) => this.email = email; 21 | 22 | public EmailBuilder From(string from) 23 | { 24 | email.From = from; 25 | return this; 26 | } 27 | 28 | public EmailBuilder To(string to) 29 | { 30 | email.To = to; 31 | return this; 32 | } 33 | 34 | public EmailBuilder Subject(string subject) 35 | { 36 | email.Subject = subject; 37 | return this; 38 | } 39 | 40 | public EmailBuilder Body(string body) 41 | { 42 | email.Body = body; 43 | return this; 44 | } 45 | } 46 | 47 | private void SendEmailInternal(EmailBuilder.Email email) {} 48 | 49 | public void SendEmail(Action builder) 50 | { 51 | var email = new EmailBuilder.Email(); 52 | builder(new EmailBuilder(email)); 53 | SendEmailInternal(email); 54 | } 55 | } 56 | 57 | class Program 58 | { 59 | public static void Main(string[] args) 60 | { 61 | var ms = new MailService(); 62 | ms.SendEmail(email => email.From("foo@bar.com") 63 | .To("bar@baz.com") 64 | .Body("Hello, how are you?")); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Creational/Builder/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using NUnit.Framework; 4 | 5 | namespace DotNetDesignPatternDemos.Creational.Builder 6 | { 7 | namespace Coding.Exercise 8 | { 9 | class Field 10 | { 11 | public string Type, Name; 12 | 13 | public override string ToString() 14 | { 15 | return $"public {Type} {Name}"; 16 | } 17 | } 18 | 19 | class Class 20 | { 21 | public string Name; 22 | public List Fields = new List(); 23 | 24 | public Class() 25 | { 26 | 27 | } 28 | 29 | public override string ToString() 30 | { 31 | var sb = new StringBuilder(); 32 | sb.AppendLine($"public class {Name}").AppendLine("{"); 33 | foreach (var f in Fields) 34 | sb.AppendLine($" {f};"); 35 | sb.AppendLine("}"); 36 | return sb.ToString(); 37 | } 38 | } 39 | 40 | public class CodeBuilder 41 | { 42 | public CodeBuilder(string rootName) 43 | { 44 | theClass.Name = rootName; 45 | } 46 | 47 | public CodeBuilder AddField(string name, string type) 48 | { 49 | theClass.Fields.Add(new Field { Name = name, Type = type }); 50 | return this; 51 | } 52 | 53 | public override string ToString() 54 | { 55 | return theClass.ToString(); 56 | } 57 | 58 | private Class theClass = new Class(); 59 | } 60 | } 61 | 62 | namespace Coding.Exercise.UnitTests 63 | { 64 | [TestFixture] 65 | public class FirstTestSuite 66 | { 67 | private static string Preprocess(string s) 68 | { 69 | return s.Trim().Replace("\r\n", "\n"); 70 | } 71 | 72 | [Test] 73 | public void EmptyTest() 74 | { 75 | var cb = new CodeBuilder("Foo"); 76 | Assert.That(Preprocess(cb.ToString()), Is.EqualTo("public class Foo\n{\n}")); 77 | } 78 | 79 | [Test] 80 | public void PersonTest() 81 | { 82 | var cb = new CodeBuilder("Person").AddField("Name", "string").AddField("Age", "int"); 83 | Assert.That(Preprocess(cb.ToString()), 84 | Is.EqualTo( 85 | @"public class Person 86 | { 87 | public string Name; 88 | public int Age; 89 | }" 90 | )); 91 | } 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /Creational/Builder/FunctionalBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace DotNetDesignPatternDemos.Creational.Builder 6 | { 7 | public class Person 8 | { 9 | public string Name, Position; 10 | } 11 | 12 | public abstract class FunctionalBuilder 13 | where TSelf: FunctionalBuilder 14 | where TSubject : new() 15 | { 16 | private readonly List> actions 17 | = new List>(); 18 | 19 | public TSelf Do(Action action) 20 | => AddAction(action); 21 | 22 | private TSelf AddAction(Action action) 23 | { 24 | actions.Add(p => { action(p); return p; }); 25 | return (TSelf) this; 26 | } 27 | public TSubject Build() 28 | => actions.Aggregate(new TSubject(), (p, f) => f(p)); 29 | } 30 | 31 | public sealed class PersonBuilder 32 | : FunctionalBuilder 33 | { 34 | public PersonBuilder Called(string name) 35 | => Do(p => p.Name = name); 36 | } 37 | 38 | public static class PersonBuilderExtensions 39 | { 40 | public static PersonBuilder WorksAsA 41 | (this PersonBuilder builder, string position) 42 | { 43 | builder.Do(p => p.Position = position); 44 | return builder; 45 | } 46 | } 47 | 48 | public class FunctionalBuilder 49 | { 50 | public static void Main(string[] args) 51 | { 52 | var b = new PersonBuilder(); 53 | var p = b.Called("Dmitri") 54 | .WorksAsA("Developer") 55 | .Build(); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Creational/Factories/AbstractFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PatternDemo 5 | { 6 | public interface IShape 7 | { 8 | void Draw(); 9 | } 10 | 11 | public class Square : IShape 12 | { 13 | public void Draw() => Console.WriteLine("Basic square"); 14 | } 15 | 16 | public class Rectangle : IShape 17 | { 18 | public void Draw() => Console.WriteLine("Basic rectangle"); 19 | } 20 | 21 | public class RoundedSquare : IShape 22 | { 23 | public void Draw() => Console.WriteLine("Rounded square"); 24 | } 25 | 26 | public class RoundedRectangle : IShape 27 | { 28 | public void Draw() => Console.WriteLine("Rounded rectangle"); 29 | } 30 | 31 | public enum Shape 32 | { 33 | Square, 34 | Rectangle 35 | } 36 | 37 | public abstract class ShapeFactory 38 | { 39 | public abstract IShape Create(Shape shape); 40 | } 41 | 42 | public class BasicShapeFactory : ShapeFactory 43 | { 44 | public override IShape Create(Shape shape) 45 | { 46 | switch (shape) 47 | { 48 | case Shape.Square: 49 | return new Square(); 50 | case Shape.Rectangle: 51 | return new Rectangle(); 52 | default: 53 | throw new ArgumentOutOfRangeException( 54 | nameof(shape), shape, null); 55 | } 56 | } 57 | } 58 | 59 | public class RoundedShapeFactory : ShapeFactory 60 | { 61 | public override IShape Create(Shape shape) 62 | { 63 | switch (shape) 64 | { 65 | case Shape.Square: 66 | return new RoundedSquare(); 67 | case Shape.Rectangle: 68 | return new RoundedRectangle(); 69 | default: 70 | throw new ArgumentOutOfRangeException(nameof(shape), shape, null); 71 | } 72 | } 73 | } 74 | 75 | public class Demo 76 | { 77 | public static ShapeFactory GetFactory(bool rounded) 78 | { 79 | if (rounded) 80 | return new RoundedShapeFactory(); 81 | else 82 | return new BasicShapeFactory(); 83 | } 84 | 85 | public static void Main() 86 | { 87 | var basic = GetFactory(false); 88 | var basicRectangle = basic.Create(Shape.Rectangle); 89 | basicRectangle.Draw(); 90 | 91 | var roundedSquare = GetFactory(true).Create(Shape.Square); 92 | roundedSquare.Draw(); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /Creational/Factories/AsyncFactory.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Threading.Tasks; 3 | using Autofac; 4 | 5 | namespace DotNetDesignPatternDemos.Creational.AsyncFactory 6 | { 7 | public interface IAsyncInit 8 | { 9 | Task InitAsync(); 10 | } 11 | 12 | public class Foo : IAsyncInit 13 | { 14 | /*public*/ public Foo() 15 | { 16 | // await Task.Delay(1000); 17 | } 18 | 19 | // ↓↓ 20 | public async Task InitAsync() 21 | { 22 | // some work here 23 | await Task.Delay(1000); 24 | return this; // fluent 25 | } 26 | 27 | public static Task CreateAsync() 28 | { 29 | var result = new Foo(); 30 | return result.InitAsync(); 31 | } 32 | } 33 | 34 | public static class AsyncFactory 35 | { 36 | public static async Task Create() 37 | where T : IAsyncInit, new() 38 | { 39 | var result = await new T().InitAsync(); 40 | return result; 41 | } 42 | } 43 | 44 | class Demo 45 | { 46 | public static async void Main(string[] args) 47 | { 48 | // var foo = new Foo(); 49 | // await foo.InitAsync(); 50 | 51 | // instead 52 | var foo = await Foo.CreateAsync(); 53 | 54 | // or 55 | var foo2 = AsyncFactory.Create(); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Creational/Factories/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using NUnit.Framework; 4 | 5 | namespace DotNetDesignPatternDemos.Creational.Factories 6 | { 7 | namespace Coding.Exercise 8 | { 9 | public class Person 10 | { 11 | public int Id { get; set; } 12 | public string Name { get; set; } 13 | } 14 | 15 | public class PersonFactory 16 | { 17 | private int id = 0; 18 | 19 | public Person CreatePerson(string name) 20 | { 21 | return new Person {Id = id++, Name = name}; 22 | } 23 | } 24 | } 25 | 26 | namespace Coding.Exercise.UnitTests 27 | { 28 | [TestFixture] 29 | public class FirstTestSuite 30 | { 31 | [Test] 32 | public void Test() 33 | { 34 | var pf = new PersonFactory(); 35 | 36 | var p1 = pf.CreatePerson("Chris"); 37 | Assert.That(p1.Name, Is.EqualTo("Chris")); 38 | Assert.That(p1.Id, Is.EqualTo(0)); 39 | 40 | var p2 = pf.CreatePerson("Sarah"); 41 | Assert.That(p2.Id, Is.EqualTo(1)); 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Creational/Factories/Factory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DotNetDesignPatternDemos.Creational.Factories 4 | { 5 | public class Point 6 | { 7 | private double x, y; 8 | 9 | protected Point(double x, double y) 10 | { 11 | this.x = x; 12 | this.y = y; 13 | } 14 | 15 | // polar 16 | // public Point(double rho, double theta) 17 | // { 18 | // 19 | // } 20 | 21 | public override string ToString() 22 | { 23 | return $"{nameof(x)}: {x}, {nameof(y)}: {y}"; 24 | } 25 | 26 | public Point(double a, 27 | double b, // names do not communicate intent 28 | CoordinateSystem cs = CoordinateSystem.Cartesian) 29 | { 30 | switch (cs) 31 | { 32 | case CoordinateSystem.Polar: 33 | x = a * Math.Cos(b); 34 | y = a * Math.Sin(b); 35 | break; 36 | default: 37 | x = a; 38 | y = b; 39 | break; 40 | } 41 | 42 | // steps to add a new system 43 | // 1. augment CoordinateSystem 44 | // 2. change ctor 45 | } 46 | 47 | // factory property 48 | 49 | public static Point Origin => new Point(0, 0); 50 | 51 | // constant field 52 | public static Point Origin2 = new Point(0, 0); 53 | 54 | // factory method 55 | 56 | public static Point NewCartesianPoint(double x, double y) 57 | { 58 | return new Point(x, y); 59 | } 60 | 61 | public static Point NewPolarPoint(double rho, double theta) 62 | { 63 | return new Point(rho*Math.Cos(theta), rho*Math.Sin(theta)); 64 | } 65 | 66 | public enum CoordinateSystem 67 | { 68 | Cartesian, 69 | Polar 70 | } 71 | 72 | // make it lazy 73 | public static class Factory 74 | { 75 | public static Point NewCartesianPoint(double x, double y) 76 | { 77 | return new Point(x, y); 78 | } 79 | } 80 | } 81 | 82 | class PointFactory 83 | { 84 | public static Point NewCartesianPoint(float x, float y) 85 | { 86 | return new Point(x, y); // needs to be public 87 | } 88 | } 89 | 90 | class Demo 91 | { 92 | static void Main(string[] args) 93 | { 94 | var p1 = new Point(2, 3, Point.CoordinateSystem.Cartesian); 95 | var origin = Point.Origin; 96 | 97 | var p2 = Point.Factory.NewCartesianPoint(1, 2); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Creational/Prototype/CopyConstructors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static System.Console; 3 | 4 | namespace DotNetDesignPatternDemos.Creational.Prototype 5 | { 6 | public class Address 7 | { 8 | public string StreetAddress, City, Country; 9 | 10 | public Address(string streetAddress, string city, string country) 11 | { 12 | StreetAddress = streetAddress; 13 | City = city; 14 | Country = country; 15 | } 16 | 17 | public Address(Address other) 18 | { 19 | StreetAddress = other.StreetAddress; 20 | City = other.City; 21 | Country = other.Country; 22 | } 23 | 24 | public override string ToString() 25 | { 26 | return $"{nameof(StreetAddress)}: {StreetAddress}, {nameof(City)}: {City}, {nameof(Country)}: {Country}"; 27 | } 28 | } 29 | 30 | public class Person 31 | { 32 | public string Name; 33 | public Address Address; 34 | 35 | public Person(string name, Address address) 36 | { 37 | Name = name; 38 | Address = address; 39 | } 40 | 41 | public Person(Person other) 42 | { 43 | Name = other.Name; 44 | Address = new Address(other.Address); 45 | } 46 | 47 | public override string ToString() 48 | { 49 | return $"{nameof(Name)}: {Name}, {nameof(Address)}: {Address}"; 50 | } 51 | } 52 | 53 | public class CopyConstructors 54 | { 55 | static void Main(string[] args) 56 | { 57 | var john = new Person("John", 58 | new Address("123 London Road", "London", "UK")); 59 | 60 | //var jane = john; 61 | var jane = new Person(john); 62 | 63 | jane.Name = "Jane"; 64 | 65 | WriteLine(john); // oops, john is called jane 66 | WriteLine(jane); 67 | 68 | 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Creational/Prototype/CopyThroughSerialization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static System.Console; 3 | using System.IO; 4 | using System.Runtime.Serialization.Formatters.Binary; 5 | using System.Xml.Serialization; 6 | 7 | namespace DotNetDesignPatternDemos.Creational.Prototype 8 | { 9 | public static class ExtensionMethods 10 | { 11 | public static T DeepCopy(this T self) 12 | { 13 | using (var stream = new MemoryStream()) 14 | { 15 | BinaryFormatter formatter = new BinaryFormatter(); 16 | formatter.Serialize(stream, self); 17 | stream.Seek(0, SeekOrigin.Begin); 18 | object copy = formatter.Deserialize(stream); 19 | return (T) copy; 20 | } 21 | } 22 | 23 | public static T DeepCopyXml(this T self) 24 | { 25 | using (var ms = new MemoryStream()) 26 | { 27 | XmlSerializer s = new XmlSerializer(typeof(T)); 28 | s.Serialize(ms, self); 29 | ms.Position = 0; 30 | return (T) s.Deserialize(ms); 31 | } 32 | } 33 | } 34 | 35 | //[Serializable] // this is, unfortunately, required 36 | public class Foo 37 | { 38 | public uint Stuff; 39 | public string Whatever; 40 | 41 | public override string ToString() 42 | { 43 | return $"{nameof(Stuff)}: {Stuff}, {nameof(Whatever)}: {Whatever}"; 44 | } 45 | } 46 | 47 | public static class CopyThroughSerialization 48 | { 49 | static void Main() 50 | { 51 | Foo foo = new Foo {Stuff = 42, Whatever = "abc"}; 52 | 53 | //Foo foo2 = foo.DeepCopy(); // crashes without [Serializable] 54 | Foo foo2 = foo.DeepCopyXml(); 55 | 56 | foo2.Whatever = "xyz"; 57 | WriteLine(foo); 58 | WriteLine(foo2); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Creational/Prototype/Exercise.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DotNetDesignPatternDemos.Creational.Prototype 4 | { 5 | namespace Coding.Exercise 6 | { 7 | public class Point 8 | { 9 | public int X, Y; 10 | } 11 | 12 | public class Line 13 | { 14 | public Point Start, End; 15 | 16 | public Line DeepCopy() 17 | { 18 | var newStart = new Point{X = Start.X, Y = Start.Y}; 19 | var newEnd = new Point {X = End.X, Y = End.Y}; 20 | return new Line {Start = newStart, End = newEnd}; 21 | } 22 | } 23 | } 24 | 25 | namespace Coding.Exercise.UnitTests 26 | { 27 | [TestFixture] 28 | public class FirstTestSuite 29 | { 30 | [Test] 31 | public void Test() 32 | { 33 | var line1 = new Line 34 | { 35 | Start = new Point { X = 3, Y = 3}, 36 | End = new Point { X = 10, Y = 10} 37 | }; 38 | 39 | var line2 = line1.DeepCopy(); 40 | line1.Start.X = line1.End.X = line1.Start.Y = line1.End.Y = 0; 41 | 42 | Assert.That(line2.Start.X, Is.EqualTo(3)); 43 | Assert.That(line2.Start.Y, Is.EqualTo(3)); 44 | Assert.That(line2.End.X, Is.EqualTo(10)); 45 | Assert.That(line2.End.Y, Is.EqualTo(10)); 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Creational/Prototype/ICloneableIsBad.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static System.Console; 3 | 4 | namespace DotNetDesignPatternDemos.Creational.Prototype.ICloneableIsBad 5 | { 6 | // ICloneable is ill-specified 7 | 8 | interface IDeepCopyable 9 | { 10 | T DeepCopy(); 11 | } 12 | 13 | public class Address : ICloneable 14 | { 15 | public readonly string StreetName; 16 | public int HouseNumber; 17 | 18 | public Address(string streetName, int houseNumber) 19 | { 20 | StreetName = streetName; 21 | HouseNumber = houseNumber; 22 | } 23 | 24 | public override string ToString() 25 | { 26 | return $"{nameof(StreetName)}: {StreetName}, {nameof(HouseNumber)}: {HouseNumber}"; 27 | } 28 | 29 | public object Clone() 30 | { 31 | return new Address(StreetName, HouseNumber); 32 | } 33 | } 34 | 35 | 36 | 37 | public class Person : ICloneable 38 | { 39 | public readonly string[] Names; 40 | public readonly Address Address; 41 | 42 | public Person(string[] names, Address address) 43 | { 44 | Names = names; 45 | Address = address; 46 | } 47 | 48 | public override string ToString() 49 | { 50 | return $"{nameof(Names)}: {string.Join(",", Names)}, {nameof(Address)}: {Address}"; 51 | } 52 | 53 | public object Clone() 54 | { 55 | return MemberwiseClone(); 56 | //return new Person(Names, Address); 57 | } 58 | } 59 | 60 | public static class Demo 61 | { 62 | static void Main() 63 | { 64 | var john = new Person( 65 | new []{"John", "Smith"}, 66 | new Address("London Road", 123)); 67 | 68 | var jane = (Person)john.Clone(); 69 | jane.Address.HouseNumber = 321; // oops, John is now at 321 70 | 71 | // this doesn't work 72 | //var jane = john; 73 | 74 | // but clone is typically shallow copy 75 | jane.Names[0] = "Jane"; 76 | 77 | WriteLine(john); 78 | WriteLine(jane); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Creational/Prototype/PrototypeFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DotNetDesignPatternDemos.Creational.Prototype; 3 | using NUnit.Framework.Internal.Commands; 4 | using static System.Console; 5 | 6 | namespace DotNetDesignPatternDemos.Creational.PrototypeFactory 7 | { 8 | public class Address 9 | { 10 | public string StreetAddress, City; 11 | public int Suite; 12 | 13 | public Address(string streetAddress, string city, int suite) 14 | { 15 | StreetAddress = streetAddress; 16 | City = city; 17 | Suite = suite; 18 | } 19 | 20 | public Address(Address other) 21 | { 22 | StreetAddress = other.StreetAddress; 23 | City = other.City; 24 | Suite = other.Suite; 25 | } 26 | } 27 | 28 | public partial class Employee 29 | { 30 | public string Name; 31 | public Address Address; 32 | 33 | public Employee(string name, Address address) 34 | { 35 | Name = name ?? throw new ArgumentNullException(paramName: nameof(name)); 36 | Address = address ?? throw new ArgumentNullException(paramName: nameof(address)); 37 | } 38 | 39 | public Employee(Employee other) 40 | { 41 | Name = other.Name; 42 | Address = new Address(other.Address); 43 | } 44 | 45 | public override string ToString() 46 | { 47 | return $"{nameof(Name)}: {Name}, {nameof(Address)}: {Address}"; 48 | } 49 | 50 | //partial class EmployeeFactory {} 51 | } 52 | 53 | public class EmployeeFactory 54 | { 55 | private static Employee main = 56 | new Employee(null, new Address("123 East Dr", "London", 0)); 57 | private static Employee aux = 58 | new Employee(null, new Address("123B East Dr", "London", 0)); 59 | 60 | private static Employee NewEmployee(Employee proto, string name, int suite) 61 | { 62 | var copy = proto.DeepCopy(); 63 | copy.Name = name; 64 | copy.Address.Suite = suite; 65 | return copy; 66 | } 67 | 68 | public static Employee NewMainOfficeEmployee(string name, int suite) => 69 | NewEmployee(main, name, suite); 70 | 71 | public static Employee NewAuxOfficeEmployee(string name, int suite) => 72 | NewEmployee(aux, name, suite); 73 | } 74 | 75 | public class Demo 76 | { 77 | static Employee main = new Employee(null, new Address("123 East Dr", "London", 0)); 78 | 79 | static void Main(string[] args) 80 | { 81 | var main = new Employee(null, new Address("123 East Dr", "London", 0)); 82 | var aux = new Employee(null, new Address("123B East Dr", "London", 0)); 83 | 84 | 85 | var john = new Employee("John", new Address("123 London Road", "London", 123)); 86 | 87 | //var chris = john; 88 | var jane = new Employee(john); 89 | 90 | jane.Name = "Jane"; 91 | 92 | WriteLine(john); // oop s, john is called chris 93 | WriteLine(jane); 94 | 95 | 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Creational/Singleton/AmbientContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DotNetDesignPatternDemos.Creational.Singleton.AmbientStack 6 | { 7 | // non-thread-safe global context 8 | public sealed class BuildingContext : IDisposable 9 | { 10 | public int WallHeight = 0; 11 | public int WallThickness = 300; // etc. 12 | private static Stack stack 13 | = new Stack(); 14 | 15 | static BuildingContext() 16 | { 17 | // ensure there's at least one state 18 | stack.Push(new BuildingContext(0)); 19 | } 20 | 21 | public BuildingContext(int wallHeight) 22 | { 23 | WallHeight = wallHeight; 24 | stack.Push(this); 25 | } 26 | 27 | public static BuildingContext Current => stack.Peek(); 28 | 29 | public void Dispose() 30 | { 31 | // not strictly necessary 32 | if (stack.Count > 1) 33 | stack.Pop(); 34 | } 35 | } 36 | 37 | public class Building 38 | { 39 | public readonly List Walls = new List(); 40 | 41 | public override string ToString() 42 | { 43 | var sb = new StringBuilder(); 44 | foreach (var wall in Walls) 45 | sb.AppendLine(wall.ToString()); 46 | return sb.ToString(); 47 | } 48 | } 49 | 50 | public struct Point 51 | { 52 | private int X, Y; 53 | 54 | public Point(int x, int y) 55 | { 56 | X = x; 57 | Y = y; 58 | } 59 | 60 | public override string ToString() 61 | { 62 | return $"{nameof(X)}: {X}, {nameof(Y)}: {Y}"; 63 | } 64 | } 65 | 66 | public class Wall 67 | { 68 | public Point Start, End; 69 | public int Height; 70 | 71 | public const int UseAmbient = Int32.MinValue; 72 | 73 | // public Wall(Point start, Point end, int elevation = UseAmbient) 74 | // { 75 | // Start = start; 76 | // End = end; 77 | // Elevation = elevation; 78 | // } 79 | 80 | public Wall(Point start, Point end) 81 | { 82 | Start = start; 83 | End = end; 84 | //Elevation = BuildingContext.Elevation; 85 | Height = BuildingContext.Current.WallHeight; 86 | } 87 | 88 | public override string ToString() 89 | { 90 | return $"{nameof(Start)}: {Start}, {nameof(End)}: {End}, " + 91 | $"{nameof(Height)}: {Height}"; 92 | } 93 | } 94 | 95 | public class AmbientContextDemo 96 | { 97 | public static void Main(string[] args) 98 | { 99 | var house = new Building(); 100 | 101 | // ground floor 102 | //var e = 0; 103 | house.Walls.Add(new Wall(new Point(0,0), new Point(5000,0)/*, e*/)); 104 | house.Walls.Add(new Wall(new Point(0,0), new Point(0,4000)/*, e*/)); 105 | 106 | // first floor 107 | //e = 3500; 108 | using (new BuildingContext(3500)) 109 | { 110 | house.Walls.Add(new Wall(new Point(0, 0), new Point(5000, 0) /*, e*/)); 111 | house.Walls.Add(new Wall(new Point(0, 0), new Point(0, 4000) /*, e*/)); 112 | } 113 | 114 | // back to ground again 115 | // e = 0; 116 | house.Walls.Add(new Wall(new Point(5000,0), new Point(5000, 4000)/*, e*/)); 117 | 118 | Console.WriteLine(house); 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /Creational/Singleton/Exercise.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace DotNetDesignPatternDemos.Creational.Singleton 5 | { 6 | namespace Coding.Exercise 7 | { 8 | public class SingletonTester 9 | { 10 | public static bool IsSingleton(Func func) 11 | { 12 | var obj1 = func(); 13 | var obj2 = func(); 14 | return ReferenceEquals(obj1, obj2); 15 | } 16 | } 17 | } 18 | 19 | namespace Coding.Exercise.Tests 20 | { 21 | [TestFixture] 22 | public class FirstTestSuite 23 | { 24 | [Test] 25 | public void Test() 26 | { 27 | var obj = new object(); 28 | Assert.IsTrue(SingletonTester.IsSingleton(() => obj)); 29 | Assert.IsFalse(SingletonTester.IsSingleton(() => new object())); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Creational/Singleton/Monostate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using static System.Console; 3 | 4 | namespace DotNetDesignPatternDemos.Creational.Singleton.Monostate 5 | { 6 | public class ChiefExecutiveOfficer 7 | { 8 | private static string name; 9 | private static int age; 10 | 11 | public string Name 12 | { 13 | get => name; 14 | set => name = value; 15 | } 16 | 17 | public int Age 18 | { 19 | get => age; 20 | set => age = value; 21 | } 22 | 23 | public override string ToString() 24 | { 25 | return $"{nameof(Name)}: {Name}, {nameof(Age)}: {Age}"; 26 | } 27 | } 28 | 29 | public class Demo 30 | { 31 | static void Main(string[] args) 32 | { 33 | var ceo = new ChiefExecutiveOfficer(); 34 | ceo.Name = "Adam Smith"; 35 | ceo.Age = 55; 36 | 37 | var ceo2 = new ChiefExecutiveOfficer(); 38 | WriteLine(ceo2); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Creational/Singleton/Multiton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | 5 | namespace DotNetDesignPatternDemos.Creational.Multiton 6 | { 7 | 8 | // can only allow two components related to subsystems 9 | enum Subsystem 10 | { 11 | Main, 12 | Backup 13 | } 14 | 15 | class Printer 16 | { 17 | private Printer() { } 18 | 19 | public static Printer Get(Subsystem ss) 20 | { 21 | if (instances.ContainsKey(ss)) 22 | return instances[ss]; 23 | 24 | var instance = new Printer(); 25 | instances[ss] = instance; 26 | return instance; 27 | } 28 | 29 | private static readonly Dictionary instances 30 | = new Dictionary(); 31 | } 32 | 33 | class Demo 34 | { 35 | public static void Main(string[] args) 36 | { 37 | var primary = Printer.Get(Subsystem.Main); 38 | var backup = Printer.Get(Subsystem.Backup); 39 | 40 | var backupAgain = Printer.Get(Subsystem.Backup); 41 | 42 | Console.WriteLine(ReferenceEquals(backup, backupAgain)); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Creational/Singleton/PerThreadSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace DotNetDesignPatternDemos.Creational.Singleton.PerThread 6 | { 7 | public sealed class PerThreadSingleton 8 | { 9 | private static ThreadLocal threadInstance 10 | = new ThreadLocal( 11 | () => new PerThreadSingleton()); 12 | 13 | public int Id; 14 | 15 | private PerThreadSingleton() 16 | { 17 | Id = Thread.CurrentThread.ManagedThreadId; 18 | } 19 | 20 | public static PerThreadSingleton Instance => threadInstance.Value; 21 | } 22 | 23 | public class Demo 24 | { 25 | public static void Main(string[] args) 26 | { 27 | var t1 = Task.Factory.StartNew(() => 28 | { 29 | Console.WriteLine($"t1: " + PerThreadSingleton.Instance.Id); 30 | }); 31 | var t2 = Task.Factory.StartNew(() => 32 | { 33 | Console.WriteLine($"t2: " + PerThreadSingleton.Instance.Id); 34 | Console.WriteLine($"t2 again: " + PerThreadSingleton.Instance.Id); 35 | }); 36 | Task.WaitAll(t1, t2); 37 | } 38 | 39 | 40 | } 41 | } -------------------------------------------------------------------------------- /Creational/Singleton/capitals.txt: -------------------------------------------------------------------------------- 1 | Tokyo 2 | 33200000 3 | New York 4 | 17800000 5 | Sao Paulo 6 | 17700000 7 | Seoul 8 | 17500000 9 | Mexico City 10 | 17400000 11 | Osaka 12 | 16425000 13 | Manila 14 | 14750000 15 | Mumbai 16 | 14350000 17 | Delhi 18 | 14300000 19 | Jakarta 20 | 14250000 -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/FunctionalBuilder.fs: -------------------------------------------------------------------------------- 1 | module FunctionalDesignPatternDemos.FunctionalBuilder 2 | open System.Text 3 | 4 | let p args = 5 | let allArgs = args |> String.concat "\n" 6 | ["

    "; allArgs; "

    "] |> String.concat "\n" 7 | 8 | let img url = "" 9 | 10 | [] 11 | let main argv = 12 | let html = 13 | p [ 14 | "Check out this picture"; 15 | img "pokemon.com/pikachu.png" 16 | ] 17 | printfn "%s" html 18 | 19 | 0 -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/FunctionalDecorator.fs: -------------------------------------------------------------------------------- 1 | module FunctionalDesignPatternDemos.FunctionalDecorator 2 | open System.Diagnostics 3 | 4 | let doWork() = 5 | printfn "Doing some work" 6 | 7 | let logger work name = 8 | let sw = Stopwatch.StartNew() 9 | printfn "%s %s" "Entering method" name 10 | work() 11 | sw.Stop() 12 | printfn "Exiting method %s; %fs elapsed" name sw.Elapsed.TotalSeconds 13 | 14 | //[] 15 | let main argv = 16 | let loggedWork() = logger doWork "doWork" 17 | loggedWork() 18 | 0 -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/FunctionalDesignPatternDemos.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/FunctionalFactory.fs: -------------------------------------------------------------------------------- 1 | module FunctionalDesignPatternDemos.FunctionalFactory 2 | 3 | type ICountryInfo = 4 | abstract member Capital : string 5 | 6 | type Country = 7 | | USA 8 | | UK 9 | with 10 | static member Create = function 11 | | "USA" | "America" -> USA 12 | | "UK" | "England" -> UK 13 | | _ -> failwith "No such country" 14 | 15 | let make country = 16 | match country with 17 | | USA -> { new ICountryInfo with 18 | member x.Capital = "Washington" } 19 | | UK -> { new ICountryInfo with 20 | member x.Capital = "London" } 21 | 22 | //[] 23 | let main argv = 24 | 25 | let uk = make Country.UK 26 | printfn "%s" uk.Capital // London 27 | 28 | let usa = Country.Create "America" 29 | 30 | 0 -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/FunctionalStrategy.fs: -------------------------------------------------------------------------------- 1 | module FunctionalDesignPatternDemos.FunctionalStrategy 2 | 3 | let processList items startToken itemAction endToken = 4 | let mid = items |> (Seq.map itemAction) |> (String.concat "\n") 5 | [startToken; mid; endToken] |> String.concat "\n" 6 | 7 | let processListHtml items = 8 | processList items "
      " (fun i -> "
    • " + i + "
    • ") "
    " 9 | 10 | let processListMarkdown items = 11 | processList items "" (fun i -> " * " + i) "" 12 | 13 | //[] 14 | let main argv = 15 | let items = ["hello"; "world"] 16 | printfn "%s" (processListHtml items) 17 | printfn "%s" (processListMarkdown items) 18 | 0 -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/Interpreter.fs: -------------------------------------------------------------------------------- 1 | module FunctionalDesignPatternDemos.Interpreter 2 | 3 | open System 4 | open System.IO 5 | open System.Xml 6 | open System.Xml.Linq 7 | open Microsoft.FSharp.Reflection 8 | 9 | type Expression = 10 | Math of Expression list 11 | | Plus of lhs:Expression * rhs:Expression 12 | | Value of value:string 13 | member self.Val = 14 | let rec eval expr = 15 | match expr with 16 | | Math m -> eval(m.Head) 17 | | Plus (lhs, rhs) -> eval lhs + eval rhs 18 | | Value v -> v |> int 19 | eval self 20 | 21 | let text = @" 22 | 23 | 2 24 | 3 25 | 26 | " 27 | 28 | let cases = FSharpType.GetUnionCases (typeof) 29 | |> Array.map(fun f -> 30 | (f.Name, FSharpValue.PreComputeUnionConstructor(f))) 31 | |> Map.ofArray 32 | 33 | let makeCamelCase (text:string) = 34 | Char.ToUpper(text.Chars(0)).ToString() + text.Substring(1) 35 | 36 | let rec recursiveBuild (root:XElement) = 37 | let name = root.Name.LocalName |> makeCamelCase 38 | 39 | let makeCase parameters = 40 | try 41 | let caseInfo = cases.Item name 42 | (caseInfo parameters) :?> Expression 43 | with 44 | | exp -> raise <| new Exception(String.Format("Failed to create {0} : {1}", name, exp.Message)) 45 | 46 | let elems = root.Elements() |> Seq.toArray 47 | let values = elems |> Array.map(fun f -> recursiveBuild f) 48 | if elems.Length = 0 then 49 | let rootValue = root.Value.Trim() 50 | makeCase [| box rootValue |] 51 | else 52 | try 53 | values |> Array.map box |> makeCase 54 | with 55 | | _ -> makeCase [| values |> Array.toList |] 56 | 57 | let rec print expr = 58 | match expr with 59 | | Math m -> print m.Head 60 | | Plus (lhs, rhs) -> String.Format("({0}+{1})", print lhs, print rhs) 61 | | Value v -> v 62 | 63 | let rec eval expr = 64 | match expr with 65 | | Math m -> eval m.Head 66 | | Plus (lhs, rhs) -> eval lhs + eval rhs 67 | | Value v -> v |> int 68 | 69 | //[] 70 | let main argv = 71 | use stringReader = new StringReader(text) 72 | use xmlReader = XmlReader.Create(stringReader) 73 | let doc = XDocument.Load(xmlReader) 74 | 75 | let parsed = recursiveBuild doc.Root 76 | printf "%s = %d" (print parsed) (eval parsed) 77 | 78 | 0 -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/Program.fs: -------------------------------------------------------------------------------- 1 | module FunctionalDesignPatternDemos.Program 2 | 3 | open System 4 | 5 | let addFive x = x + 5 6 | let timesTwo x = x * 2 7 | 8 | //let timesTwoAddFive x = x |> timesTwo |> addFive 9 | let timesTwoAddFive = timesTwo >> addFive 10 | 11 | //[] 12 | let main argv = 13 | printfn "%i" (addFive (timesTwo 3)) // 11 14 | printfn "%i" (3 |> timesTwo |> addFive) 15 | printfn "%i" (addFive <| (timesTwo <| 3)) 16 | 17 | printfn "%i" (timesTwoAddFive 3) 18 | 0 -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/TemplateMethod.fs: -------------------------------------------------------------------------------- 1 | module FunctionalDesignPatternDemos.TemplateMethod 2 | 3 | type GameState = { 4 | mutable CurrentPlayer: int; 5 | mutable NumberOfPlayers: int; 6 | mutable WinningPlayer: int; 7 | } 8 | 9 | let runGame initialState startAction takeTurnAction haveWinnerAction = 10 | let state = initialState 11 | startAction state 12 | while not (haveWinnerAction state) do 13 | takeTurnAction state 14 | printfn "Player %i wins." state.WinningPlayer 15 | 16 | let chess() = 17 | let mutable turn = 0 18 | let mutable maxTurns = 10 19 | let state = { 20 | NumberOfPlayers = 2; 21 | CurrentPlayer = 0; 22 | WinningPlayer = -1; 23 | } 24 | let start state = 25 | printfn "Starting a game of chess with %i players" state.NumberOfPlayers 26 | 27 | let takeTurn state = 28 | printfn "Turn %i taken by player %i." turn state.CurrentPlayer 29 | state.CurrentPlayer <- (state.CurrentPlayer+1) % state.NumberOfPlayers 30 | turn <- turn + 1 31 | state.WinningPlayer <- state.CurrentPlayer 32 | 33 | let haveWinner state = 34 | turn = maxTurns 35 | 36 | runGame state start takeTurn haveWinner 37 | 38 | //[] 39 | let main argv = 40 | 41 | chess() 42 | 43 | 0 -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/bin/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeTarget": { 3 | "name": ".NETCoreApp,Version=v2.1", 4 | "signature": "" 5 | }, 6 | "compilationOptions": {}, 7 | "targets": { 8 | ".NETCoreApp,Version=v2.1": { 9 | "FunctionalDesignPatternDemos/1.0.0": { 10 | "dependencies": { 11 | "FSharp.Core": "4.7.0" 12 | }, 13 | "runtime": { 14 | "FunctionalDesignPatternDemos.dll": {} 15 | } 16 | }, 17 | "FSharp.Core/4.7.0": { 18 | "runtime": { 19 | "lib/netstandard2.0/FSharp.Core.dll": { 20 | "assemblyVersion": "4.7.0.0", 21 | "fileVersion": "4.700.19.40208" 22 | } 23 | }, 24 | "resources": { 25 | "lib/netstandard2.0/cs/FSharp.Core.resources.dll": { 26 | "locale": "cs" 27 | }, 28 | "lib/netstandard2.0/de/FSharp.Core.resources.dll": { 29 | "locale": "de" 30 | }, 31 | "lib/netstandard2.0/es/FSharp.Core.resources.dll": { 32 | "locale": "es" 33 | }, 34 | "lib/netstandard2.0/fr/FSharp.Core.resources.dll": { 35 | "locale": "fr" 36 | }, 37 | "lib/netstandard2.0/it/FSharp.Core.resources.dll": { 38 | "locale": "it" 39 | }, 40 | "lib/netstandard2.0/ja/FSharp.Core.resources.dll": { 41 | "locale": "ja" 42 | }, 43 | "lib/netstandard2.0/ko/FSharp.Core.resources.dll": { 44 | "locale": "ko" 45 | }, 46 | "lib/netstandard2.0/pl/FSharp.Core.resources.dll": { 47 | "locale": "pl" 48 | }, 49 | "lib/netstandard2.0/pt-BR/FSharp.Core.resources.dll": { 50 | "locale": "pt-BR" 51 | }, 52 | "lib/netstandard2.0/ru/FSharp.Core.resources.dll": { 53 | "locale": "ru" 54 | }, 55 | "lib/netstandard2.0/tr/FSharp.Core.resources.dll": { 56 | "locale": "tr" 57 | }, 58 | "lib/netstandard2.0/zh-Hans/FSharp.Core.resources.dll": { 59 | "locale": "zh-Hans" 60 | }, 61 | "lib/netstandard2.0/zh-Hant/FSharp.Core.resources.dll": { 62 | "locale": "zh-Hant" 63 | } 64 | } 65 | } 66 | } 67 | }, 68 | "libraries": { 69 | "FunctionalDesignPatternDemos/1.0.0": { 70 | "type": "project", 71 | "serviceable": false, 72 | "sha512": "" 73 | }, 74 | "FSharp.Core/4.7.0": { 75 | "type": "package", 76 | "serviceable": true, 77 | "sha512": "sha512-6Bf2qzSCaJoP6tTfO+a5muUS7QbyJANqSut73zQvwQ9It5P0ITz9VKseeia2ofco30c55EQ0jA2wf2+A2gpGnw==", 78 | "path": "fsharp.core/4.7.0", 79 | "hashPath": "fsharp.core.4.7.0.nupkg.sha512" 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/bin/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Functional/FunctionalDesignPatternDemos/bin/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.dll -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/bin/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Functional/FunctionalDesignPatternDemos/bin/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.pdb -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/bin/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.runtimeconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeOptions": { 3 | "additionalProbingPaths": [ 4 | "C:\\Users\\Дима\\.dotnet\\store\\|arch|\\|tfm|", 5 | "C:\\Users\\Дима\\.nuget\\packages" 6 | ] 7 | } 8 | } -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/bin/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.runtimeconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeOptions": { 3 | "tfm": "netcoreapp2.1", 4 | "framework": { 5 | "name": "Microsoft.NETCore.App", 6 | "version": "2.1.0" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the FSharp WriteCodeFragment class. 3 | // 4 | namespace FSharp 5 | 6 | open System 7 | open System.Reflection 8 | 9 | 10 | [] 11 | [] 12 | [] 13 | [] 14 | [] 15 | [] 16 | [] 17 | do() 18 | -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.AssemblyInfoInputs.cache: -------------------------------------------------------------------------------- 1 | 2b2f19fe7e5bf2dbf4ca0ca648e82497636e3dd0 2 | -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.assets.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.assets.cache -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.dll -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.fsproj.CoreCompileInputs.cache: -------------------------------------------------------------------------------- 1 | 9407fed0f361129ebb9a69d13b647e1d05535bca 2 | -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.fsproj.FileListAbsolute.txt: -------------------------------------------------------------------------------- 1 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.deps.json 2 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.runtimeconfig.json 3 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.runtimeconfig.dev.json 4 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.dll 5 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.pdb 6 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.fsprojAssemblyReference.cache 7 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.dll 8 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.pdb 9 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.AssemblyInfoInputs.cache 10 | C:\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.AssemblyInfo.fs 11 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.deps.json 12 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.runtimeconfig.json 13 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.runtimeconfig.dev.json 14 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.dll 15 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\bin\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.pdb 16 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.fsprojAssemblyReference.cache 17 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.AssemblyInfoInputs.cache 18 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.AssemblyInfo.fs 19 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.dll 20 | C:\Users\Дима\Dropbox\Projects\Demos\DotNetDesignPatternDemos\Functional\FunctionalDesignPatternDemos\obj\Debug\netcoreapp2.1\FunctionalDesignPatternDemos.pdb 21 | -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.fsprojAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.fsprojAssemblyReference.cache -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/design-patterns-in-.net-core-3/00715a06e8987c995cb344e3e823f1c4fc93837e/Functional/FunctionalDesignPatternDemos/obj/Debug/netcoreapp2.1/FunctionalDesignPatternDemos.pdb -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/FunctionalDesignPatternDemos.fsproj.nuget.cache: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dgSpecHash": "DFgJ1wGZ1EwwDn12HVVeu6WmO/x1N87BFVX6KYGTT1Sbz2DIpxwx4v93e0kwNWFkKfzhHP+Ro15Hs8AcGZWu1w==", 4 | "success": true 5 | } -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/FunctionalDesignPatternDemos.fsproj.nuget.dgspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": 1, 3 | "restore": { 4 | "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\FunctionalDesignPatternDemos.fsproj": {} 5 | }, 6 | "projects": { 7 | "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\FunctionalDesignPatternDemos.fsproj": { 8 | "version": "1.0.0", 9 | "restore": { 10 | "projectUniqueName": "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\FunctionalDesignPatternDemos.fsproj", 11 | "projectName": "FunctionalDesignPatternDemos", 12 | "projectPath": "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\FunctionalDesignPatternDemos.fsproj", 13 | "packagesPath": "C:\\Users\\Дима\\.nuget\\packages\\", 14 | "outputPath": "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\obj\\", 15 | "projectStyle": "PackageReference", 16 | "configFilePaths": [ 17 | "C:\\Users\\Дима\\AppData\\Roaming\\NuGet\\NuGet.Config", 18 | "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" 19 | ], 20 | "originalTargetFrameworks": [ 21 | "netcoreapp2.1" 22 | ], 23 | "sources": { 24 | "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, 25 | "https://api.nuget.org/v3/index.json": {} 26 | }, 27 | "frameworks": { 28 | "netcoreapp2.1": { 29 | "projectReferences": {} 30 | } 31 | } 32 | }, 33 | "frameworks": { 34 | "netcoreapp2.1": { 35 | "dependencies": { 36 | "FSharp.Core": { 37 | "target": "Package", 38 | "version": "[4.7.0, )" 39 | }, 40 | "Microsoft.NETCore.App": { 41 | "suppressParent": "All", 42 | "target": "Package", 43 | "version": "[2.1.0, )", 44 | "autoReferenced": true 45 | } 46 | }, 47 | "imports": [ 48 | "net461", 49 | "net462", 50 | "net47", 51 | "net471", 52 | "net472", 53 | "net48" 54 | ], 55 | "assetTargetFallback": true, 56 | "warn": true, 57 | "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\3.1.201\\RuntimeIdentifierGraph.json" 58 | } 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/FunctionalDesignPatternDemos.fsproj.nuget.g.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | True 5 | NuGet 6 | $(MSBuildThisFileDirectory)project.assets.json 7 | $(UserProfile)\.nuget\packages\ 8 | C:\Users\Дима\.nuget\packages\ 9 | PackageReference 10 | 5.3.0 11 | 12 | 13 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/FunctionalDesignPatternDemos.fsproj.nuget.g.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/project.nuget.cache: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "dgSpecHash": "JNxrS3oQKUG40Q/oHoab3pjKp1fwBWFLovhHWDShXnVhU6rKgyo7AasEMH05nkjFMag5vr9SQj0LLOgPYQ/WoA==", 4 | "success": true, 5 | "projectFilePath": "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\FunctionalDesignPatternDemos.fsproj", 6 | "expectedPackageFiles": [ 7 | "C:\\Users\\Dmitri\\.nuget\\packages\\fsharp.core\\4.7.0\\fsharp.core.4.7.0.nupkg.sha512", 8 | "C:\\Users\\Dmitri\\.nuget\\packages\\microsoft.netcore.app\\2.1.0\\microsoft.netcore.app.2.1.0.nupkg.sha512", 9 | "C:\\Users\\Dmitri\\.nuget\\packages\\microsoft.netcore.dotnetapphost\\2.1.0\\microsoft.netcore.dotnetapphost.2.1.0.nupkg.sha512", 10 | "C:\\Users\\Dmitri\\.nuget\\packages\\microsoft.netcore.dotnethostpolicy\\2.1.0\\microsoft.netcore.dotnethostpolicy.2.1.0.nupkg.sha512", 11 | "C:\\Users\\Dmitri\\.nuget\\packages\\microsoft.netcore.dotnethostresolver\\2.1.0\\microsoft.netcore.dotnethostresolver.2.1.0.nupkg.sha512", 12 | "C:\\Users\\Dmitri\\.nuget\\packages\\microsoft.netcore.platforms\\2.1.0\\microsoft.netcore.platforms.2.1.0.nupkg.sha512", 13 | "C:\\Users\\Dmitri\\.nuget\\packages\\microsoft.netcore.targets\\2.1.0\\microsoft.netcore.targets.2.1.0.nupkg.sha512", 14 | "C:\\Users\\Dmitri\\.nuget\\packages\\netstandard.library\\2.0.3\\netstandard.library.2.0.3.nupkg.sha512" 15 | ], 16 | "logs": [] 17 | } -------------------------------------------------------------------------------- /Functional/FunctionalDesignPatternDemos/obj/project.packagespec.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "restore": { 4 | "projectUniqueName": "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\FunctionalDesignPatternDemos.fsproj", 5 | "projectName": "FunctionalDesignPatternDemos", 6 | "projectPath": "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\FunctionalDesignPatternDemos.fsproj", 7 | "outputPath": "C:\\Dropbox\\Projects\\Demos\\DotNetDesignPatternDemos\\Functional\\FunctionalDesignPatternDemos\\obj\\", 8 | "projectStyle": "PackageReference", 9 | "fallbackFolders": [ 10 | "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder" 11 | ], 12 | "originalTargetFrameworks": [ 13 | "netcoreapp2.1" 14 | ], 15 | "sources": { 16 | "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, 17 | "https://api.nuget.org/v3/index.json": {}, 18 | "https://dotnet.myget.org/F/roslyn/api/v3/index.json": {} 19 | }, 20 | "frameworks": { 21 | "netcoreapp2.1": { 22 | "projectReferences": {} 23 | } 24 | } 25 | }, 26 | "frameworks": { 27 | "netcoreapp2.1": { 28 | "dependencies": { 29 | "FSharp.Core": { 30 | "target": "Package", 31 | "version": "[4.7.0, )" 32 | }, 33 | "Microsoft.NETCore.App": { 34 | "suppressParent": "All", 35 | "target": "Package", 36 | "version": "[2.1.0, )", 37 | "autoReferenced": true 38 | } 39 | }, 40 | "imports": [ 41 | "net461", 42 | "net462", 43 | "net47", 44 | "net471", 45 | "net472", 46 | "net48" 47 | ], 48 | "assetTargetFallback": true, 49 | "warn": true, 50 | "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\3.1.100\\RuntimeIdentifierGraph.json" 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Freeware License, some rights reserved 2 | 3 | Copyright (c) 2020 Dmitri Nesteruk 4 | 5 | Permission is hereby granted, free of charge, to anyone obtaining a copy 6 | of this software and associated documentation files (the "Software"), 7 | to work with the Software within the limits of freeware distribution and fair use. 8 | This includes the rights to use, copy, and modify the Software for personal use. 9 | Users are also allowed and encouraged to submit corrections and modifications 10 | to the Software for the benefit of other users. 11 | 12 | It is not allowed to reuse, modify, or redistribute the Software for 13 | commercial use in any way, or for a user’s educational materials such as books 14 | or blog articles without prior permission from the copyright holder. 15 | 16 | The above copyright notice and this permission notice need to be included 17 | in all copies or substantial portions of the software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*Design Patterns in .NET Core 3*](https://www.apress.com/9781484261798) by Dmitri Nesteruk (Apress, 2020). 4 | 5 | [comment]: #cover 6 | ![Cover image](9781484261798.jpg) 7 | 8 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 9 | 10 | ## Releases 11 | 12 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 13 | 14 | ## Contributions 15 | 16 | See the file Contributing.md for more information on how you can contribute to this repository. -------------------------------------------------------------------------------- /SOLID/DIP.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using static System.Console; 5 | 6 | namespace DotNetDesignPatternDemos.SOLID.DIP 7 | { 8 | // hl modules should not depend on low-level; both should depend on abstractions 9 | // abstractions should not depend on details; details should depend on abstractions 10 | 11 | public enum Relationship 12 | { 13 | Parent, 14 | Child, 15 | Sibling 16 | } 17 | 18 | public class Person 19 | { 20 | public string Name; 21 | // public DateTime DateOfBirth; 22 | } 23 | 24 | public interface IRelationshipBrowser 25 | { 26 | IEnumerable FindAllChildrenOf(string name); 27 | } 28 | 29 | public class Relationships : IRelationshipBrowser // low-level 30 | { 31 | private List<(Person,Relationship,Person)> relations 32 | = new List<(Person, Relationship, Person)>(); 33 | 34 | public void AddParentAndChild(Person parent, Person child) 35 | { 36 | relations.Add((parent, Relationship.Parent, child)); 37 | relations.Add((child, Relationship.Child, parent)); 38 | } 39 | 40 | public List<(Person, Relationship, Person)> Relations => relations; 41 | 42 | public IEnumerable FindAllChildrenOf(string name) 43 | { 44 | return relations 45 | .Where(x => x.Item1.Name == name 46 | && x.Item2 == Relationship.Parent).Select(r => r.Item3); 47 | } 48 | } 49 | 50 | public class Research 51 | { 52 | public Research(Relationships relationships) 53 | { 54 | // high-level: find all of john's children 55 | //var relations = relationships.Relations; 56 | //foreach (var r in relations 57 | // .Where(x => x.Item1.Name == "John" 58 | // && x.Item2 == Relationship.Parent)) 59 | //{ 60 | // WriteLine($"John has a child called {r.Item3.Name}"); 61 | //} 62 | } 63 | 64 | public Research(IRelationshipBrowser browser) 65 | { 66 | foreach (var p in browser.FindAllChildrenOf("John")) 67 | { 68 | WriteLine($"John has a child called {p.Name}"); 69 | } 70 | } 71 | 72 | static void Main() 73 | { 74 | var parent = new Person {Name = "John"}; 75 | var child1 = new Person {Name = "Chris"}; 76 | var child2 = new Person {Name = "Matt"}; 77 | 78 | // low-level module 79 | var relationships = new Relationships(); 80 | relationships.AddParentAndChild(parent, child1); 81 | relationships.AddParentAndChild(parent, child2); 82 | 83 | new Research(relationships); 84 | 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /SOLID/ISP.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DotNetDesignPatternDemos.SOLID.ISP 4 | { 5 | public class Document 6 | { 7 | } 8 | 9 | public interface IMachine 10 | { 11 | void Print(Document d); 12 | void Fax(Document d); 13 | void Scan(Document d); 14 | } 15 | 16 | // ok if you need a multifunction machine 17 | public class MultiFunctionPrinter : IMachine 18 | { 19 | public void Print(Document d) 20 | { 21 | // 22 | } 23 | 24 | public void Fax(Document d) 25 | { 26 | // 27 | } 28 | 29 | public void Scan(Document d) 30 | { 31 | // 32 | } 33 | } 34 | 35 | public class OldFashionedPrinter : IMachine 36 | { 37 | public void Print(Document d) 38 | { 39 | // yep 40 | } 41 | 42 | public void Fax(Document d) 43 | { 44 | throw new System.NotImplementedException(); 45 | } 46 | 47 | [Obsolete("Not supported", true)] 48 | public void Scan(Document d) 49 | { 50 | throw new System.NotImplementedException(); 51 | } 52 | } 53 | 54 | public interface IPrinter 55 | { 56 | void Print(Document d); 57 | } 58 | 59 | public interface IScanner 60 | { 61 | void Scan(Document d); 62 | } 63 | 64 | public class Printer : IPrinter 65 | { 66 | public void Print(Document d) 67 | { 68 | 69 | } 70 | } 71 | 72 | public class Photocopier : IPrinter, IScanner 73 | { 74 | public void Print(Document d) 75 | { 76 | throw new System.NotImplementedException(); 77 | } 78 | 79 | public void Scan(Document d) 80 | { 81 | throw new System.NotImplementedException(); 82 | } 83 | } 84 | 85 | public interface IMultiFunctionDevice : IPrinter, IScanner // 86 | { 87 | 88 | } 89 | 90 | public class MultiFunctionMachine : IMultiFunctionDevice 91 | { 92 | // compose this out of several modules 93 | private IPrinter printer; 94 | private IScanner scanner; 95 | 96 | public MultiFunctionMachine(IPrinter printer, IScanner scanner) 97 | { 98 | this.printer = printer ?? throw new ArgumentNullException(paramName: nameof(printer)); 99 | this.scanner = scanner ?? throw new ArgumentNullException(paramName: nameof(scanner)); 100 | } 101 | 102 | public void Print(Document d) 103 | { 104 | printer.Print(d); 105 | } 106 | 107 | public void Scan(Document d) 108 | { 109 | scanner.Scan(d); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /SOLID/LSP.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using static System.Console; 4 | 5 | namespace DotNetDesignPatternDemos.SOLID.LSP 6 | { 7 | // using a classic example 8 | public class Rectangle 9 | { 10 | //public int Width { get; set; } 11 | //public int Height { get; set; } 12 | 13 | public virtual int Width { get; set; } 14 | public virtual int Height { get; set; } 15 | 16 | public Rectangle() {} 17 | 18 | public Rectangle(int width, int height) 19 | { 20 | Width = width; 21 | Height = height; 22 | } 23 | 24 | public bool IsSquare => Width == Height; 25 | 26 | public int Area => Width * Height; 27 | 28 | public override string ToString() 29 | { 30 | return $"{nameof(Width)}: {Width}, {nameof(Height)}: {Height}"; 31 | } 32 | } 33 | 34 | public class Square : Rectangle 35 | { 36 | public Square() 37 | { 38 | } 39 | 40 | public Square(int side) 41 | { 42 | Width = Height = side; 43 | } 44 | 45 | //public new int Width 46 | //{ 47 | // set 48 | // { 49 | // base.Width = base.Height = value; 50 | // } 51 | //} 52 | 53 | //public new int Height 54 | //{ 55 | // set { base.Width = base.Height = value; } 56 | //} 57 | 58 | public override int Width // nasty side effects 59 | { 60 | set => base.Width = base.Height = value; 61 | } 62 | 63 | public override int Height 64 | { 65 | set => base.Width = base.Height = value; 66 | } 67 | } 68 | 69 | public class Demo 70 | { 71 | public static void UseIt(Rectangle r) 72 | { 73 | int width = r.Width; 74 | r.Height = 10; 75 | WriteLine($"Expected area of {10*width}, got {r.Area}"); 76 | } 77 | 78 | static void Main() 79 | { 80 | var rc = new Rectangle(2,3); 81 | UseIt(rc); 82 | 83 | var sq = new Square(5); 84 | UseIt(sq); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /SOLID/SRP.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using static System.Console; 6 | 7 | namespace DotNetDesignPatternDemos.SOLID.SRP 8 | { 9 | // just stores a couple of journal entries and ways of 10 | // working with them 11 | public class Journal 12 | { 13 | private readonly List entries = new List(); 14 | 15 | private static int count = 0; 16 | 17 | public void AddEntry(string text) 18 | { 19 | entries.Add(text); 20 | } 21 | 22 | public void RemoveEntry(int index) 23 | { 24 | if (index < entries.Count && index >= 0) 25 | entries.RemoveAt(index); 26 | } 27 | 28 | public override string ToString() 29 | { 30 | return string.Join(Environment.NewLine, entries); 31 | } 32 | 33 | // breaks single responsibility principle 34 | public void Save(string filename, bool overwrite = false) 35 | { 36 | File.WriteAllText(filename, ToString()); 37 | } 38 | 39 | public void Load(string filename) 40 | { 41 | 42 | } 43 | 44 | public void Load(Uri uri) 45 | { 46 | 47 | } 48 | } 49 | 50 | // handles the responsibility of persisting objects 51 | public class PersistenceManager 52 | { 53 | public void SaveToFile(Journal journal, string filename, bool overwrite = false) 54 | { 55 | if (overwrite || !File.Exists(filename)) 56 | File.WriteAllText(filename, journal.ToString()); 57 | } 58 | } 59 | 60 | public class Demo 61 | { 62 | static void Main(string[] args) 63 | { 64 | var j = new Journal(); 65 | j.AddEntry("I cried today."); 66 | j.AddEntry("I ate a bug."); 67 | WriteLine(j); 68 | 69 | var p = new PersistenceManager(); 70 | var filename = @"c:\temp\journal.txt"; 71 | p.SaveToFile(j, filename); 72 | 73 | var psi = new ProcessStartInfo(); 74 | psi.FileName = filename; 75 | psi.UseShellExecute = true; 76 | Process.Start(psi); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Structural/Adapter/AdapterDI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Autofac; 4 | using Autofac.Features.Metadata; 5 | 6 | namespace DotNetDesignPatternDemos.Structural.Adapter.DI 7 | { 8 | public interface ICommand 9 | { 10 | void Execute(); 11 | } 12 | 13 | public class SaveCommand : ICommand 14 | { 15 | public void Execute() 16 | { 17 | Console.WriteLine("Saving current file"); 18 | } 19 | } 20 | 21 | public class OpenCommand : ICommand 22 | { 23 | public void Execute() 24 | { 25 | Console.WriteLine("Opening a file"); 26 | } 27 | } 28 | 29 | public class Button 30 | { 31 | private ICommand command; 32 | private string name; 33 | 34 | public Button(ICommand command, string name) 35 | { 36 | this.command = command; 37 | this.name = name; 38 | } 39 | 40 | public void Click() 41 | { 42 | command.Execute(); 43 | } 44 | 45 | public void PrintMe() 46 | { 47 | Console.WriteLine($"I am a button called {name}"); 48 | } 49 | } 50 | 51 | public class Editor 52 | { 53 | public IEnumerable