├── .gitignore ├── AbstractFactory ├── README.md ├── abstract-factory.sln ├── src │ ├── Factory │ │ ├── AbstractFactory.csproj │ │ ├── AbstractVehicleFactory.cs │ │ ├── Factories │ │ │ ├── Cyclefactory.cs │ │ │ ├── MotorBikeFactory.cs │ │ │ ├── MotorVehicleFactory.cs │ │ │ └── TruckFactory.cs │ │ ├── IVehicle.cs │ │ ├── IVehicleFactory.cs │ │ ├── VehicleFactory.cs │ │ └── Vehicles │ │ │ ├── Cycles.cs │ │ │ ├── MotorBikes.cs │ │ │ ├── MotorCars.cs │ │ │ ├── Trucks.cs │ │ │ └── VehicleRequirements.cs │ └── VehicleGame │ │ ├── Program.cs │ │ └── VehicleGame.csproj └── tests │ ├── AbstractFactoryTests.csproj │ └── UnitTest1.cs ├── Adapter ├── Adapter.sln ├── README.md └── src │ └── AdapterSample │ ├── AdapterSample.csproj │ ├── Bicycle.cs │ ├── ITransport.cs │ ├── Program.cs │ └── Transport.cs ├── Builder ├── README.md ├── builder-pattern.sln ├── src │ ├── BuilderPattern.csproj │ ├── Gender.cs │ ├── Person.cs │ └── PersonBuilder.cs ├── test │ └── BuilderTest │ │ ├── BuilderTest.csproj │ │ └── Program.cs └── tests │ └── BuilderTests │ ├── BuilderTests.csproj │ └── UnitTest1.cs ├── Decorator └── README.md ├── FactoryMethod ├── README.md ├── factory-method.sln ├── img │ └── factory-pattern.jpg ├── src │ ├── Car.cs │ ├── FactoryPattern.csproj │ ├── IVehicle.cs │ ├── Motorbike.cs │ ├── Truck.cs │ ├── UniCycle.cs │ ├── VehicleFactory.cs │ └── VehicleGame │ │ ├── Program.cs │ │ └── VehicleGame.csproj └── tests │ ├── FactoryPatternTest.csproj │ └── UnitTest1.cs ├── LICENSE ├── Mediator ├── README.md ├── docker-compose.yml ├── mediator.sln └── src │ └── mediator │ ├── Activities │ ├── RouteNames.cs │ └── Salutations │ │ ├── Create │ │ ├── CreateSalutationCommand.cs │ │ ├── Post.cs │ │ └── PostHandler.cs │ │ └── Get │ │ ├── Get.cs │ │ ├── GetSalutationHandler.cs │ │ ├── GetSalutationQuery.cs │ │ └── SalutationResponse.cs │ ├── MappingProfile.cs │ ├── Program.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── mediator.csproj ├── Prototype ├── README.md ├── prototype.sln └── src │ ├── Threenine.Client │ ├── Program.cs │ └── Threenine.Client.csproj │ ├── Threenine.Employee │ ├── Developer.cs │ ├── Threenine.Employee.csproj │ └── Typist.cs │ └── interface │ ├── IClonable.cs │ └── Threenine.Interface.csproj ├── README.md ├── SimpleFactory ├── README.md ├── simple-factory.sln ├── src │ ├── FirstNameFirst.cs │ ├── LastNameFirst.cs │ ├── SimpleFactory.csproj │ ├── UserName.cs │ └── UsernameFactory.cs └── tests │ ├── FirstNameFirstTest.cs │ ├── SimpleFactoryTest.cs │ └── SimpleFactoryTest.csproj ├── Singleton ├── README.md ├── singleton.sln ├── src │ ├── TestConsole │ │ ├── Program.cs │ │ └── TestConsole.csproj │ └── Threenine.Print │ │ ├── AlmostLazy │ │ └── Spooler.cs │ │ ├── DoubleLock │ │ └── Spooler.cs │ │ ├── FullLazy │ │ └── Spooler.cs │ │ ├── GenericLazy │ │ └── Spooler.cs │ │ ├── PrintQueue.cs │ │ ├── Simple │ │ └── Spooler.cs │ │ ├── SimpleThreadSafe │ │ └── Spooler.cs │ │ ├── Spool.cs │ │ └── Threenine.Print.csproj └── test │ └── Threenine.Print.Tests │ ├── DoubleLockTests.cs │ ├── GenericLazyTests.cs │ └── Threenine.Print.Tests.csproj └── strategy ├── README.md ├── StrategyClient ├── IRule.cs ├── Program.cs ├── Rules │ ├── ColoredTerminalRule.cs │ ├── NotANumberRule.cs │ ├── NotInRangeRule.cs │ ├── ProfoundStatementRule.cs │ └── SnarkyCommentRule.cs └── StrategyClient.csproj ├── img.png ├── img_1.png └── strategy.sln /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | #Visual Studio Code 7 | .vscode 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Dd]ebugPublic/ 21 | [Rr]elease/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | **/Properties/launchSettings.json 59 | 60 | # StyleCop 61 | StyleCopReport.xml 62 | 63 | # Files built by Visual Studio 64 | *_i.c 65 | *_p.c 66 | *_i.h 67 | *.ilk 68 | *.meta 69 | *.obj 70 | *.iobj 71 | *.pch 72 | *.pdb 73 | *.ipdb 74 | *.pgc 75 | *.pgd 76 | *.rsp 77 | *.sbr 78 | *.tlb 79 | *.tli 80 | *.tlh 81 | *.tmp 82 | *.tmp_proj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | 259 | # Microsoft Fakes 260 | FakesAssemblies/ 261 | 262 | # GhostDoc plugin setting file 263 | *.GhostDoc.xml 264 | 265 | # Node.js Tools for Visual Studio 266 | .ntvs_analysis.dat 267 | node_modules/ 268 | 269 | # Visual Studio 6 build log 270 | *.plg 271 | 272 | # Visual Studio 6 workspace options file 273 | *.opt 274 | 275 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 276 | *.vbw 277 | 278 | # Visual Studio LightSwitch build output 279 | **/*.HTMLClient/GeneratedArtifacts 280 | **/*.DesktopClient/GeneratedArtifacts 281 | **/*.DesktopClient/ModelManifest.xml 282 | **/*.Server/GeneratedArtifacts 283 | **/*.Server/ModelManifest.xml 284 | _Pvt_Extensions 285 | 286 | # Paket dependency manager 287 | .paket/paket.exe 288 | paket-files/ 289 | 290 | # FAKE - F# Make 291 | .fake/ 292 | 293 | # JetBrains Rider 294 | .idea/ 295 | *.sln.iml 296 | 297 | # CodeRush 298 | .cr/ 299 | 300 | # Python Tools for Visual Studio (PTVS) 301 | __pycache__/ 302 | *.pyc 303 | 304 | # Cake - Uncomment if you are using it 305 | # tools/** 306 | # !tools/packages.config 307 | 308 | # Tabs Studio 309 | *.tss 310 | 311 | # Telerik's JustMock configuration file 312 | *.jmconfig 313 | 314 | # BizTalk build output 315 | *.btp.cs 316 | *.btm.cs 317 | *.odx.cs 318 | *.xsd.cs 319 | 320 | # OpenCover UI analysis results 321 | OpenCover/ 322 | 323 | # Azure Stream Analytics local run output 324 | ASALocalRun/ 325 | 326 | # MSBuild Binary and Structured Log 327 | *.binlog 328 | 329 | # NVidia Nsight GPU debugger configuration file 330 | *.nvuser 331 | 332 | # MFractors (Xamarin productivity tool) working folder 333 | .mfractor/ 334 | -------------------------------------------------------------------------------- /AbstractFactory/README.md: -------------------------------------------------------------------------------- 1 | # Abstract Factory Pattern 2 | 3 | The Abstract Factory Pattern is one level of abstraction higher than [Factory Method Design Pattern](https://garywoodfine.com/factory-method-design-pattern/). Using the Abstract Factory Pattern a framework is defined, which produces objects that follow a general pattern and at runtime this factory is paired with any concrete factory to produce objects that follow a defined pattern. 4 | 5 | The Abstract Factory Pattern is a creational Gang of Four (GoF) design pattern, defined in their seminal book [ Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2N22a2H), in which they presented a catalogue of simple and succinct solutions to commonly occurring design problems. 6 | 7 | ### What is the Abstract Factory Pattern 8 | The Abstract Factory Pattern is used when you want to return several related classes of objects, each of which can return several different objects on request. Typically you may use the Abstract Factory Pattern, in-conjunction with other factory patterns like [Simple Factory Pattern](https://garywoodfine.com/simple-factory-pattern/) and the [Factory Method Pattern](https://garywoodfine.com/factory-method-design-pattern/). 9 | 10 | The best way to think of the Abstract factory pattern, is that it is a super factory, or a *factory of factories*. Typically it is an interface which is responsible for creating a factory of related objects without explicitly specifying the derived classes. 11 | ![Abstract Factory Pattern](https://garywoodfine.com/wp-content/uploads/2018/11/abstractFactoryPattern.jpg) 12 | 13 | The key components of the Abstract Factory Pattern are : 14 | 15 | **Abstract Factory:** An abstract container which creates abstract objects. 16 | 17 | **Concrete Factory:** Implements an abstract container to create concrete object. 18 | 19 | **Abstract Object:** An interface defined with the attributes of the Object to be created. 20 | 21 | **Concrete Object:** The actual object by referring related abstract objects. 22 | 23 | **Client:** The client application that creates families of related objects using the factory. 24 | 25 | ### Example Abstract Factory in C# 26 | 27 | In our example we will continue with our sample game application of building a Vehicle depending on a set of user requirements passed into our method. This is hypothetical game we started creating in the [Factory Method Design Pattern](https://garywoodfine.com/factory-method-design-pattern/) article. We will *refactor* this application to introduce abstract factory design pattern and provide the user with some additional choices they can provide. 28 | 29 | The user will be able to stipulate the Number of Wheels, whether they have an engine, if they will be carrying cargo and if they will be carrying passengers. Once this information is supplied the application will return the best type of vehicle to suit their purpose. The game will be a simple Console application which simply asks the users to enter their choice and responds with an answer. 30 | 31 | I've kept the code rather simple, in order to illustrate a point. The code for our Entire console is follows: 32 | 33 | ```c# 34 | static void Main(string[] args) 35 | { 36 | var requirements = new VehicleRequirements(); 37 | 38 | Console.WriteLine( "To Build a Vehicle answer the following questions"); 39 | 40 | Console.WriteLine("How many wheels do you have "); 41 | var wheels = Console.ReadLine(); 42 | int wheelCount = 0; 43 | 44 | if (!int.TryParse(wheels, out wheelCount)) 45 | { 46 | wheelCount= 1; 47 | } 48 | 49 | requirements.NumberOfWheels = wheelCount; 50 | 51 | Console.WriteLine("Do you have an engine ( Y/n )"); 52 | var engine = Console.ReadLine(); 53 | switch (engine) 54 | { 55 | case "Y": 56 | requirements.Engine = true; 57 | break; 58 | case "N": 59 | requirements.Engine = false; 60 | break; 61 | default: 62 | requirements.Engine = false; 63 | break; 64 | } 65 | 66 | Console.WriteLine("How many passengers will you be carrying ? (1 - 10)"); 67 | 68 | var passengers = Console.ReadLine(); 69 | var passengerCount = 0; 70 | 71 | if (!int.TryParse(passengers, out passengerCount)) 72 | { 73 | passengerCount = 1; 74 | } 75 | 76 | requirements.Passengers = passengerCount; 77 | 78 | 79 | Console.WriteLine("Will you be carrying cargo"); 80 | 81 | var cargo = Console.ReadLine(); 82 | switch (cargo) 83 | { 84 | case "Y": 85 | requirements.Engine = true; 86 | break; 87 | case "N": 88 | requirements.Engine = false; 89 | break; 90 | default: 91 | requirements.Engine = false; 92 | break; 93 | } 94 | 95 | var vehicle = GetVehicle(requirements); 96 | 97 | Console.WriteLine(vehicle.GetType().Name); 98 | } 99 | 100 | ``` 101 | 102 | The code in this section despite being quite messy at this point, and pretty hard to read, (This is rather deliberate at this stage because we will be addressing this in a future article!) - does nothing more than ask a user a set of questions and adds answers to a defined `VehicleRequirements` is then passed to method which then calls the `Abstract Factory` which I will explain shortly. 103 | 104 | The `GetVehicle` Method will return an instance of class which implements our `IVehicle` intefacce. 105 | 106 | ```c# 107 | private static IVehicle GetVehicle(VehicleRequirements requirements) 108 | { 109 | var factory = new VehicleFactory(); 110 | IVehicle vehicle; 111 | 112 | if (requirements.HasEngine) 113 | { 114 | return factory.MotorVehicleFactory().Create(requirements); 115 | } 116 | 117 | return factory 118 | .CycleFactory().Create(requirements); 119 | 120 | } 121 | ``` 122 | This is the method where we actually start to see the implementation of our Abstract Factory - or our *factory of factories*. 123 | 124 | In this contrived example, our method is going to do a check to see, if the type of vehicle we want to build has an engine or not and based on that data it will select whether we want to create a Motor powered vehicle or Pedal powered vehicle and redirect to appropriate factory to create the vehicle. Ideally, you'd probably defer this choice the Abstract factory itself, but I just wanted to illustrate the point, that using an abstract factory class we can still make decisions on which factory we wanted to use. 125 | 126 | Both of the methods defined on the abstract class will provide us with an object implementing `IVehicle` interface. The application doesn't control the ultimate end type of object but it can select which factory it wants to build the object based on certain criteria. 127 | 128 | Our abstract factory is actually an `abstract` class which is defined with 2 methods which require the implementation of 2 additional factories. You'll notice that these two methods are also just implementations of `IVehicleFactory`. 129 | 130 | ```c# 131 | public abstract class AbstractVehicleFactory 132 | { 133 | public abstract IVehicleFactory CycleFactory(); 134 | public abstract IVehicleFactory MotorVehicleFactory(); 135 | 136 | } 137 | ``` 138 | This class is nothing more than an abstract class and has no implementation code at all. We could if desired have some default implementation code which could be overidden if desired. However, for my purpose I have just left them as stubs. 139 | 140 | The class enables the implementation of two different factories, each will be responsible for returning different vehicle types. In our Case Cycles or Motor Vehicles. 141 | 142 | Our actual implementation of our class abstract factory class, would possibly look something like the following. 143 | 144 | ```c# 145 | public class VehicleFactory : AbstractVehicleFactory 146 | { 147 | public override IVehicleFactory CycleFactory() 148 | { 149 | return new Cyclefactory(); 150 | } 151 | 152 | public override IVehicleFactory MotorVehicleFactory() 153 | { 154 | return new MotorVehicleFactory(); 155 | } 156 | } 157 | ``` 158 | Our implementation of our Vehicle Factory class, in truth is probably considered bad coding practice, are nothing more than **Pass Through Methods** , but I have intentionally implemented this to illustrate a point that ultimately our methods will more than likely invoke an alternate factory. 159 | 160 | > A Pass Through Method is one that does little except invoke another method, whose signature is similar or identical to that of the calling method. 161 | >This typically denotes there is not a clean division between classes 162 | > 163 | >--- [John Ousterhout - A Philosophy of Software Design](https://garywoodfine.com/philosophy-of-software-design/) 164 | 165 | If we take a look at an implementation of one of the factories i.e `CycleFactory` you'll notice that I have actually elected to make use of the [Factory Method Pattern](https://garywoodfine.com/factory-method-design-pattern/) to create an instance of an object to return. I chose this to labour another point, in that as a developer you can use these patterns interchangeably and coincide with other patterns. 166 | 167 | ```c# 168 | public class Cyclefactory : IVehicleFactory 169 | { 170 | public IVehicle Create(VehicleRequirements requirements) 171 | { 172 | switch (requirements.Passengers) 173 | { 174 | case 1: 175 | if(requirements.NumberOfWheels == 1) return new Unicycle(); 176 | return new Bicycle(); 177 | case 2: 178 | return new Tandem(); 179 | case 3: 180 | return new Tricyle(); 181 | case 4: 182 | if (requirements.HasCargo) return new GoKart(); 183 | return new FamilyBike(); 184 | default: 185 | return new Bicycle(); 186 | } 187 | } 188 | } 189 | ``` 190 | This also serves as an emphasis on the notion of **Factory of Factories** concept. 191 | 192 | I have tried to keep the logic in this sample as contrived as possible, but also try to illustrate that this factory is ultimately responsible for instantiating the object that is passed back to calling client. This serves to highlight that, the client does not need to concern itself with creating the object and that the decision point for which object to return is separated by several layers. 193 | 194 | This enables improved testability via separation of concerns. Even in this simple example you would notice that client, only needs to concern itself with data that has been entered i.e. Validate the data as close the source as possible, it then passes that data to an abstract factory class which then disseminates it to the required factories to create an object. 195 | 196 | It is this feature which makes the Abstract Factory method such a popular and powerful pattern to learn to provide greater opportunity to unit test code. Using the pattern in-conjunction with other patterns further enhance testability of your code. 197 | 198 | ### Refactor the Factory 199 | 200 | The code for our abstract factory is working, but lets be honest it is rather **Dirty Code** and it does not actually provide all the benefits we could get from using the abstract factory pattern. For a few reasons I alluded to earlier. 201 | 202 | Let's conduct a slight refactor of factory pattern. The first one, is we could probably do with either removing the option of the client app from having to make the decision of which factory to use, or we could extend on that option. 203 | 204 | In order to illustrate an option, lets do a complete refactor and tidy up the code. My first option would be to remove those nasty **Pass Through Methods** they really do not provide a clean abstraction and in actually fact we could remove the the need to have 2 Factory methods on our class and just provide one. This will make our class even easier to understand and remove any chance of errors. 205 | 206 | In our case lets just provide the client with one choice of `Factory`. Our new refactored abstract factory class will now only have one method. 207 | 208 | ```c# 209 | public abstract class AbstractVehicleFactory 210 | { 211 | public abstract IVehicle Create(); 212 | } 213 | ``` 214 | 215 | With this complete, lets go ahead and complete the refactor of the implementation of our Factory Class. 216 | ```c# 217 | private readonly IVehicleFactory _factory; 218 | private readonly VehicleRequirements _requirements; 219 | 220 | public VehicleFactory(VehicleRequirements requirements) 221 | { 222 | _factory = requirements.HasEngine ? (IVehicleFactory) new MotorVehicleFactory() : new Cyclefactory(); 223 | _requirements = requirements; 224 | 225 | } 226 | public override IVehicle Create() 227 | { 228 | return _factory.Create(_requirements); 229 | } 230 | 231 | ``` 232 | 233 | You'll notice here, we've actually used the constructor to pass in the requirements and use the data to determine which factory is best suited to create our vehicle of choice. This will minimize the risk of the client using the wrong factory. 234 | 235 | The complexity of clients `GetVehicle` method has greatly been reduced, they now no longer need to concern themselves with inspecting the data and determining which factory to use. All they need to do is pass in the `VehicleRequirements` in the constructor and call the `Create` method and all the magic will just happen. 236 | 237 | ```c# 238 | private static IVehicle GetVehicle(VehicleRequirements requirements) 239 | { 240 | var factory = new VehicleFactory(requirements); 241 | return factory.Create(); 242 | } 243 | ``` 244 | 245 | What we have achieved here, is an important aspect of *Abstractions* 246 | 247 | > An Abstraction is a simplified view of an entity, which omits unimportant details. 248 | > 249 | >--- [John Ousterhout - A Philosophy of Software Design](https://garywoodfine.com/philosophy-of-software-design/) 250 | 251 | We have managed to [Pull Complexity Downwards](https://garywoodfine.com/philosophy-of-software-design#complexity) and shielded our users from complexity providing them with an easy to use interface. 252 | 253 | > When developing a module, look for opportunities to take a little bit of extra suffering upon yourself in order to reduce the suffering of your users. 254 | > 255 | >--- [John Ousterhout - A Philosophy of Software Design](https://garywoodfine.com/philosophy-of-software-design/) 256 | 257 | ### Consequences of Abstract Factory 258 | 259 | The main purpose of the Abstract Factory Pattern is to isolate concrete classes created. The actual class names of the classes are hidden in the factory and need not to be known at the client level at all. 260 | 261 | Due to the isolation of classes, developers can change or interchange these product class families as desired. Since only one instance type of concrete classes can be generated, this keeps developers cannot inadvertently create classes of different class families. However, the pattern does incur additional effort to add new class families since there is a need to define new, unambiguous conditions to create new classes. 262 | 263 | Despite all the classes that Abstract Factory generates inheriting the same base class or interface, there is nothing to prevent developers adding additional methods or have differing implementation logic. This presents problems when using any sub-class because you cannot ensure if your sub-class has the same methods available. 264 | 265 | To solve this problem, you can ensure that all classes only implement methods defined on a common interface or inherit the abstract class. 266 | 267 | 268 | ### Conclusion 269 | The Abstract Factory pattern enables developers to generically define families of related objects, leaving the actual concrete implementation of those objects to be implemented as needed. 270 | 271 | The Abstract Factory pattern is pretty common in C#, and the .net Framework has libraries that use it to provide a way to extend and customize their standard components. 272 | 273 | ## Sponsored by 274 | [![threenine logo](http://static.threenine.co.uk/img/github_footer.png)](https://threenine.co.uk/) 275 | 276 | 277 | 278 | 279 | 280 | -------------------------------------------------------------------------------- /AbstractFactory/abstract-factory.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F81C4515-9592-4CD4-8C73-9127812D8E7B}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AbstractFactory", "src\Factory\AbstractFactory.csproj", "{7E9C701F-56F8-4BEE-9F1C-95DD43946479}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VehicleGame", "src\VehicleGame\VehicleGame.csproj", "{48FBA777-4647-41E9-A178-B00322AA3393}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Debug|x64.ActiveCfg = Debug|Any CPU 28 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Debug|x64.Build.0 = Debug|Any CPU 29 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Debug|x86.ActiveCfg = Debug|Any CPU 30 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Debug|x86.Build.0 = Debug|Any CPU 31 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Release|x64.ActiveCfg = Release|Any CPU 34 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Release|x64.Build.0 = Release|Any CPU 35 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Release|x86.ActiveCfg = Release|Any CPU 36 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479}.Release|x86.Build.0 = Release|Any CPU 37 | {48FBA777-4647-41E9-A178-B00322AA3393}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {48FBA777-4647-41E9-A178-B00322AA3393}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {48FBA777-4647-41E9-A178-B00322AA3393}.Debug|x64.ActiveCfg = Debug|Any CPU 40 | {48FBA777-4647-41E9-A178-B00322AA3393}.Debug|x64.Build.0 = Debug|Any CPU 41 | {48FBA777-4647-41E9-A178-B00322AA3393}.Debug|x86.ActiveCfg = Debug|Any CPU 42 | {48FBA777-4647-41E9-A178-B00322AA3393}.Debug|x86.Build.0 = Debug|Any CPU 43 | {48FBA777-4647-41E9-A178-B00322AA3393}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {48FBA777-4647-41E9-A178-B00322AA3393}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {48FBA777-4647-41E9-A178-B00322AA3393}.Release|x64.ActiveCfg = Release|Any CPU 46 | {48FBA777-4647-41E9-A178-B00322AA3393}.Release|x64.Build.0 = Release|Any CPU 47 | {48FBA777-4647-41E9-A178-B00322AA3393}.Release|x86.ActiveCfg = Release|Any CPU 48 | {48FBA777-4647-41E9-A178-B00322AA3393}.Release|x86.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(NestedProjects) = preSolution 51 | {7E9C701F-56F8-4BEE-9F1C-95DD43946479} = {F81C4515-9592-4CD4-8C73-9127812D8E7B} 52 | {48FBA777-4647-41E9-A178-B00322AA3393} = {F81C4515-9592-4CD4-8C73-9127812D8E7B} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/AbstractFactory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/AbstractVehicleFactory.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public abstract class AbstractVehicleFactory 4 | { 5 | public abstract IVehicle Create(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Factories/Cyclefactory.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class Cyclefactory : IVehicleFactory 4 | { 5 | public IVehicle Create(VehicleRequirements requirements) 6 | { 7 | switch (requirements.Passengers) 8 | { 9 | case 1: 10 | if(requirements.NumberOfWheels == 1) return new Unicycle(); 11 | return new Bicycle(); 12 | case 2: 13 | return new Tandem(); 14 | case 3: 15 | return new Tricyle(); 16 | case 4: 17 | if (requirements.HasCargo) return new GoKart(); 18 | return new FamilyBike(); 19 | default: 20 | return new Bicycle(); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Factories/MotorBikeFactory.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class MotorBikeFactory : IVehicleFactory 4 | { 5 | public IVehicle Create(VehicleRequirements requirements) 6 | { 7 | switch (requirements.NumberOfWheels) 8 | { 9 | case 2: 10 | return new MotorBike(); 11 | case 3: 12 | return new TriBike(); 13 | default: 14 | return new MotorBike(); 15 | 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Factories/MotorVehicleFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace AbstractFactory 4 | { 5 | public class MotorVehicleFactory : IVehicleFactory 6 | { 7 | public IVehicle Create(VehicleRequirements requirements) 8 | { 9 | if(requirements.NumberOfWheels <= 3) return new MotorBikeFactory().Create(requirements); 10 | 11 | if (!requirements.HasCargo && requirements.NumberOfWheels == 4) 12 | { 13 | if (requirements.Passengers >= 4) 14 | { 15 | return new Sedan(); 16 | 17 | } 18 | else 19 | { 20 | return new MPV(); 21 | } 22 | 23 | } 24 | 25 | if (requirements.HasCargo && requirements.NumberOfWheels == 4) 26 | { 27 | return new Van(); 28 | } 29 | 30 | switch (requirements.NumberOfWheels) 31 | { 32 | case 6: 33 | case 8: 34 | return new Truck(); 35 | case 10: 36 | case 12 : 37 | return new HeavyGoodsTruck(); 38 | default : 39 | return new ArticulatedLorry(); 40 | 41 | } 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Factories/TruckFactory.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class TruckFactory : IVehicleFactory 4 | { 5 | public IVehicle Create(VehicleRequirements requirements) 6 | { 7 | throw new System.NotImplementedException(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/IVehicle.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public interface IVehicle 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/IVehicleFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AbstractFactory 4 | { 5 | public interface IVehicleFactory 6 | { 7 | IVehicle Create(VehicleRequirements requirements); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/VehicleFactory.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class VehicleFactory : AbstractVehicleFactory 4 | { 5 | private readonly IVehicleFactory _factory; 6 | private readonly VehicleRequirements _requirements; 7 | 8 | public VehicleFactory(VehicleRequirements requirements) 9 | { 10 | _factory = requirements.HasEngine ? (IVehicleFactory) new MotorVehicleFactory() : new Cyclefactory(); 11 | _requirements = requirements; 12 | 13 | } 14 | public override IVehicle Create() 15 | { 16 | return _factory.Create(_requirements); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Vehicles/Cycles.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class Unicycle : IVehicle 4 | { 5 | 6 | } 7 | public class Bicycle : IVehicle 8 | { 9 | 10 | } 11 | 12 | public class Tandem : IVehicle 13 | { 14 | 15 | } 16 | 17 | public class Tricyle : IVehicle 18 | { 19 | 20 | } 21 | 22 | public class FamilyBike : IVehicle 23 | { 24 | 25 | } 26 | 27 | public class GoKart : IVehicle 28 | { 29 | 30 | } 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Vehicles/MotorBikes.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class MotorBike : IVehicle 4 | { 5 | 6 | } 7 | 8 | public class TriBike : IVehicle 9 | { 10 | 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Vehicles/MotorCars.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class Sedan : IVehicle 4 | { 5 | 6 | } 7 | 8 | public class MPV : IVehicle 9 | { 10 | 11 | } 12 | 13 | public class Van : IVehicle 14 | { 15 | 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Vehicles/Trucks.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class Truck : IVehicle 4 | { 5 | 6 | } 7 | 8 | public class HeavyGoodsTruck : IVehicle 9 | { 10 | 11 | } 12 | 13 | public class ArticulatedLorry : IVehicle 14 | { 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /AbstractFactory/src/Factory/Vehicles/VehicleRequirements.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | public class VehicleRequirements 4 | { 5 | public int NumberOfWheels { get; set; } 6 | public bool HasEngine { get; set; } 7 | public int Passengers { get; set; } 8 | public bool HasCargo { get; set; } 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /AbstractFactory/src/VehicleGame/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using AbstractFactory; 4 | 5 | namespace VehicleGame 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | var requirements = new VehicleRequirements(); 12 | 13 | Console.WriteLine( "To Build a Vehicle answer the following questions"); 14 | 15 | Console.WriteLine("How many wheels do you have "); 16 | var wheels = Console.ReadLine(); 17 | int wheelCount = 0; 18 | 19 | if (!int.TryParse(wheels, out wheelCount)) 20 | { 21 | wheelCount= 1; 22 | } 23 | 24 | requirements.NumberOfWheels = wheelCount; 25 | 26 | Console.WriteLine("Do you have an engine ( Y/n )"); 27 | var engine = Console.ReadLine(); 28 | switch (engine.ToLower()) 29 | { 30 | case "y": 31 | requirements.HasEngine = true; 32 | break; 33 | case "n": 34 | requirements.HasEngine = false; 35 | break; 36 | default: 37 | requirements.HasEngine = false; 38 | break; 39 | } 40 | 41 | Console.WriteLine("How many passengers will you be carrying ? (1 - 10)"); 42 | 43 | var passengers = Console.ReadLine(); 44 | var passengerCount = 0; 45 | 46 | if (!int.TryParse(passengers, out passengerCount)) 47 | { 48 | passengerCount = 1; 49 | } 50 | 51 | requirements.Passengers = passengerCount; 52 | 53 | 54 | Console.WriteLine("Will you be carrying cargo"); 55 | 56 | var cargo = Console.ReadLine(); 57 | switch (cargo.ToLower()) 58 | { 59 | case "y": 60 | requirements.HasEngine = true; 61 | break; 62 | case "n": 63 | requirements.HasEngine = false; 64 | break; 65 | default: 66 | requirements.HasEngine = false; 67 | break; 68 | } 69 | 70 | var vehicle = GetVehicle(requirements); 71 | 72 | Console.WriteLine(vehicle.GetType().Name); 73 | } 74 | 75 | private static IVehicle GetVehicle(VehicleRequirements requirements) 76 | { 77 | var factory = new VehicleFactory(requirements); 78 | return factory.Create(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /AbstractFactory/src/VehicleGame/VehicleGame.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /AbstractFactory/tests/AbstractFactoryTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /AbstractFactory/tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace AbstractFactoryTests 5 | { 6 | public class UnitTest1 7 | { 8 | [Fact] 9 | public void Test1() 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Adapter/Adapter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdapterSample", "src\AdapterSample\AdapterSample.csproj", "{C076AA2A-8AE2-4820-AB40-C7F158AF80E1}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Debug|x64.ActiveCfg = Debug|Any CPU 24 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Debug|x64.Build.0 = Debug|Any CPU 25 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Debug|x86.ActiveCfg = Debug|Any CPU 26 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Debug|x86.Build.0 = Debug|Any CPU 27 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Release|x64.ActiveCfg = Release|Any CPU 30 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Release|x64.Build.0 = Release|Any CPU 31 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Release|x86.ActiveCfg = Release|Any CPU 32 | {C076AA2A-8AE2-4820-AB40-C7F158AF80E1}.Release|x86.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /Adapter/README.md: -------------------------------------------------------------------------------- 1 | ## Adapter pattern 2 | 3 | The Adapter Design Pattern is the first software design pattern of the Structural Pattern, that the Gang of Four (GOF) Design Patterns, presented in their book , [Design Patterns - Elements of Reusable Object-Oriented Software](https://amzn.to/2PdkTck) 4 | that I will discuss in series of Structural Design Patterns. 5 | 6 | The Adapter design pattern is a structural pattern that allows incompatible interfaces, from disparate systems to exchange data and work together. It is extremely useful when integrating tool-kits, libraries and other utilities together. Apple Macbook users will be very familiar adapters, which they will frequently use to plug various devices and network connections etc. 7 | 8 | [![USB C Hub Multiport Adapter, 13-in-1 MacBook Pro USB Type C Adapter Hub with Ethernet, VGA, 4K HDMI, 3 USB 2.0, 2 USB 3.0, 3-Slot Card Reader, 3.5mm Audio and Power Delivery](https://garywoodfine.com/wp-content/uploads/2021/04/adapter-pattern.jpg)](https://amzn.to/3gHAkXX) 9 | 10 | This is essentially a physical implementation of the Adapter pattern. [Head First Design Patterns](https://amzn.to/3tSqSVo "Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software") provides a really good succinct definition of the Adapter Pattern. 11 | 12 | > The Adapter pattern allows you to provide an object instance to a client that has a dependency on an interface that your instance does not implement. An Adapter class is created that fulfils the expected interface of the client but that implements the methods of the interface by delegating to different methods of another object. 13 | > 14 | > [Adaptive Code: Agile coding with design patterns and SOLID principles]() 15 | 16 | ### Two types of Adapters. 17 | 18 | There are typically two kinds of of adapters: 19 | * *object* adapters 20 | * *class* adapters 21 | 22 | #### Class adapters 23 | Typically you may only really encounter this type of pattern when using C or C++ or other languages that enable multiple inheritance. 24 | 25 | #### Object adapters 26 | It basically the only adapter pattern C# developers have available. 27 | 28 | > **The Adapter Pattern** 29 | > 30 | > Converts the interface of a class into another interface this client expects. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. 31 | 32 | The main advantage of this pattern is that it enables the use of another library in an application that has an incompatible interface by using an adapter that does the conversion. 33 | 34 | 35 | ![Adapter Pattern](https://garywoodfine.com/wp-content/uploads/2021/04/adapter-pattern.png) 36 | 37 | ### When to use the Adapter Pattern? 38 | There are a number of situations when making use of the Adapter pattern can be a great solution 39 | 40 | * A class needs to be reused that does not have an interface that a client requires. 41 | * Allow a system to use classes of another system that is incompatible with it. 42 | * Allow communication between a new and already existing system that is independent of each other. 43 | * Sometimes a toolkit or class library cannot be used because its interface is incompatible with the interface required by an application. 44 | 45 | ### Simple Adapter pattern implementation 46 | In its most simple form the Adapter Pattern can just be a simple *wrapper* class which implements an interface. However the implementation within the class my implement a different set of classes to deliver the functionality required. 47 | 48 | You may have an interface for Transport class that defines a `Commute` method. 49 | ```c# 50 | public interface ITransport 51 | { 52 | void Commute(); 53 | } 54 | ``` 55 | 56 | However, the only class you have a available is `Bicycle` class that has a `Pedal` method which will work for what you need. 57 | 58 | ```c# 59 | public class Bicycle 60 | { 61 | public void Pedal() 62 | { 63 | Console.WriteLine("Pedaling"); 64 | } 65 | } 66 | ``` 67 | 68 | The snag is that the method or class that is going to use it, can only use classes that implement the `ITransport` interface. 69 | 70 | We can use the Adapter pattern here to create a class that implements the `ITransport` interface, but it actually just wraps the `Bicycle` class. 71 | 72 | ```c# 73 | public class Transport : ITransport 74 | { 75 | private Bicycle _bike => new Bicycle(); 76 | public void Commute() 77 | { 78 | _bike.Pedal(); 79 | } 80 | } 81 | ``` 82 | 83 | You can now implement the `Transport` class in your application, because it implements the `ITransport` interface. 84 | 85 | ```c# 86 | class Program 87 | { 88 | static void Main(string[] args) 89 | { 90 | var transport = new Transport(); 91 | transport.Commute(); 92 | } 93 | } 94 | ``` 95 | That is all there is to the Adapter pattern. Even in this most simplistic of implementations you can see the power of enabling classes that my seem incompatible with your application , yet you can still make use of them. 96 | 97 | ### Conclusion 98 | The Adapter Pattern is a very simple pattern, but can be quite powerful and extremely useful, and is really a worthwhile pattern to be aware of. Many developers, will most likely have used the Adapter pattern, without actually being explicitly aware of it. 99 | 100 | The are more advanced implementation details of the Adapter pattern, but the fundamentals of the pattern remain the same. -------------------------------------------------------------------------------- /Adapter/src/AdapterSample/AdapterSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Adapter/src/AdapterSample/Bicycle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdapterSample 4 | { 5 | public class Bicycle 6 | { 7 | public void Pedal() 8 | { 9 | Console.WriteLine("Pedaling"); 10 | } 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /Adapter/src/AdapterSample/ITransport.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace AdapterSample 4 | { 5 | public interface ITransport 6 | { 7 | void Commute(); 8 | } 9 | } -------------------------------------------------------------------------------- /Adapter/src/AdapterSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AdapterSample 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | var transport = new Transport(); 10 | transport.Commute(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Adapter/src/AdapterSample/Transport.cs: -------------------------------------------------------------------------------- 1 | namespace AdapterSample 2 | { 3 | public class Transport : ITransport 4 | { 5 | private Bicycle _bike => new Bicycle(); 6 | public void Commute() 7 | { 8 | _bike.Pedal(); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /Builder/README.md: -------------------------------------------------------------------------------- 1 | 2 | The Builder Pattern is a creational Gang of Four (GoF) design pattern, defined in their seminal book 3 | [ Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2N22a2H), in which they presented a 4 | catalogue of simple and succinct solutions to commonly occurring design problems. 5 | 6 | The pattern is useful for encapsulating and abstracting the creation of objects. It is distinct from the more common 7 | [Factory Pattern](https://garywoodfine.com/factory-method-design-pattern/) because the Builder Pattern contains methods 8 | of customising the creation of an object. 9 | 10 | Whenever an object can be configured in multiple ways across multiple dimensions, the Builder Pattern can simplify the 11 | creation of objects and clarify the intent. 12 | 13 | ![Builder Pattern](https://garywoodfine.com/wp-content/uploads/2018/11/BuilderPattern.png) 14 | 15 | Let's explore the Builder Pattern and how developers can use it to construct objects from components. You may have 16 | already seen that the [Factory Pattern](https://garywoodfine.com/factory-method-design-pattern/) returns one of 17 | several different subclasses, depending on the data passed in arguments to creation methods. 18 | 19 | We'll now learn that the Builder Pattern assembles a number of objects in various ways depending on the data. 20 | 21 | ### Advantages of the Builder Pattern 22 | * The builder pattern enables developers to hide details of how an object is created 23 | * The builder pattern enables developers to vary the internal representation of an object it builds. 24 | * Each specific builder is independent of others and the rest of the application, improving Modularity and simplifies 25 | and enables the addition of other Builders. 26 | * Provides greater control over the creation of objects. 27 | 28 | The builder pattern is similar to the Abstract Factory Pattern in that both return classes made up of a number of 29 | other methods and objects. 30 | 31 | The main difference between the Builder Pattern and the Abstract Factory Pattern, is that the Abstract Factory Pattern 32 | returns a family of related classes and the Builder Pattern constructs a complex object step by step, depending on the 33 | data presented to it. 34 | 35 | ### Builder Pattern in Unit tests 36 | The builder pattern is a popular pattern to use in Unit tests, in fact one of my favourite tools to use in 37 | Unit Tests is [Nbuilder - A rapid test object generator](https://github.com/nbuilder/nbuilder), which if you read 38 | the source code also provides a great example of how to implement the builder pattern. 39 | 40 | In his book [Adaptive Code](https://amzn.to/2VyXJAN) Gary Maclean Hall states the builder pattern is useful for 41 | encapsulating and abstracting the creation of objects, and provides an example of using the builder pattern to help 42 | clarify the intent of unit tests, by assisting to eliminate any unnecessary arrange code. 43 | 44 | 45 | ### Example Builder Pattern 46 | 47 | {% github garywoodfine/software-design-patterns %} 48 | 49 | In this example, we are going to implement a very simple Builder Pattern and use it to create a Person class to 50 | contain some attributes to describe a person 51 | 52 | ```c# 53 | public class Person 54 | { 55 | public int Id { get; set; } 56 | 57 | public string Firstname { get; set; } 58 | 59 | public string Lastname { get; set; } 60 | 61 | public DateTime DateOfBirth { get; set; } 62 | 63 | public Gender Gender { get; set; } 64 | } 65 | 66 | 67 | ``` 68 | 69 | You may notice that we make use of an Enum to contain the value of the gender. 70 | 71 | ```c# 72 | public enum Gender 73 | { 74 | Male, 75 | Female 76 | } 77 | ``` 78 | There is nothing all that complicated about the class, it's a simple POCO class. We can now develop our Builder class, 79 | which again we will keep simple to help illustrate the point. The builder class will basically return the object 80 | in a string format. 81 | 82 | ```c# 83 | public class Person 84 | { 85 | public int Id { get; set; } 86 | 87 | public string Firstname { get; set; } 88 | 89 | public string Lastname { get; set; } 90 | 91 | public DateTime DateOfBirth { get; set; } 92 | 93 | public string Occupation { get; set; } 94 | 95 | public Gender Gender { get; set; } 96 | 97 | public override string ToString() 98 | { 99 | return $"Person with id: {Id} with date of birth {DateOfBirth.ToLongDateString()} 100 | and name {string.Concat(Firstname, " ",Lastname)} is a {Occupation}"; 101 | } 102 | } 103 | 104 | ``` 105 | 106 | The builder class in its simplest guise just a series of name constructor methods with arguments, 107 | you'll notice that they always return an instance of the class. The final method on the builder class is `Build` 108 | method will return the completed object. By convention this method is typically named `Build` or `Create` 109 | or something similar. 110 | 111 | We can now make use of our Builder to create a person as follows. 112 | 113 | ```c# 114 | class Program 115 | { 116 | static void Main(string[] args) 117 | { 118 | 119 | var person = new PersonBuilder() 120 | .Id(10) 121 | .Firstname("Gary") 122 | .Lastname("Woodfine") 123 | .Gender(Gender.Male) 124 | .DateOfBirth(DateTime.Now) 125 | .Occupation("Freelance Full-Stack Developer") 126 | .Build(); 127 | 128 | Console.WriteLine(person.ToString()); 129 | 130 | Console.ReadLine(); 131 | } 132 | } 133 | 134 | ``` 135 | We build the object by instantiation the PersonBuilder then adding the properties, then the final method we call is 136 | the `Build` method. We then simply call the `ToString()` method to write out our values. 137 | 138 | Using the Builder Pattern, we can avoid using large constructor methods to provide all the required parameters for 139 | constructing our object. Large constructor methods lead to unreadable and difficult to maintain code. It is possible 140 | that there may not always be the need to supply all arguments in constructor methods because in all probability they 141 | won't always be needed. 142 | 143 | In the above code, I intentionally introduced a code smell, in the `ToString()`, you'll notice there is a lot of 144 | string interpolation and even additional concatenation. I primarily because I wanted to highlight how the .net core 145 | makes use of the builder pattern. 146 | 147 | We can make use of the StringBuilder class, StringBuilder prevents having to recreate a string each time you 148 | are adding to it. Using the String class in C# means you are using an immutable object, but StringBuilder is much 149 | faster in most cases since it's not having to create a new String each time you append to it. 150 | 151 | We can now refactor our `ToString()` method as follows. 152 | 153 | ```c# 154 | public override string ToString()=> 155 | new StringBuilder() 156 | .Append("Person with id: ") 157 | .Append(Id.ToString()) 158 | .Append("with date of birth ") 159 | .Append(DateOfBirth.ToLongDateString()) 160 | .Append(" and name ") 161 | .Append(Firstname) 162 | .Append(" ") 163 | .Append(Lastname) 164 | .Append(" is a ") 165 | .Append(Occupation) 166 | .ToString(); 167 | ``` 168 | We use the StringBuilder to create the string. You'll notice, that even though I said by convention you could use 169 | the `Build` or `Create` to define the method that will return your object, but you don't really need to rather you 170 | could opt for another name, in the case of StringBuilder it is `ToString()` 171 | 172 | 173 | 174 | In the above example we have implemented a simple builder patter, however it probably isn't easy to determine why this 175 | actually provides any benefit to developers. After all,from this simple implementation you might be thinking but surely 176 | we could just a simply use C# object initialisation and get exactly the result. 177 | 178 | ```c# 179 | var person2 = new Person 180 | { 181 | Id = 10, 182 | Firstname = "Gary", 183 | Lastname = "Woodfine", 184 | DateOfBirth = DateTime.Now, 185 | Occupation = "Freelance Full Stack Developer", 186 | Gender = Gender.Male 187 | }; 188 | 189 | ``` 190 | The problem with this approach is that it is vry similar to passing arguments to a function, which inadvertently adds complexity to understanding the code. 191 | 192 | >The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided when possible. More than three (polyadic) requires very special justification – and then shouldn’t be used anyway. 193 | 194 | > [Uncle Bob - Clean Code](https://amzn.to/2PTO6ar) 195 | 196 | There will invariably be situations when instantiating objects that you will need to call a function to do something to provide a value to that object. i.e. Generate a new ID which may need calling out to function to get a newly created Id value etc. 197 | 198 | It is in situations like this that make the Builder pattern a much more viable option, and as defined in [Philosophy of Software Design](https://garywoodfine.com/philosophy-of-software-design/) we are able to pull complexity downwards. 199 | 200 | > When developing a module, look for opportunities to take a little bit of extra suffering upon yourself in order to reduce the suffering of your users. 201 | > John Ousterhout - [Philosophy of Software Design](https://amzn.to/2V8AHfC) 202 | 203 | ### Fluent Builder Pattern implementation 204 | 205 | The standard definition the Builder pattern separates the construction of a complex object from its representation so 206 | that the same construction process can create different representations. 207 | 208 | The Builder pattern provides step-by-step creation of a complex object so that the same construction process can 209 | create different representations is the routine in the builder pattern that also makes for finer control over the 210 | construction process. All the different builders generally inherit from an abstract builder class that declares the g 211 | eneral functions to be used by the director to let the builder create the product in parts. 212 | 213 | Builder has a similar motivation to the [abstract factory](https://garywoodfine.com/abstract-factory-design-pattern/) but, whereas in that pattern, the client uses the 214 | abstract factory class methods to create its own object, in Builder the client instructs the builder class on how to 215 | create the object and then asks it for the result. How the class is put together is up to the Builder class. It's a 216 | subtle difference. 217 | 218 | The Builder pattern is applicable when the algorithm for creating a complex object should be independent of the parts 219 | that make up the object and how they are assembled and the construction process must allow different representations 220 | for the object constructed. 221 | 222 | If we consider a person object and think of all the different variations we could expect to create a person object. 223 | For instance, how do we deal with married woman? Considering in some cases we may need to take into consideration her 224 | maiden name. How do we cater for trans-gender people? It soon becomes clear that there are all manner of rules and 225 | variations we need to consider when build a person object. All manner of varying combinations and all sorts of 226 | additional properties we will need to include. Rules that may not be easy or convenient to incorporate in object 227 | initialisation. 228 | 229 | In our first implementation of builder for a fluent interface of the Person class, we implemented the builder with no 230 | strings attached. We have not enforced rules for the order of assignment. 231 | 232 | The code is simple and easy to understand but it does leave the builder class open to misuse. We can implement the 233 | basic expression builder with method chaining in C# .NET. 234 | 235 | We're going to refactor our builder slightly to incorporate a new method `Create` which will accept Firstname and 236 | Lastname argument but more importantly we are going to remove the creation of the `Person` class from the constructor 237 | and into `Create` method. 238 | 239 | It also doesn't make much sense providing and Id to an object on creation, it is highly likely that a new Id should be 240 | created when the object is created. So we'll also remove the Id parameter from the Builder. 241 | 242 | Implementing a fluent interface is a relatively straight-forward task that can be done through the use of 243 | method chaining. Method chaining is simply a set of assignment methods that return itself. The result from each 244 | method can then call the next assignment method, and so-on. 245 | 246 | To guide the user and enforce rules of construction (such as, the Class can only be assigned once, 247 | followed by attributes), we utilize progressive interfaces. Where the method would return “this”, we instead return 248 | an interface for the next step in line. 249 | 250 | ```c# 251 | public class PersonBuilder 252 | { 253 | private Person _person; 254 | 255 | public PersonBuilder Create(string firstName, string lastName) 256 | { 257 | _person = new Person(); 258 | _person.Firstname = firstName; 259 | _person.Lastname = lastName; 260 | _person.Id = Guid.NewGuid(); 261 | return this; 262 | 263 | } 264 | public PersonBuilder DateOfBirth( DateTime dob) 265 | { 266 | _person.DateOfBirth = dob; 267 | return this; 268 | } 269 | 270 | public PersonBuilder Gender(Gender gender) 271 | { 272 | _person.Gender = gender; 273 | return this; 274 | } 275 | 276 | public PersonBuilder Occupation(string occupation) 277 | { 278 | _person.Occupation = occupation; 279 | return this; 280 | } 281 | 282 | public Person Build() 283 | { 284 | return _person; 285 | } 286 | } 287 | 288 | ``` 289 | 290 | we’ve implemented the Expression Builder pattern using method chaining. The class itself constructs a Person 291 | for us. We may then call any of the assignment methods to populate the fields and attributes of the Character class. 292 | Each method returns a copy of itself, allowing us to chain the assignment methods, one after the other, 293 | thus implementing our fluent interface in C# .NET. We can then finally call the `Build()` method to obtain the 294 | completed Person class. 295 | 296 | ### Summary 297 | 298 | We examined the Builder Pattern and seen how useful it is too create complex objects. We also looked at an example 299 | how the .net core framework itself makes use of the builder pattern to provide common functionality string building 300 | functionality. 301 | 302 | 303 | ## Sponsored by 304 | [![threenine logo](http://static.threenine.co.uk/img/github_footer.png)](https://threenine.co.uk/) 305 | -------------------------------------------------------------------------------- /Builder/builder-pattern.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuilderPattern", "src\BuilderPattern.csproj", "{6658F91E-1E6E-407D-9AF4-C251D5CDE389}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{83963CE7-BC4E-4338-B537-EDFDB094851F}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuilderTest", "test\BuilderTest\BuilderTest.csproj", "{2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{020C0F2E-4C84-430A-AC35-3314C0FDC000}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuilderTests", "tests\BuilderTests\BuilderTests.csproj", "{932F729E-6149-41EB-950E-5FD2AB1CAF2F}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Debug|x64 = Debug|x64 20 | Debug|x86 = Debug|x86 21 | Release|Any CPU = Release|Any CPU 22 | Release|x64 = Release|x64 23 | Release|x86 = Release|x86 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Debug|x64.ActiveCfg = Debug|Any CPU 32 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Debug|x64.Build.0 = Debug|Any CPU 33 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Debug|x86.ActiveCfg = Debug|Any CPU 34 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Debug|x86.Build.0 = Debug|Any CPU 35 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Release|x64.ActiveCfg = Release|Any CPU 38 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Release|x64.Build.0 = Release|Any CPU 39 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Release|x86.ActiveCfg = Release|Any CPU 40 | {6658F91E-1E6E-407D-9AF4-C251D5CDE389}.Release|x86.Build.0 = Release|Any CPU 41 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Debug|x64.ActiveCfg = Debug|Any CPU 44 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Debug|x64.Build.0 = Debug|Any CPU 45 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Debug|x86.ActiveCfg = Debug|Any CPU 46 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Debug|x86.Build.0 = Debug|Any CPU 47 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Release|x64.ActiveCfg = Release|Any CPU 50 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Release|x64.Build.0 = Release|Any CPU 51 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Release|x86.ActiveCfg = Release|Any CPU 52 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B}.Release|x86.Build.0 = Release|Any CPU 53 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Debug|x64.ActiveCfg = Debug|Any CPU 56 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Debug|x64.Build.0 = Debug|Any CPU 57 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Debug|x86.ActiveCfg = Debug|Any CPU 58 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Debug|x86.Build.0 = Debug|Any CPU 59 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Release|x64.ActiveCfg = Release|Any CPU 62 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Release|x64.Build.0 = Release|Any CPU 63 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Release|x86.ActiveCfg = Release|Any CPU 64 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F}.Release|x86.Build.0 = Release|Any CPU 65 | EndGlobalSection 66 | GlobalSection(NestedProjects) = preSolution 67 | {2385EE3D-FD73-4DB8-ACE3-B6B8B7ACCC0B} = {83963CE7-BC4E-4338-B537-EDFDB094851F} 68 | {932F729E-6149-41EB-950E-5FD2AB1CAF2F} = {020C0F2E-4C84-430A-AC35-3314C0FDC000} 69 | EndGlobalSection 70 | EndGlobal 71 | -------------------------------------------------------------------------------- /Builder/src/BuilderPattern.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Builder/src/Gender.cs: -------------------------------------------------------------------------------- 1 | namespace BuilderPattern 2 | { 3 | public enum Gender 4 | { 5 | Male, 6 | Female 7 | } 8 | } -------------------------------------------------------------------------------- /Builder/src/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace BuilderPattern 5 | { 6 | public class Person 7 | { 8 | public Guid Id { get; set; } 9 | 10 | public string Firstname { get; set; } 11 | 12 | public string Lastname { get; set; } 13 | 14 | public DateTime DateOfBirth { get; set; } 15 | 16 | public string Occupation { get; set; } 17 | 18 | public Gender Gender { get; set; } 19 | 20 | public override string ToString()=> 21 | new StringBuilder() 22 | .Append("Person with id: ") 23 | .Append(Id.ToString()) 24 | .Append(" with date of birth ") 25 | .Append(DateOfBirth.ToLongDateString()) 26 | .Append(" and name ") 27 | .Append(Firstname) 28 | .Append(" ") 29 | .Append(Lastname) 30 | .Append(" is a ") 31 | .Append(Occupation) 32 | .ToString(); 33 | 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Builder/src/PersonBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BuilderPattern 4 | { 5 | public class PersonBuilder 6 | { 7 | private Person _person; 8 | 9 | public PersonBuilder Create(string firstName, string lastName) 10 | { 11 | _person = new Person(); 12 | _person.Firstname = firstName; 13 | _person.Lastname = lastName; 14 | _person.Id = Guid.NewGuid(); 15 | return this; 16 | 17 | } 18 | public PersonBuilder DateOfBirth( DateTime dob) 19 | { 20 | _person.DateOfBirth = dob; 21 | return this; 22 | } 23 | 24 | public PersonBuilder Gender(Gender gender) 25 | { 26 | _person.Gender = gender; 27 | return this; 28 | } 29 | 30 | public PersonBuilder Occupation(string occupation) 31 | { 32 | _person.Occupation = occupation; 33 | return this; 34 | } 35 | 36 | public Person Build() 37 | { 38 | return _person; 39 | } 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Builder/test/BuilderTest/BuilderTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Builder/test/BuilderTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BuilderPattern; 3 | 4 | namespace BuilderTest 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | 11 | var person = new PersonBuilder() 12 | .Create("Gary", "Woodfine") 13 | .Gender(Gender.Male) 14 | .DateOfBirth(DateTime.Now) 15 | .Occupation("Freelance Full-Stack Developer") 16 | .Build(); 17 | 18 | 19 | Console.WriteLine(person.ToString()); 20 | 21 | 22 | Console.ReadLine(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Builder/tests/BuilderTests/BuilderTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Builder/tests/BuilderTests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace BuilderTests 5 | { 6 | public class UnitTest1 7 | { 8 | [Fact] 9 | public void Test1() 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Decorator/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garywoodfine/software-design-patterns/4acd2c4c6a0092ef84056f4301a6b45e520e8de2/Decorator/README.md -------------------------------------------------------------------------------- /FactoryMethod/README.md: -------------------------------------------------------------------------------- 1 | # Factory Method Design Pattern 2 | 3 | The factory concept is probably the most common design patterns and recurs throughout the object-oriented programming. 4 | You will find countless references and uses of the factory pattern within the [.net core foundational libraries](https://github.com/dotnet/corefx) and throughout the .net framework source code, most notable and probably one of the most commonly used factories can be found in the `System.Data.Common` namespace and the `DbProviderFactories`. 5 | 6 | The factory method design pattern falls under the Creational Design Patterns of [Gang of Four (GOF) Design Patterns](https://amzn.to/2RxkPTm), and is most commonly used to create objects. 7 | 8 | ##What is the Factory Method Pattern 9 | 10 | The factory method pattern is a clever but subtle extension to the concept that a class acts as a traffic cop and decides which sub class of a single hierarchy will be instantiated. 11 | 12 | In the factory pattern, developers create an object without exposing the creation logic. An interface is used for creating an object, but lets subclass decide which class to instantiate. Rather than defining each object manually, developers can do it programmatically. 13 | 14 | In short, a factory is an object that creates objects without the use of a constructor. 15 | 16 | The pattern does not actually have a decision point where one subclass is directly selected over another class. Instead, programs written to this pattern usually define an abstract class that creates objects but lets each subclass decide which object to create. 17 | 18 | 19 | 20 | ## When to use a Factory Method 21 | The are a number of circumstances when developing an application when making use of the Factory Method is suitable. These situation include : 22 | * A class can't anticipate which class objects it must create 23 | * A class uses its subclasses to specify which objects it creates 24 | * You need to localize the knowledge of which class gets created 25 | 26 | It is generally considered a bad idea for base classes to know implementation details of their derived types. It is in situations like this is when you should use the Abstract Factory pattern. 27 | 28 | A Typical situation maybe when a constructor needs to return an instance of type within which it resides, a factory method is able to return many different types of objects, all which belong to the same inheritance hierarchy. 29 | 30 | ## How to implement Factory Pattern 31 | We'll develop a factory method that enables the creation of a vehicle depending on the number of wheels required 32 | 33 | ![factory pattern](https://garywoodfine.com/wp-content/uploads/2018/10/factory-pattern.jpg) 34 | 35 | A vehicles can consist of any number of wheels, as an example, in a game when character needs to build a vehicle when they have set number of wheels. 36 | 37 | If we pass the number of Wheels to our factory method it will build the vehicle and return it. 38 | 39 | We'll keep this example trivial so we don't get lost in the detail, so we will define a simple interface of ```IVEHICLE``` which is nothing more than empty interface. 40 | We'll also Define 4 classes which will Inherit the ```IVehicle``` interface. Unicycle, MotorBike, Car and Truck 41 | 42 | ```c# 43 | public interface IVehicle 44 | { 45 | 46 | } 47 | public class Unicycle : IVehicle 48 | { 49 | 50 | } 51 | public class Car : IVehicle 52 | { 53 | 54 | } 55 | public class Motorbike : IVehicle 56 | { 57 | 58 | } 59 | public class Truck : IVehicle 60 | { 61 | 62 | } 63 | 64 | ``` 65 | 66 | We can now create a ```VechicleFactory``` class with a build method to create a vehicle depending on the 67 | number of wheels supplied. 68 | 69 | ```c# 70 | public static class VehicleFactory 71 | { 72 | public static IVehicle Build(int numberOfWheels) 73 | { 74 | switch (numberOfWheels) 75 | { 76 | case 1: 77 | return new UniCycle(); 78 | case 2: 79 | case 3: 80 | return new Motorbike(); 81 | case 4: 82 | return new Car(); 83 | default : 84 | return new Truck(); 85 | 86 | } 87 | } 88 | } 89 | ``` 90 | 91 | We can now develop our little game, in this example it will be a Console Application, and we'll ask the user to enter a number of 92 | wheels and then we'll tell them what type of Vehicle they built. 93 | 94 | ```c# 95 | class Program 96 | { 97 | static void Main(string[] args) 98 | { 99 | Console.WriteLine("Enter a number of wheels between 1 and 12 to build a vehicle and press enter"); 100 | 101 | var wheels = Console.ReadLine(); 102 | var vehicle = VehicleFactory.Build(Convert.ToInt32(wheels)); 103 | Console.WriteLine($" You built a {vehicle.GetType().Name}"); 104 | Console.Read(); 105 | } 106 | } 107 | ``` 108 | If you start the Console Application, you'll be asked a question to enter a number between 1 and 12 and a vehicle will be created depending on the number of wheels supplied. 109 | 110 | This is obviously a very trivial example of a Factory Method just in order to put a point across, as to how to actually use them. 111 | If you're looking for a more complex implementation of a Factory Method pattern, take a look at the [source code of my Threenine.Map](https://github.com/threenine/Threenine.Map) application, and more specifically the class I have cunningly named the [MapConfigurationFactory](https://github.com/threenine/Threenine.Map/blob/master/src/MapConfigurationFactory.cs). 112 | 113 | You'll notice that this class, is basically the main brain of the application and is responsible for building and loading all the mapping configurations that have been defined within the application. 114 | It has a number of entry points defined, but the main method I always use is the ```Scan``` method, which ultimately is the Abstract Factory Method. 115 | 116 | The library itself contains just 3 interfaces ```ICustomMap, IMapFrom, IMapTo``` , which enable developers to implement various mapping logic and associate it with the POCO or Entity classes it relates too. For detailed explanation to how it works [check out the documentation](https://threeninemap.readthedocs.io/en/latest/MapConfigurationFactory.html). 117 | 118 | In brief what the ```MapConfigurationFactory``` does, it uses reflection to iterate through all the libraries contained in an assembly and retrieves all the classes have been marked with any of the interfaces and loads the Mappings to the [Automapper - a Convention-based object-object mapper](https://automapper.org/). 119 | 120 | ```c# 121 | using System; 122 | using System.Collections.Generic; 123 | using System.Linq; 124 | using System.Reflection; 125 | using AutoMapper; 126 | 127 | namespace Threenine.Map 128 | { 129 | public class MapConfigurationFactory 130 | { 131 | public static void Scan(Func assemblyFilter = null) 132 | { 133 | var target = typeof(TType).Assembly; 134 | 135 | bool LoadAllFilter(AssemblyName x) => true; 136 | 137 | var assembliesToLoad = target.GetReferencedAssemblies() 138 | .Where(assemblyFilter ?? LoadAllFilter) 139 | .Select(Assembly.Load) 140 | .ToList(); 141 | 142 | assembliesToLoad.Add(target); 143 | 144 | LoadMapsFromAssemblies(assembliesToLoad.ToArray()); 145 | } 146 | 147 | public static void LoadMapsFromAssemblies(params Assembly[] assemblies) 148 | { 149 | var types = assemblies.SelectMany(a => a.GetExportedTypes()).ToArray(); 150 | LoadAllMappings(types); 151 | } 152 | 153 | 154 | public static void LoadAllMappings(IList types) 155 | { 156 | Mapper.Initialize( 157 | cfg => 158 | { 159 | LoadStandardMappings(cfg, types); 160 | LoadCustomMappings(cfg, types); 161 | }); 162 | } 163 | 164 | 165 | public static void LoadCustomMappings(IMapperConfigurationExpression config, IList types) 166 | { 167 | var instancesToMap = (from t in types 168 | from i in t.GetInterfaces() 169 | where typeof(ICustomMap).IsAssignableFrom(t) && 170 | !t.IsAbstract && 171 | !t.IsInterface 172 | select (ICustomMap) Activator.CreateInstance(t)).ToArray(); 173 | 174 | 175 | foreach (var map in instancesToMap) 176 | { 177 | map.CustomMap(config); 178 | } 179 | } 180 | 181 | public static void LoadStandardMappings(IMapperConfigurationExpression config, IList types) 182 | { 183 | var mapsFrom = (from t in types 184 | from i in t.GetInterfaces() 185 | where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>) && 186 | !t.IsAbstract && 187 | !t.IsInterface 188 | select new 189 | { 190 | Source = i.GetGenericArguments()[0], 191 | Destination = t 192 | }).ToArray(); 193 | 194 | 195 | foreach (var map in mapsFrom) 196 | { 197 | config.CreateMap(map.Source, map.Destination); 198 | } 199 | 200 | 201 | var mapsTo = (from t in types 202 | from i in t.GetInterfaces() 203 | where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapTo<>) && 204 | !t.IsAbstract && 205 | !t.IsInterface 206 | select new 207 | { 208 | Source = i.GetGenericArguments()[0], 209 | Destination = t 210 | }).ToArray(); 211 | 212 | 213 | foreach (var map in mapsTo) 214 | { 215 | config.CreateMap(map.Source, map.Destination); 216 | } 217 | } 218 | } 219 | } 220 | 221 | ``` 222 | 223 | ### Conclusion 224 | The Factory Method pattern, in my opinion is one of the most important patterns to understand within software development. It's the one pattern that in all likelihood that you'll most often implement. factory design pattern, is used to instantiate objects based on another data type. Factories are often used to reduce code bloat and make it easier to modify which objects need to be created. 225 | 226 | ## Sponsored by 227 | [![threenine logo](http://static.threenine.co.uk/img/github_footer.png)](https://threenine.co.uk/) 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /FactoryMethod/factory-method.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FactoryPattern", "src\FactoryPattern.csproj", "{F41CA761-DB72-4454-91BC-3BB7BA274814}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D4948635-5592-443B-B9EE-374E374898C6}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VehicleGame", "src\VehicleGame\VehicleGame.csproj", "{33B4BB3B-E553-40B5-9992-E6908993B4BE}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FactoryPatternTest", "tests\FactoryPatternTest.csproj", "{DA5E2649-C03E-4198-9181-6419FF3A8DB4}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Debug|x64.ActiveCfg = Debug|Any CPU 30 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Debug|x64.Build.0 = Debug|Any CPU 31 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Debug|x86.ActiveCfg = Debug|Any CPU 32 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Debug|x86.Build.0 = Debug|Any CPU 33 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Release|x64.ActiveCfg = Release|Any CPU 36 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Release|x64.Build.0 = Release|Any CPU 37 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Release|x86.ActiveCfg = Release|Any CPU 38 | {F41CA761-DB72-4454-91BC-3BB7BA274814}.Release|x86.Build.0 = Release|Any CPU 39 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Debug|x64.ActiveCfg = Debug|Any CPU 42 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Debug|x64.Build.0 = Debug|Any CPU 43 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Debug|x86.ActiveCfg = Debug|Any CPU 44 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Debug|x86.Build.0 = Debug|Any CPU 45 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Release|x64.ActiveCfg = Release|Any CPU 48 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Release|x64.Build.0 = Release|Any CPU 49 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Release|x86.ActiveCfg = Release|Any CPU 50 | {33B4BB3B-E553-40B5-9992-E6908993B4BE}.Release|x86.Build.0 = Release|Any CPU 51 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Debug|x64.ActiveCfg = Debug|Any CPU 54 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Debug|x64.Build.0 = Debug|Any CPU 55 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Debug|x86.ActiveCfg = Debug|Any CPU 56 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Debug|x86.Build.0 = Debug|Any CPU 57 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Release|x64.ActiveCfg = Release|Any CPU 60 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Release|x64.Build.0 = Release|Any CPU 61 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Release|x86.ActiveCfg = Release|Any CPU 62 | {DA5E2649-C03E-4198-9181-6419FF3A8DB4}.Release|x86.Build.0 = Release|Any CPU 63 | EndGlobalSection 64 | GlobalSection(NestedProjects) = preSolution 65 | {33B4BB3B-E553-40B5-9992-E6908993B4BE} = {D4948635-5592-443B-B9EE-374E374898C6} 66 | EndGlobalSection 67 | EndGlobal 68 | -------------------------------------------------------------------------------- /FactoryMethod/img/factory-pattern.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garywoodfine/software-design-patterns/4acd2c4c6a0092ef84056f4301a6b45e520e8de2/FactoryMethod/img/factory-pattern.jpg -------------------------------------------------------------------------------- /FactoryMethod/src/Car.cs: -------------------------------------------------------------------------------- 1 | using DefaultNamespace; 2 | 3 | namespace FactoryPattern 4 | { 5 | public class Car : IVehicle 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /FactoryMethod/src/FactoryPattern.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FactoryMethod/src/IVehicle.cs: -------------------------------------------------------------------------------- 1 | namespace DefaultNamespace 2 | { 3 | public interface IVehicle 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /FactoryMethod/src/Motorbike.cs: -------------------------------------------------------------------------------- 1 | using DefaultNamespace; 2 | 3 | namespace FactoryPattern 4 | { 5 | public class Motorbike : IVehicle 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /FactoryMethod/src/Truck.cs: -------------------------------------------------------------------------------- 1 | using DefaultNamespace; 2 | 3 | namespace FactoryPattern 4 | { 5 | public class Truck : IVehicle 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /FactoryMethod/src/UniCycle.cs: -------------------------------------------------------------------------------- 1 | using DefaultNamespace; 2 | 3 | namespace FactoryPattern 4 | { 5 | public class UniCycle : IVehicle 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /FactoryMethod/src/VehicleFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DefaultNamespace; 3 | 4 | namespace FactoryPattern 5 | { 6 | public static class VehicleFactory 7 | { 8 | public static IVehicle Build(int numberOfWheels) 9 | { 10 | switch (numberOfWheels) 11 | { 12 | case 1: 13 | return new UniCycle(); 14 | case 2: 15 | case 3: 16 | return new Motorbike(); 17 | case 4: 18 | return new Car(); 19 | default : 20 | return new Truck(); 21 | 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /FactoryMethod/src/VehicleGame/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FactoryPattern; 3 | 4 | namespace VehicleGame 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | Console.WriteLine("Enter a number of wheels between 1 and 12 to build a vehicle and press enter"); 11 | 12 | var wheels = Console.ReadLine(); 13 | var vehicle = VehicleFactory.Build(Convert.ToInt32(wheels)); 14 | Console.WriteLine($" You built a {vehicle.GetType().Name}"); 15 | Console.Read(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /FactoryMethod/src/VehicleGame/VehicleGame.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /FactoryMethod/tests/FactoryPatternTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /FactoryMethod/tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace FactoryPatternTest 5 | { 6 | public class UnitTest1 7 | { 8 | [Fact] 9 | public void Test1() 10 | { 11 | 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Mediator/README.md: -------------------------------------------------------------------------------- 1 | ## The Mediator Pattern 2 | 3 | 4 | The Mediator Pattern one of the *Behavioral Patterns* defined by the Gang of Four in [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2PdkTck ) 5 | 6 | Mediator pattern is used to reduce communication complexity between multiple objects or classes. The pattern provides a mediator class which handles all the communications between different classes and supports easy maintenance of the code by loose coupling. 7 | 8 | A mediator sits between method callers and receivers creating a configurable layer of abstraction that determines how callers and receivers get wired up. 9 | 10 | ### Benefits of the Mediator Pattern 11 | * Increases the reusability of the objects supported by the Mediator by decoupling them from the system. 12 | * Simplifies maintenance of the system by centralizing control logic 13 | * Simplifies and reduces the variety of messages sent between objects in the system. 14 | 15 | The Mediator is most commonly used to co-ordinate related GUI components. However, it is also gaining popularity amongst C# developers when developing web API's and Microservices to enable typical Request & Response messaging. 16 | 17 | A drawback of the Mediator pattern is that without a proper design the Mediator object itself can become overly complex. 18 | 19 | Fortunately, in C# Jimmy Bogard, of [Automapper](https://github.com/AutoMapper/AutoMapper) fame has also developed [MediatR - Simple mediator implementation in .NET](https://github.com/jbogard/MediatR). MediatR supports request/response, commands, queries, notifications and events, synchronous and async with intelligent dispatching via C# generic variance. 20 | 21 | 22 | I prefer to use MediatR as my implementation of choice of the Mediator Pattern, because of its ease of use and versatility. I also learned a lot about code by reading the MediatR source code. It is well worth cloning the repository and spending sometime just going through the implementation, it will provide one with a good sense of how to implement the pattern and also an appreciation of some of the finer points of the C# language. 23 | 24 | ### Why use the Mediator pattern 25 | When working with Domain Classes where multiple functions are used to modify state and implement domain rules it usually becomes difficult to debug, extend and review the implementation. Often business rule functions implement multiple sub-rules that are repeatedly required elsewhere in the domain. This may lead to Multiple layers of complex abstraction required to share functionality or multiple strategies from several developers advocating different patterns and practices. 26 | 27 | A common challenge in a codebase, is dealing with the constant flux of ever changing domain requirements and business rules. 28 | 29 | Typically software services start out as simple CRUD applications, but gradually evolve to become complex as more rules and changes are introduced. 30 | 31 | Developers may initially try to solve this problem, by implementing a *Manager* class to centralise the logic to contain every action possible to modify the state of an entity or to modify the state of another *manager* class. 32 | 33 | Testing often becomes convoluted, messy, complex and usually incomplete. Typically the problems occur when in order to test the class there are a number of dependencies to mock and configure. Just to test a new function. 34 | 35 | Typically no validation of the inbound request is implemented, and the request is allowed to pass through the middle layer, API layer and only validated within the Manager class functions. This often causes bloated action functions with complicated validation rules before the implementation logic! 36 | 37 | ### How does the Mediator pattern solve issues 38 | 39 | The mediator pattern promotes loose coupling, by implementing a mediator object to enable objects to communicate with it rather than each other. 40 | 41 | ## Example of the mediator pattern using MediatR 42 | 43 | In [Developing Api’s using Http Endpoints](https://garywoodfine.com/developing-apis-using-http-endpoints/) I discussed some of the problems relating to using MVC pattern to developing Web API projects and how to overcome by making use of the [Adralis API Endpoints](https://github.com/ardalis/ApiEndpoints) and I even created a [API Template project](https://garywoodfine.com/how-to-create-project-templates-in-net-core/) to help you get started. 44 | 45 | In this very simplified example we are going to make use of the template to create a basic project structure to illustrate how to use the Mediator Pattern, and we will also be making use of [MediatR](https://github.com/jbogard/MediatR/wiki), primarily because the template project comes pre-configured to use it. 46 | 47 | Once you have [installed the template](https://www.nuget.org/packages/threenine.ApiProject/) we can create a new project using 48 | 49 | ```c# 50 | dotnet new apiproject -n mediator 51 | ``` 52 | Once the project has been generated, we will have have all that is required done for us to provide the most simplistic example of the Mediator pattern. 53 | 54 | **The Sample project is also generated using what is known as [Vertical Slice Architecture](https://jimmybogard.com/vertical-slice-architecture/) or Feature Slices which are Structural Pattern and methodology that aims for logic separation for components and library code** 55 | 56 | It is important that the basis of the Mediator pattern, is Request & Response mediation or what is more commonly known as the [CQRS (*Command Query Responsibility Segregation*)](https://articles.geekiam.io/what-is-cqrs "What is CQRS | Geek.I.Am") . 57 | 58 | > Mediator promotes loose coupling by keeping objects from referring to each other explicitly and it lets you vary their interaction independently 59 | > 60 | > [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2PdkTck ) 61 | 62 | 63 | ![Mediator Pattern](https://garywoodfine.com/wp-content/uploads/2021/05/mediator.png) 64 | 65 | In order to speed things up even more, I am also going to use a few other products I have developed to quickly build out the other infrastructure details. Hence, I will be making use of the [ThreeNine.Data - Generic Repository](https://www.nuget.org/packages/Threenine.Data/ "Threenine.Data | Nuget") which I discussed in more detail in [Using the Repository and Unit Of Work Pattern in .net core](https://garywoodfine.com/generic-repository-pattern-net-core/) and also the [Code First Database library Nuget package](https://www.nuget.org/packages/Boleyn.Database.Postgre/) from our [Headless CMS](https://threenine.co.uk/what-is-a-headless-cms/) we are currently in the process of developing. 66 | 67 | I will not discuss the whole process of setting up the project to use these libraries because the [source code](https://github.com/garywoodfine/software-design-patterns/tree/master/Mediator) is for this example is available. For the purpose of this article we will only discuss the Mediator pattern implementation. 68 | 69 | We will implement functionality to Create a new Salutation, which is essentially a polite expression of greeting, which covers things like Mr., Mrs. , Prof. etc. Our simplistic API endpoint is simply going to enable adding new items to the list. 70 | 71 | In our application configuration in the `Startup.cs` we will first enable MediatR to essentially scan our assembly and automatically wire up all Mediator Handlers it finds ready to be used. We'll do this by simply adding the `services.AddMediatR(typeof(Startup));` in our `ConfigureServices` method. 72 | 73 | ```c# 74 | public void ConfigureServices(IServiceCollection services) 75 | { 76 | services.AddControllers(); 77 | services.AddSwaggerGen(c => 78 | { 79 | c.SwaggerDoc("v1", new OpenApiInfo {Title = "mediator", Version = "v1"}); 80 | c.EnableAnnotations(); 81 | }); 82 | services.AddAutoMapper(typeof(Startup)); 83 | services.AddMediatR(typeof(Startup)); 84 | services.AddDbContext(options => 85 | options.UseNpgsql(Configuration.GetConnectionString("mediator")) 86 | ).AddUnitOfWork(); 87 | } 88 | ``` 89 | 90 | We'll create a new Api Endpoint as `Post` , the most important details are that we simply dependency inject `IMediator` object into our class, which has been made possible because of the step we carried out earlier of instructing MediatR to scan our assembly and wire up all the handlers. 91 | 92 | You notice that we have configured our endpoint to Accept a request, which will contain a command, and that our endpoint will not provide a Response. Although it is still important to remember our Endpoint will still provide an ActionResult response, but in this instance it means that endpoint will not be providing an Object response. 93 | 94 | ```c# 95 | [Route(RouteNames.Salutations)] 96 | public class Post : BaseAsyncEndpoint.WithRequest.WithoutResponse 97 | { 98 | private readonly IMediator _mediator; 99 | 100 | public Post(IMediator mediator) 101 | { 102 | _mediator = mediator; 103 | } 104 | 105 | [HttpPost] 106 | [SwaggerOperation( 107 | Summary = "Create a new salutation", 108 | Description = "", 109 | OperationId = "AA440D51-75A5-4975-8875-C1799B58D4EB", 110 | Tags = new []{RouteNames.Salutations} 111 | )] 112 | [ProducesResponseType(StatusCodes.Status201Created)] 113 | public override async Task HandleAsync([FromBody] CreateSalutationCommand request, CancellationToken cancellationToken = new()) 114 | { 115 | var result = await _mediator.Send(request, cancellationToken); 116 | return new CreatedResult( new Uri(RouteNames.Salutations, UriKind.Relative), new { id = result }); 117 | } 118 | } 119 | ``` 120 | Our endpoint handler accepts a `CreateSalutationCommand` object, which in theory is an implementation of CQRS, We need to create our `CreateSalutationCommand` object as follows. 121 | 122 | 123 | ```c# 124 | public class CreateSalutationCommand : IRequest 125 | { 126 | public string Abbreviation { get; set; } 127 | public string FullWord { get; set; } 128 | } 129 | ``` 130 | You will notice our Command inherits from the `IRequest` essentially stipulate the Type that our object will request. This type can either a Primitive or Complex type, however in this case we are simply requesting an Integer return value containing the ID of the created object. 131 | 132 | We can now configure out Handler object, you will notice that we create a `class` which inherits from the `IRequestHandler` interface and accepts the `CreateSalutationCommand` and provides an `int` response type. 133 | 134 | We can inject whatever dependencies we may need into this class, in our case it will be the Mapper and Unit Of Work from our generic repository. 135 | 136 | ```c# 137 | public class PostHandler : IRequestHandler 138 | { 139 | private readonly IUnitOfWork _unitOfWork; 140 | private readonly IMapper _mapper; 141 | 142 | public PostHandler(IUnitOfWork unitOfWork, IMapper mapper) 143 | { 144 | _unitOfWork = unitOfWork; 145 | _mapper = mapper; 146 | } 147 | 148 | public async Task Handle(CreateSalutationCommand request, CancellationToken cancellationToken) 149 | { 150 | var salutation = _mapper.Map(request); 151 | var repo = _unitOfWork.GetRepositoryAsync(); 152 | await repo.InsertAsync(salutation, cancellationToken); 153 | await _unitOfWork.CommitAsync(); 154 | return salutation.Id; 155 | } 156 | } 157 | } 158 | ``` 159 | In this very simple example, you'll notice that we have placed our business logic all with `Handle` method, which I would be the first to admit is not great, but it also serves to illustrate that we have separated it from our Endpoint logic. 160 | 161 | In later, articles based on MediatR, I will provide further examples of how this can further be improved enabling you to take further advantage of MediatR, but for now I just want to illustrate the Mediator Pattern at work. 162 | 163 | The take away from this example, is that you will notice that at no point do we explicitly define a reference to our Handler anywhere, in our Post endpoint, we simply make use of the `Send` function available on the `IMediator` to send our request to whichever object has been defined to satisfy the Request and Response pairing. The Mediator, then ensures it sends it to which ever it has been configured too. 164 | 165 | ### Conclusion 166 | The most simplest definition of the Mediator pattern is that it is an object that encapsulates how objects interact. So it can obviously handle passing Request and Response between objects. 167 | 168 | The Mediator pattern promotes loose coupling by not having objects refer to each other, but instead refer to the mediator. So they pass the messages to the mediator, who will pass it on to the object that has been defined to handle them. 169 | 170 | The CQRS pattern defined actually just an “implementation” of the mediator pattern. As long as we are promoting loose coupling through a “mediator” class that can pass data back and forth so that the caller doesn’t need to know how things are being handled, then we can say we are implementing the Mediator Pattern. 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /Mediator/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | services: 3 | postgres: 4 | container_name: mediatr_postgre 5 | image: postgres 6 | environment: 7 | POSTGRES_USER: mediator 8 | POSTGRES_PASSWORD: password12@ 9 | PGDATA: /data/postgres 10 | volumes: 11 | - postgres:/data/postgres 12 | ports: 13 | - "5432:5432" 14 | networks: 15 | - postgres 16 | restart: unless-stopped 17 | networks: 18 | postgres: 19 | driver: bridge 20 | volumes: 21 | postgres: -------------------------------------------------------------------------------- /Mediator/mediator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.6.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mediator", "src\mediator\mediator.csproj", "{468A2BB9-1240-42E1-BFB7-2AC7D738CF54}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Debug|x64.ActiveCfg = Debug|Any CPU 24 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Debug|x64.Build.0 = Debug|Any CPU 25 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Debug|x86.ActiveCfg = Debug|Any CPU 26 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Debug|x86.Build.0 = Debug|Any CPU 27 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Release|x64.ActiveCfg = Release|Any CPU 30 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Release|x64.Build.0 = Release|Any CPU 31 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Release|x86.ActiveCfg = Release|Any CPU 32 | {468A2BB9-1240-42E1-BFB7-2AC7D738CF54}.Release|x86.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /Mediator/src/mediator/Activities/RouteNames.cs: -------------------------------------------------------------------------------- 1 | namespace mediator.Content.Activities 2 | { 3 | public static class RouteNames 4 | { 5 | public const string Salutations = "salutations"; 6 | } 7 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Activities/Salutations/Create/CreateSalutationCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace mediator.Activities.Salutations 4 | { 5 | public class CreateSalutationCommand : IRequest 6 | { 7 | public string Abbreviation { get; set; } 8 | public string FullWord { get; set; } 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Activities/Salutations/Create/Post.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Ardalis.ApiEndpoints; 5 | using mediator.Content.Activities; 6 | using MediatR; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Swashbuckle.AspNetCore.Annotations; 10 | 11 | namespace mediator.Activities.Salutations 12 | { 13 | [Route(RouteNames.Salutations)] 14 | public class Post : BaseAsyncEndpoint.WithRequest.WithoutResponse 15 | { 16 | private readonly IMediator _mediator; 17 | 18 | public Post(IMediator mediator) 19 | { 20 | _mediator = mediator; 21 | } 22 | 23 | [HttpPost] 24 | [SwaggerOperation( 25 | Summary = "Create a new salutation", 26 | Description = "", 27 | OperationId = "AA440D51-75A5-4975-8875-C1799B58D4EB", 28 | Tags = new []{RouteNames.Salutations} 29 | )] 30 | [ProducesResponseType(StatusCodes.Status201Created)] 31 | public override async Task HandleAsync([FromBody] CreateSalutationCommand request, CancellationToken cancellationToken = new()) 32 | { 33 | var result = await _mediator.Send(request, cancellationToken); 34 | return new CreatedResult( new Uri(RouteNames.Salutations, UriKind.Relative), new { id = result }); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Activities/Salutations/Create/PostHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using AutoMapper; 4 | using Boleyn.Database.Entities.Authors; 5 | using MediatR; 6 | using Threenine.Data; 7 | 8 | namespace mediator.Activities.Salutations 9 | { 10 | public class PostHandler : IRequestHandler 11 | { 12 | private readonly IUnitOfWork _unitOfWork; 13 | private readonly IMapper _mapper; 14 | 15 | public PostHandler(IUnitOfWork unitOfWork, IMapper mapper) 16 | { 17 | _unitOfWork = unitOfWork; 18 | _mapper = mapper; 19 | } 20 | 21 | public async Task Handle(CreateSalutationCommand request, CancellationToken cancellationToken) 22 | { 23 | var salutation = _mapper.Map(request); 24 | var repo = _unitOfWork.GetRepositoryAsync(); 25 | await repo.InsertAsync(salutation, cancellationToken); 26 | await _unitOfWork.CommitAsync(); 27 | return salutation.Id; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Activities/Salutations/Get/Get.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Ardalis.ApiEndpoints; 4 | using MediatR; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Swashbuckle.AspNetCore.Annotations; 8 | 9 | namespace mediator.Content.Activities.Salutations 10 | { 11 | [Route(RouteNames.Salutations)] 12 | public class Get : BaseAsyncEndpoint.WithRequest.WithResponse 13 | { 14 | private readonly IMediator _mediator; 15 | 16 | public Get(IMediator mediator) 17 | { 18 | _mediator = mediator; 19 | } 20 | 21 | [HttpGet("{id}")] 22 | [SwaggerOperation( 23 | Summary = "Retrieve Salutation details by Id ", 24 | Description = "Retrieves a Salutation response ", 25 | OperationId = "EF0A3653-153F-4E73-8D20-621C9F9FFDC9", 26 | Tags = new[] {RouteNames.Salutations}) 27 | ] 28 | [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SalutationResponse))] 29 | [Produces("application/json")] 30 | public override async Task> HandleAsync([FromRoute] GetSalutationQuery request, 31 | CancellationToken cancellationToken = default) 32 | { 33 | var response = await _mediator.Send(request, cancellationToken); 34 | return new OkObjectResult(response); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Activities/Salutations/Get/GetSalutationHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using MediatR; 5 | 6 | namespace mediator.Content.Activities.Salutations 7 | { 8 | public class GetSalutationHandler : IRequestHandler 9 | { 10 | public async Task Handle(GetSalutationQuery getSalutationQuery, CancellationToken cancellationToken) 11 | { 12 | /// Your Logic Goes here 13 | // This is only to supply an example and you should do whatever you need to achieve here 14 | return await Task.FromResult(new SalutationResponse 15 | { 16 | Id = Guid.NewGuid().ToString(), 17 | Name = nameof(SalutationResponse) 18 | }); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Activities/Salutations/Get/GetSalutationQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace mediator.Content.Activities.Salutations 5 | { 6 | public class GetSalutationQuery : IRequest 7 | { 8 | [FromRoute(Name = "id")] public string Id { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Activities/Salutations/Get/SalutationResponse.cs: -------------------------------------------------------------------------------- 1 | namespace mediator.Content.Activities.Salutations 2 | { 3 | public class SalutationResponse 4 | { 5 | public string Id { get; set; } 6 | public string Name { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/MappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Boleyn.Database.Entities.Authors; 3 | using mediator.Activities.Salutations; 4 | 5 | namespace mediator 6 | { 7 | public class MappingProfile : Profile 8 | { 9 | public MappingProfile() 10 | { 11 | CreateMap() 12 | .ForMember(dest => dest.Abbreviation, opt => opt.MapFrom(src => src.Abbreviation)) 13 | .ForMember(dest => dest.FullWord, opt => opt.MapFrom(src => src.FullWord)); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Hosting; 4 | using Serilog; 5 | using Serilog.Events; 6 | 7 | namespace mediator.Content 8 | { 9 | public class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | Log.Logger = new LoggerConfiguration() 14 | .MinimumLevel.Override("Microsoft", LogEventLevel.Information) 15 | .Enrich.FromLogContext() 16 | .WriteTo.Console() 17 | .CreateBootstrapLogger(); 18 | try 19 | { 20 | Log.Information("mediator Application is starting"); 21 | CreateHostBuilder(args).Build().Run(); 22 | } 23 | catch (Exception ex) 24 | { 25 | Log.Fatal(ex, "mediator application failed to start"); 26 | } 27 | finally 28 | { 29 | Log.CloseAndFlush(); 30 | } 31 | } 32 | 33 | public static IHostBuilder CreateHostBuilder(string[] args) => 34 | Host.CreateDefaultBuilder(args) 35 | .UseSerilog((hostContext, LoggerConfiguration) => 36 | { 37 | LoggerConfiguration.ReadFrom.Configuration(hostContext.Configuration); 38 | }) 39 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); 40 | } 41 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/Startup.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using Boleyn.Database.Postgre; 3 | using MediatR; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.EntityFrameworkCore; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.OpenApi.Models; 11 | using Serilog; 12 | using Threenine.Data.DependencyInjection; 13 | 14 | namespace mediator.Content 15 | { 16 | public class Startup 17 | { 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | } 22 | 23 | public IConfiguration Configuration { get; } 24 | 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | public void ConfigureServices(IServiceCollection services) 27 | { 28 | services.AddControllers(); 29 | services.AddSwaggerGen(c => 30 | { 31 | c.SwaggerDoc("v1", new OpenApiInfo {Title = "mediator", Version = "v1"}); 32 | c.EnableAnnotations(); 33 | }); 34 | services.AddAutoMapper(typeof(Startup)); 35 | services.AddMediatR(typeof(Startup)); 36 | services.AddDbContext(options => 37 | options.UseNpgsql(Configuration.GetConnectionString("mediator")) 38 | ).AddUnitOfWork(); 39 | } 40 | 41 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 42 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 43 | { 44 | app.UseSerilogRequestLogging(); 45 | using (var serviceScope = app.ApplicationServices.GetRequiredService().CreateScope()) 46 | { 47 | var context = serviceScope.ServiceProvider.GetService(); 48 | context.Database.Migrate(); 49 | } 50 | if (env.IsDevelopment()) 51 | { 52 | app.UseDeveloperExceptionPage(); 53 | app.UseSwagger(); 54 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "mediator v1")); 55 | } 56 | 57 | app.UseHttpsRedirection(); 58 | 59 | app.UseRouting(); 60 | 61 | app.UseAuthorization(); 62 | 63 | app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Mediator/src/mediator/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": "Information", 4 | "Override": { 5 | "Microsoft.AspNetCore": "Warning" 6 | }, 7 | "WriteTo": [ 8 | { 9 | "Name": "Console", 10 | "Args": { 11 | "outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] [{SourceContext}] {Application} {Message}{NewLine}{Exception}" 12 | } 13 | } 14 | ], 15 | "Enrich": [ 16 | "FromLogContext", 17 | "WithMachineName", 18 | "WithProcessId", 19 | "WithThreadId" 20 | ], 21 | "Properties": { 22 | "Application" : "mediator" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Mediator/src/mediator/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "mediator": "User ID=mediator;Password=password12@;Host=localhost;Port=5432;Database=mediator;Pooling=true;Integrated Security=true;" 4 | }, 5 | "AllowedHosts": "*" 6 | } 7 | -------------------------------------------------------------------------------- /Mediator/src/mediator/mediator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Template 4 | Threenine.mediator 5 | net5.0 6 | true 7 | API Project template 8 | Gary Woodfine 9 | Project template to create Adralis API endpoint project 10 | git 11 | api project, api endpoints, 12 | https://github.com/threenine/api-template 13 | Copyright © 2005 - 2021 Three Nine Consulting Limited 14 | https://threenine.co.uk 15 | https://github.com/threenine/api-template/blob/master/LICENSE 16 | http://static.threenine.co.uk/threenine_icon.png 17 | true 18 | false 19 | false 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Prototype/README.md: -------------------------------------------------------------------------------- 1 | # The Protoype Pattern C# .net core 2 | 3 | 4 | The Prototype Design Pattern is one of the _Creational Design Patterns_ defined by the _Gang Of Four (GOF)_ published their book [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2PwkRfA) in which they presented a catalog of simple and succinct solutions to commonly occurring design problems. 5 | 6 | The Prototype pattern is specifically used when creating a duplicate object of an existing object while attempting to conserve resources and focus on performance. 7 | 8 | For example, consider a real-world example of a Software Developer. In order to create or instantiate a new instance of Software Developer, we would in the first instance create a new Human Object, Nurture and Parent the Human object while educating it. Ensuring that the Human Object gets all the right attributes and elements which eventually lead to it becoming a Software Developer. 9 | 10 | Creating a Software Developer object, is a very resource intensive and time consuming process. What happens you need another 10 of these objects ? Wouldn't it be great if we could just clone our existing copy of the software developer, which already has all the attributes and properties we need instantly? 11 | 12 | This is the type pf scenario that Prototype Pattern serves to address. The pattern provides a prototype interface which enables creating a clone of the current object. This pattern is used when creation of object directly is costly. For example, an object is to be created after a costly database operation. We can cache the object, returns its clone on next request and update the database as and when needed thus reducing database calls. 13 | 14 | ## Prototype Pattern Implementation in C# 15 | A typical implementation of the Prototype pattern could modelled as follows 16 | 17 | ![Prototype pattern](https://garywoodfine.com/wp-content/uploads/2019/10/Prototype-2.png) 18 | 19 | A class may implement an ICloneable Interface which requires the class to implement a Clone method. 20 | 21 | The .net framework provides developers with the [ICloneable Interface](https://docs.microsoft.com/en-us/dotnet/api/system.icloneable?view=netcore-3.0) 22 | 23 | >The ICloneable interface enables you to provide a customized implementation that creates a copy of an existing object. The ICloneable interface contains one member, the Clone method, which is intended to provide cloning support beyond that supplied by Object.MemberwiseClone. 24 | > 25 | 26 | The ICloneable interface simply requires that your implementation of the `Clone()` method return a copy of the current object instance. 27 | 28 | The developer is free to choose any implementation to perform the clone operation i.e. Deep, Shallow or Custom copy of the object. 29 | 30 | ## Practical Implementation of Prototype Pattern in C# 31 | 32 | In the first simple implementation of the Prototype Pattern, we'll create a Developer class that implements `ICloneable` interface which preforms a [MemberwiseClone](https://docs.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netcore-3.0) operation. 33 | 34 | The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object. 35 | 36 | ```c# 37 | 38 | public class Developer : ICloneable 39 | { 40 | public string FirstName { get; set; } 41 | public string Lastname { get; set; } 42 | public string[] Skills { get; set; } 43 | 44 | public object Clone() 45 | { 46 | return this.MemberwiseClone(); 47 | } 48 | } 49 | 50 | ``` 51 | 52 | We can now implement our basic client App to instantiate an initial Developer object then we will create a Second version of the object making use of the Clone method that is available. We will then just inspect the cloned developer to check the FirstName it has been defined. 53 | 54 | ```c# 55 | class Program 56 | { 57 | static void Main(string[] args) 58 | { 59 | 60 | var dev = new Developer 61 | { 62 | FirstName = "Gary", 63 | Lastname = "Woodfine", 64 | Skills = new List {"C#", "PHP", "SQL", "JavaScript"} 65 | }; 66 | 67 | 68 | var dev2 = dev.Clone() as Developer; 69 | 70 | Console.WriteLine($"The Cloned Developer name is { dev2.FirstName } { dev2.Lastname }"); 71 | 72 | Console.WriteLine("The second developer has the following skills: "); 73 | 74 | 75 | foreach (var skill in dev2.Skills) 76 | { 77 | Console.WriteLine(skill); 78 | } 79 | } 80 | } 81 | ``` 82 | If you run the application you'll notice that the Second instance of the Developer has all the same properties of the first one instantiated. 83 | 84 | 85 | ## Applications for the Prototype Pattern in C# 86 | 87 | The Prototype Pattern is useful when you need to be able to quickly create new instances of objects based on other objects. 88 | 89 | The following are typical application scenarios where you may want to consider using the Prototype pattern in C# 90 | 91 | 92 | * You want to instantiate classes at run time, for example, by dynamic loading. 93 | * Avoid building a class hierarchy of factories that parallels the class hierarchy of products. 94 | * When new instantiations of class can have one of only a few different combinations of state. 95 | * New object are going to be clones of existing object. 96 | * Avoid subclasses of an object creator in the client application i.e. [Abstract Factory Pattern](https://garywoodfine.com/abstract-factory-design-pattern/). 97 | * Avoid resource intensive object instantiation and initialisation logic. 98 | 99 | ## Advantages of the Prototype Design Pattern 100 | 101 | * Reduce the time complexity to creating resource consuming objects by using the prototype pattern. 102 | * Reduces the sub-classing. 103 | * Enables adding and removing objects at run time. 104 | * Enables configuring application classes dynamically. 105 | 106 | The Prototype Pattern is another tool you can use when you can specify the general class needed in program but need to defer the exact class until execution time. It is similar to the [Builder Pattern](https://garywoodfine.com/the-builder-pattern-net-core/) in that some class or method decides what components or details make up the final instantiated class. However, it differs in that the target classes are constructed by cloning one or more classes and then changing or filling in the details of the cloned class to behave as desired. 107 | 108 | Prototypes can be used whenever you need classes that differ only in the type of processing they offer. 109 | 110 | Any change you make in one clone of the object is immediately reflected in the other because in fact there is only one object. 111 | 112 | To see this behaviour lets add some additional logic to our code, to add an additional skill to our Second Developer instance. After that lets print out our skills of our initial instance to the console. 113 | 114 | ```c# 115 | class Program 116 | { 117 | static void Main(string[] args) 118 | { 119 | 120 | var dev = new Developer 121 | { 122 | FirstName = "Gary", 123 | Lastname = "Woodfine", 124 | Skills = new List{"C#", "PHP", "SQL", "JavaScript"} 125 | }; 126 | 127 | 128 | var dev2 = dev.Clone() as Developer; 129 | 130 | Console.WriteLine($"The Cloned Developer name is { dev2.FirstName } { dev2.Lastname }"); 131 | 132 | Console.WriteLine("The second developer has the following skills: "); 133 | 134 | 135 | foreach (var skill in dev2.Skills) 136 | { 137 | Console.WriteLine(skill); 138 | } 139 | 140 | // Add a new Skill to our Cloned Instance 141 | dev2.Skills.Add( "VueJs"); 142 | 143 | Console.WriteLine(" "); 144 | 145 | 146 | Console.WriteLine("Our Initial Developer object now has VueJS added too"); 147 | foreach (var skill in dev.Skills) 148 | { 149 | Console.WriteLine(skill); 150 | } 151 | 152 | } 153 | ``` 154 | If we run our sample we'll see that the `Vue.JS` has been added to our Developer instance skill set. 155 | 156 | ```text 157 | The Cloned Developer name is Gary Woodfine 158 | The second developer has the following skills: 159 | C# 160 | PHP 161 | SQL 162 | JavaScript 163 | 164 | Our Initial Developer object now has VueJS added too 165 | C# 166 | PHP 167 | SQL 168 | JavaScript 169 | VueJs 170 | 171 | ``` 172 | ### Summary 173 | 174 | The prototype pattern copies or clones an existing class, rather than creating a new instance, when creating new instances is more expensive. 175 | 176 | ## Sponsored by 177 | [![threenine logo](http://static.threenine.co.uk/img/github_footer.png)](https://threenine.co.uk/) 178 | 179 | 180 | -------------------------------------------------------------------------------- /Prototype/prototype.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{82D01E8F-62EB-4163-839F-42F2B33CE874}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Threenine.Employee", "src\Threenine.Employee\Threenine.Employee.csproj", "{16B15205-F290-44DC-9D5A-2EE9BD713F62}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Threenine.Client", "src\Threenine.Client\Threenine.Client.csproj", "{0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Debug|x64.ActiveCfg = Debug|Any CPU 28 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Debug|x64.Build.0 = Debug|Any CPU 29 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Debug|x86.ActiveCfg = Debug|Any CPU 30 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Debug|x86.Build.0 = Debug|Any CPU 31 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Release|x64.ActiveCfg = Release|Any CPU 34 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Release|x64.Build.0 = Release|Any CPU 35 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Release|x86.ActiveCfg = Release|Any CPU 36 | {16B15205-F290-44DC-9D5A-2EE9BD713F62}.Release|x86.Build.0 = Release|Any CPU 37 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Debug|x64.ActiveCfg = Debug|Any CPU 40 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Debug|x64.Build.0 = Debug|Any CPU 41 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Debug|x86.ActiveCfg = Debug|Any CPU 42 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Debug|x86.Build.0 = Debug|Any CPU 43 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Release|x64.ActiveCfg = Release|Any CPU 46 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Release|x64.Build.0 = Release|Any CPU 47 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Release|x86.ActiveCfg = Release|Any CPU 48 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7}.Release|x86.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(NestedProjects) = preSolution 51 | {16B15205-F290-44DC-9D5A-2EE9BD713F62} = {82D01E8F-62EB-4163-839F-42F2B33CE874} 52 | {0EE0A37E-67F6-42E4-B0C0-C2FAEC916AA7} = {82D01E8F-62EB-4163-839F-42F2B33CE874} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /Prototype/src/Threenine.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Threenine.Employee; 4 | 5 | namespace Threenine.Client 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | 12 | var dev = new Developer 13 | { 14 | FirstName = "Gary", 15 | Lastname = "Woodfine", 16 | Skills = new List{"C#", "PHP", "SQL", "JavaScript"} 17 | }; 18 | 19 | 20 | var dev2 = dev.Clone() as Developer; 21 | 22 | Console.WriteLine($"The Cloned Developer name is { dev2.FirstName } { dev2.Lastname }"); 23 | 24 | Console.WriteLine("The second developer has the following skills: "); 25 | 26 | 27 | foreach (var skill in dev2.Skills) 28 | { 29 | Console.WriteLine(skill); 30 | } 31 | 32 | // Add a new Skill to our Cloned Instance 33 | dev2.Skills.Add( "VueJs"); 34 | 35 | Console.WriteLine(" "); 36 | 37 | 38 | Console.WriteLine("Our Initial Developer object now has VueJS added too"); 39 | foreach (var skill in dev.Skills) 40 | { 41 | Console.WriteLine(skill); 42 | } 43 | 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Prototype/src/Threenine.Client/Threenine.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Prototype/src/Threenine.Employee/Developer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Threenine.Employee 5 | { 6 | public class Developer : ICloneable 7 | { 8 | public string FirstName { get; set; } 9 | public string Lastname { get; set; } 10 | public List Skills { get; set; } 11 | 12 | public object Clone() 13 | { 14 | return this.MemberwiseClone(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Prototype/src/Threenine.Employee/Threenine.Employee.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Prototype/src/Threenine.Employee/Typist.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Threenine.Employee 4 | { 5 | public class Typist : ICloneable 6 | { 7 | public object Clone() 8 | { 9 | throw new NotImplementedException(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Prototype/src/interface/IClonable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Threenine.Interface 4 | { 5 | public interface ICloneable 6 | { 7 | IClonable Clone(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Prototype/src/interface/Threenine.Interface.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | Threenine.Interface.IClonable 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Software Design Patterns in C# and .net core 2 | 3 | 4 | In software development a Software Design Pattern is a reusable solution to commonly recurring problems. A software design pattern is a description or template used to solve a problem that can be used in many different situations. 5 | 6 | In 1994, the so called Gang Of Four (GOF) published their book [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2Nx1Iq6) in which they presented a catalog of simple and succinct solutions to commonly occurring design problems. 7 | 8 | The book captured 23 Patterns that enabled software architects to create flexible, elegant and ultimately reusable design patterns without having to rediscover or reinvent the design solutions for themselves. 9 | 10 | Through a series of blog posts on [garywoodfine.com](https://garywoodfine.com) I will discuss these patterns and more on how C# .net core developers can implement these patterns in cross platform .net core software solutions. 11 | 12 | ### [Software Design Patterns](https://garywoodfine.com/software-design-patterns/) 13 | 14 | # Contents 15 | 16 | Software Design patterns are typically categorised into three groups. 17 | 18 | -------------------------------------------------------------------------------- 19 | 20 | |Creational Patterns | | 21 | | ----------| ---------------------------------- | 22 | | [Simple Factory](https://garywoodfine.com/simple-factory-pattern/) |interfaces for creating objects without exposing the object creation logic | 23 | | [Factory Method](https://garywoodfine.com/factory-method-design-pattern/) | Creates an instance of several derived classes | 24 | | [Abstract Factory](https://garywoodfine.com/abstract-factory-design-pattern/) |Creates an instance of several families of classes | 25 | | [Builder](https://garywoodfine.com/the-builder-pattern-net-core/) |Separates object construction from its representation | 26 | | [Prototype](https://garywoodfine.com/the-prototype-design-pattern-c-net-core/) |A fully initialized instance to be copied or cloned | 27 | | [Singleton](https://garywoodfine.com/singleton-design-pattern-c-net-core/) | A class of which only a single instance can exist | 28 | --------------------------------------------------------------------------------- 29 | 30 | 31 | |Structural Patterns | | 32 | | ----------| ---------------------------------- | 33 | | [Adapter](https://garywoodfine.com/the-adapter-pattern) | Match interfaces of different classes | 34 | | Bridge | Separates an object’s interface from its implementation | 35 | | Composite | A tree structure of simple and composite objects | 36 | | Decorator | Add responsibilities to objects dynamically | 37 | | Facade | A single class that represents an entire subsystem | 38 | | Flyweight | A fine-grained instance used for efficient sharing | 39 | | Proxy | An object representing another object | 40 | 41 | ------------------------------------------------------------------------------------ 42 | 43 | 44 | | Behavioral Patterns | | 45 | |------------------------------------------------------------| ---------------------------------- | 46 | | Chain of Responsibility | A way of passing a request between a chain of objects| 47 | | Command | Encapsulate a command request as an object| 48 | | Interpreter | A way to include language elements in a program| 49 | | Iterator | Sequentially access the elements of a collection| 50 | | [Mediator](https://garywoodfine.com/the-mediator-pattern/) | Defines simplified communication between classes| 51 | | Memento | Capture and restore an object's internal state| 52 | | Observer | A way of notifying change to a number of classes| 53 | | State | Alter an object's behavior when its state changes| 54 | | [Strategy](https://garywoodfine.com/how-to-use-the-strategy-pattern-in-c/) | Encapsulates an algorithm inside a class| 55 | | Template Method | Defer the exact steps of an algorithm to a subclass| 56 | | Visitor | Defines a new operation to a class without change| 57 | 58 | ----------------------------------------------------------------------------------- 59 | ## Sponsored by 60 | [![threenine logo](http://static.threenine.co.uk/img/github_footer.png)](https://threenine.co.uk/) 61 | -------------------------------------------------------------------------------- /SimpleFactory/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Simple Factory Pattern 3 | 4 | The post [Simple Factory Pattern](https://garywoodfine.com/simple-factory-pattern/) appeared first on [Gary Woodfine](https://garywoodfine.com) and is also available on [Dev.to](https://dev.to/gary_woodfine/simple-factory-pattern-in-c-and-net-core-3263). 5 | 6 | In software development, a Software Design Pattern is a reusable solution to commonly recurring problems. A software design pattern is a description or template used to solve a problem that can be used in many different situations. 7 | 8 | In 1994, the so-called Gang Of Four (GOF) published their book,[ Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2N22a2H), in which they presented a catalogue of simple and succinct solutions to commonly occurring design problems. 9 | 10 | The book captured 23 Patterns that enabled software architects to create flexible, elegant and ultimately reusable design patterns without having to rediscover or reinvent the design solutions for themselves. It's a great resource, for those developers who want to learn more about common software design patterns and how to implement them. However, the book doesn't cover all software design patterns, and it definitely doesn't cover some of the most frequently found software patterns. 11 | 12 | The Simple Factory Pattern is probably one of the most widely used patterns and at the same time, it is also one of the most underused software patterns. I frequently come across scenarios in code bases, when developers have encountered a problem and instead of elegantly handling it, they often pollute function methods with additional lines of cruft logic. Which can often be the source of additional logic bugs or lead to scalability and adaptability issues later. 13 | 14 | Simple Factory Pattern is a Factory class in its simplest form, compared to Factory Method Pattern or Abstract Factory Pattern, is a factory object for creating other objects. In simplest terms Factory helps to keep all object creation in one place and avoid of spreading new key value across the codebase. 15 | 16 | The classes a Simple Factory Pattern returns will have the same parent class and methods but will perform the task differently dependent on the type of data supplied. 17 | 18 | ## Simple Factory Pattern Implementation 19 | 20 | Let's take a look at the diagram to get a high-level view of how the Simple Factory Pattern works in C# and .net core. The simple factory design pattern is a form of abstraction, which hides the actual logic of an implementation of an object so the initialization code can focus on usage, rather than the inner workings. 21 | 22 | ![Simple Factory Pattern](https://garywoodfine.com/wp-content/uploads/2018/08/SimpleFactoryPattern.jpg "Simple Factory Pattern") 23 | 24 | In the diagram `Manufacture` is a base class, and classes `Chocolate` and `MotorVehicle` are derived from it, the `Manufacture` class decides which of the subclasses to return, depending on the arguments provided. 25 | 26 | The developer using the `Manufacture` class doesn't really care which of the subclasses is returned, because each of them have the same methods but will have different implementations. How the factory class decides which one to return, is entirely up to what data is supplied. It could be very complex logic, however, most often it is very simple logic. 27 | 28 | ### Code Sample 29 | 30 | To build a Hypothetical sample of a Simple Factory Pattern, consider a typical Firstname and Lastname scenario. If we consider we're going to build an application that will take a username string input from various applications. However, there is some inconsistency in how the string is supplied. In one application it is passed through as `"FirstName LastName"` and the other application it is passed through as `"LastName, FirstName"`. 31 | 32 | We wouldn't want to pollute our code base with different string manipulation strategies when we could make use of different classes to handle this scenario based on the input received. 33 | 34 | Deciding which version of the name to display can be a simple decision making use of the simple `If then` statement. 35 | 36 | We'll start by defining a simple class that takes the username as string argument in the constructor and allows you to fetch the split names back. 37 | 38 | First, we'll define a simple base class for the Username, consisting of the basic properties we need. 39 | 40 | ```csharp 41 | public class UserName 42 | { 43 | public string FirstName { get; set; } 44 | public string LastName { get; set; } 45 | } 46 | ``` 47 | #### Derived Classes 48 | 49 | We can develop two very simple derived classes that implement the abstract class and split the username into two parts in the constructor. In this example we'll base the class on the assumption that the username is separated by a space, when we need to use the `FirstName` first scenario 50 | 51 | ```c# 52 | 53 | public class FirstNameFirst : UserName 54 | { 55 | public FirstNameFirst(string username) 56 | { 57 | var index = username.Trim().IndexOf(" ", StringComparison.Ordinal); 58 | 59 | if (index <= 0) return; 60 | 61 | FirstName = username.Substring(0,index).Trim(); 62 | LastName = username.Substring(index + 1).Trim(); 63 | } 64 | } 65 | 66 | ``` 67 | 68 | In the second class, we'll base it on the assumption that the name is split by a comma. 69 | 70 | ```c# 71 | 72 | public class LastNameFirst : UserName 73 | { 74 | public LastNameFirst(string username) 75 | { 76 | var index = username.Trim().IndexOf(",", StringComparison.Ordinal); 77 | 78 | if (index <= 0) return; 79 | 80 | LastName = username.Substring(0,index).Trim(); 81 | FirstName = username.Substring(index + 1).Trim(); 82 | } 83 | } 84 | 85 | ``` 86 | 87 | ### Build a Simple Factory Pattern 88 | 89 | We'll build a Simple Factory which simply tests for the existence of comma and then return an instance of one class or the other. 90 | 91 | ```c# 92 | public class UsernameFactory 93 | { 94 | public UserName GetUserName(string name) 95 | { 96 | if (name.Contains(",")) return new LastNameFirst(name); 97 | 98 | return new FirstNameFirst(name); 99 | } 100 | } 101 | ``` 102 | ![Simple Factory Pattern](https://garywoodfine.com/wp-content/uploads/2018/08/UserNameFactory.jpg "Simple Factory Pattern") 103 | 104 | 105 | ### Unit Test Simple Factory Pattern 106 | 107 | We can now simply test our factory pattern using xUnit unit testing framework as follows. 108 | 109 | ```c# 110 | public class UsernameFactoryTests 111 | { 112 | private UsernameFactory _factory; 113 | 114 | public UsernameFactoryTests() 115 | { 116 | _factory = new UsernameFactory(); 117 | } 118 | 119 | [Fact] 120 | public void ShouldGetFirstNameFirst() 121 | { 122 | //arrange 123 | var user = "Gary Woodfine"; 124 | 125 | //act 126 | var username = _factory.GetUserName(user); 127 | 128 | 129 | //assert 130 | Assert.Equal("Gary", username.FirstName); 131 | Assert.Equal("Woodfine", username.LastName); 132 | 133 | } 134 | 135 | [Fact] 136 | public void ShouldGetLastNameFirst() 137 | { 138 | //arrange 139 | var user = "Woodfine, Gary"; 140 | 141 | //act 142 | var username = _factory.GetUserName(user); 143 | 144 | 145 | //assert 146 | Assert.Equal("Gary", username.FirstName); 147 | Assert.Equal("Woodfine", username.LastName); 148 | 149 | } 150 | } 151 | ``` 152 | 153 | As you can see now when we make a call to `GetUserName` function we simply pass in the string and the factory method decides which version of the UserName instance to pass back to the application. The developer doesn't need to concern themselves with any of the string manipulation required. 154 | 155 | The `UserNameFactory` simple factory design pattern is a form of abstraction, which hides the actual logic of an implementation of an object so the initialization code can focus on usage, rather than the inner workings. provides us the detail we need, all we need to do is pass the name string in and the factory will determine which class to use to format the data. 156 | 157 | ### Summary 158 | 159 | That is the fundamental principle of the Simple Factory Pattern, to create an abstraction that decides which of the several possible classes to return. A developer simply calls a method of the class without knowing the implementation detail or which subclass it actually uses to implement the logic. 160 | 161 | This approach helps to keep issues of data dependence separated from the classes' useful methods. 162 | 163 | The post [Simple Factory Pattern](https://garywoodfine.com/simple-factory-pattern/) appeared first on [Gary Woodfine](https://garywoodfine.com) and is also available on [Dev.to](https://dev.to/gary_woodfine/simple-factory-pattern-in-c-and-net-core-3263).. 164 | 165 | ## Sponsored by 166 | [![threenine logo](http://static.threenine.co.uk/img/github_footer.png)](https://threenine.co.uk/) -------------------------------------------------------------------------------- /SimpleFactory/simple-factory.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFactory", "src\SimpleFactory.csproj", "{4908008E-0718-4036-8313-2EB9022FF936}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleFactoryTest", "tests\SimpleFactoryTest.csproj", "{319CDC4E-2535-43A5-919A-FCD3AA47970A}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {4908008E-0718-4036-8313-2EB9022FF936}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {4908008E-0718-4036-8313-2EB9022FF936}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {4908008E-0718-4036-8313-2EB9022FF936}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {4908008E-0718-4036-8313-2EB9022FF936}.Debug|x64.Build.0 = Debug|Any CPU 27 | {4908008E-0718-4036-8313-2EB9022FF936}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {4908008E-0718-4036-8313-2EB9022FF936}.Debug|x86.Build.0 = Debug|Any CPU 29 | {4908008E-0718-4036-8313-2EB9022FF936}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {4908008E-0718-4036-8313-2EB9022FF936}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {4908008E-0718-4036-8313-2EB9022FF936}.Release|x64.ActiveCfg = Release|Any CPU 32 | {4908008E-0718-4036-8313-2EB9022FF936}.Release|x64.Build.0 = Release|Any CPU 33 | {4908008E-0718-4036-8313-2EB9022FF936}.Release|x86.ActiveCfg = Release|Any CPU 34 | {4908008E-0718-4036-8313-2EB9022FF936}.Release|x86.Build.0 = Release|Any CPU 35 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Debug|x64.ActiveCfg = Debug|Any CPU 38 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Debug|x64.Build.0 = Debug|Any CPU 39 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Debug|x86.ActiveCfg = Debug|Any CPU 40 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Debug|x86.Build.0 = Debug|Any CPU 41 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Release|x64.ActiveCfg = Release|Any CPU 44 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Release|x64.Build.0 = Release|Any CPU 45 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Release|x86.ActiveCfg = Release|Any CPU 46 | {319CDC4E-2535-43A5-919A-FCD3AA47970A}.Release|x86.Build.0 = Release|Any CPU 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /SimpleFactory/src/FirstNameFirst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFactory 4 | { 5 | public class FirstNameFirst : UserName 6 | { 7 | public FirstNameFirst(string username) 8 | { 9 | var index = username.Trim().IndexOf(" ", StringComparison.Ordinal); 10 | 11 | if (index <= 0) return; 12 | 13 | FirstName = username.Substring(0,index).Trim(); 14 | LastName = username.Substring(index + 1).Trim(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /SimpleFactory/src/LastNameFirst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFactory 4 | { 5 | public class LastNameFirst : UserName 6 | { 7 | public LastNameFirst(string username) 8 | { 9 | var index = username.Trim().IndexOf(",", StringComparison.Ordinal); 10 | 11 | if (index <= 0) return; 12 | 13 | LastName = username.Substring(0,index).Trim(); 14 | FirstName = username.Substring(index + 1).Trim(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /SimpleFactory/src/SimpleFactory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SimpleFactory/src/UserName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFactory 4 | { 5 | public class UserName 6 | { 7 | 8 | 9 | public string FirstName { get; set; } 10 | public string LastName { get; set; } 11 | 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /SimpleFactory/src/UsernameFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFactory 4 | { 5 | public class UsernameFactory 6 | { 7 | public UsernameFactory () 8 | { 9 | 10 | } 11 | 12 | public UserName GetUserName(string name) 13 | { 14 | if(name.Contains(",")) 15 | { 16 | return new LastNameFirst(name); 17 | 18 | } 19 | else 20 | { 21 | return new FirstNameFirst(name); 22 | 23 | } 24 | 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /SimpleFactory/tests/FirstNameFirstTest.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using Xunit; 4 | using SimpleFactory; 5 | 6 | namespace SimpleFactoryTest 7 | { 8 | public class FirstNameFirstTest 9 | { 10 | 11 | [Fact] 12 | public void ShouldSplitStringOnSpace() 13 | { 14 | var theName = "Gary Woodfine"; 15 | 16 | var splitname = new FirstNameFirst(theName); 17 | 18 | Assert.Equal("Gary", splitname.FirstName); 19 | Assert.Equal("Woodfine", splitname.LastName); 20 | 21 | } 22 | 23 | [Fact] 24 | public void FailSplitOnStringOnComma() 25 | { 26 | var theName = "Gary,Woodfine"; 27 | 28 | var splitname = new FirstNameFirst(theName); 29 | 30 | Assert.Null(splitname.FirstName); 31 | Assert.Null(splitname.LastName); 32 | 33 | } 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /SimpleFactory/tests/SimpleFactoryTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using SimpleFactory; 4 | 5 | 6 | namespace SimpleFactoryTest 7 | { 8 | public class UsernameFactoryTests 9 | { 10 | private UsernameFactory _factory; 11 | 12 | public UsernameFactoryTests() 13 | { 14 | _factory = new UsernameFactory(); 15 | } 16 | 17 | [Fact] 18 | public void ShouldGetFirstNameFirst() 19 | { 20 | //arrange 21 | var user = "Gary Woodfine"; 22 | 23 | //act 24 | var username = _factory.GetUserName(user); 25 | 26 | 27 | //assert 28 | Assert.Equal("Gary", username.FirstName); 29 | Assert.Equal("Woodfine", username.LastName); 30 | 31 | } 32 | 33 | [Fact] 34 | public void ShouldGetLastNameFirst() 35 | { 36 | //arrange 37 | var user = "Woodfine, Gary"; 38 | 39 | //act 40 | var username = _factory.GetUserName(user); 41 | 42 | 43 | //assert 44 | Assert.Equal("Gary", username.FirstName); 45 | Assert.Equal("Woodfine", username.LastName); 46 | 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SimpleFactory/tests/SimpleFactoryTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Singleton/README.md: -------------------------------------------------------------------------------- 1 | # Singleton Pattern 2 | 3 | The Singleton pattern is grouped by the Gang Of Four in [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2PdkTck) as a Creational Pattern, although to some extent it is a pattern that limits, rather than promotes, the creation of classes. 4 | 5 | The primary objective of the Singleton Pattern, is to ensure that there is __one and only one__ instance of a class and provides a global access point to it. 6 | 7 | There are a number of instances in software development where one will need to ensure that there is only one instance of a class. One such example, which may be typical for enterprise software developers is to ensure there is a single point of access to a database engine. 8 | 9 | The Singleton Pattern creates a single class responsible to create an object ensuring that only a single object gets created. This class provides a way to access its only object which can be accessed directly without the need to instantiate the object of the class. 10 | 11 | > The singleton pattern is a design pattern that restricts the instantiation of a class to one object. 12 | 13 | The Singleton Pattern does not allow any parameters to be specified when creating the instance - as otherwise a second request for an instance but with a different parameter could be problematic! 14 | 15 | If the same instance should be accessed for all requests with the same parameter, the [Abstract factory pattern](https://garywoodfine.com/abstract-factory-design-pattern/) is more appropriate. 16 | 17 | ### Creational Design Patterns 18 | * [Simple Factory Pattern ](https://garywoodfine.com/simple-factory-pattern/) 19 | * [Factory Method Pattern](https://garywoodfine.com/factory-method-design-pattern/) 20 | * [Abstract Factory Pattern](https://garywoodfine.com/abstract-factory-design-pattern/) 21 | * [Builder Pattern](https://garywoodfine.com/the-builder-pattern-net-core/) 22 | * [Prototype Pattern](https://garywoodfine.com/the-prototype-design-pattern-c-net-core/) 23 | 24 | 25 | ## Implementing the Singleton Pattern 26 | 27 | There are a number of different ways of implementing the Singleton Pattern in C#, each suited to different situations and requirements, which include Simple, Thread Safe, Lazy Loading and High Performance. 28 | 29 | ### Common Characteristics of Singleton Pattern Implementations. 30 | 31 | 1. Single Constructor - Private and Parameter-less. 32 | 2. Sealed Class i.e. Cannot be inherited 33 | 3. Static variable references the single created instance 34 | 4. Public static access to the single created instance 35 | 36 | ### Singleton Pattern Implementation 37 | 38 | In our fictional implementation of the Singleton Pattern we will be using it to create Print Spooler class. 39 | 40 | The print Spooler is a software service that manages the printing process. The Spooler accepts print jobs from the computer and makes sure that printer resources are available. It also schedules the order in which jobs are sent to the print queue for printing. 41 | 42 | In the early days of personal computers, you had to wait until a document printed before you could do anything else. Thanks to modern print spoolers, the printing process has minimal impact on user productivity. 43 | 44 | ### Processes and Threads 45 | 46 | In order to understand the singleton pattern, it is important to understand the context in which it will operate. In the .Net Framework, an application will be composed of lightweight, managed sub-processes called application domains that can comprise one or more managed threads. 47 | For the purpose of understanding the singleton pattern, let's define this as a multi-threaded application that contains one or more threads running simultaneously. 48 | 49 | Technically, the threads are actually not running simultaneously, but this is achieved by dividing the available processor time between the threads, so that each thread will execute for a small amount of time and then the thread will suspend activity, allowing for another thread to execute. 50 | 51 | Singleton Pattern, in a multi-threaded application, special care needs to be taken to ensure that access to the singleton is limited so that only one thread enters specific areas of logic at a time. Because of this synchronization of threads, 52 | it is possible for one thread to retrieve a value and update it, and, before it can be stored, another thread also updates the value. 53 | 54 | To avoid data being updated incorrectly, restriction is required to prevent more than one thread from executing the same block of logic at the same time. There are several mechanisms supported in the .Net Framework and, 55 | in the singleton pattern. 56 | 57 | We'll examine several Singleton Implementation patterns 58 | 59 | 60 | ## Simple Singleton Pattern 61 | 62 | The simplest implementation of the Singleton Pattern is not thread safe, which may result in two different threads evaluate the instance value to `null` and actually create two instances of the object thus completely violating the core concept of the Singleton Pattern. 63 | 64 | ```c# 65 | 66 | // This is not recommended implementation of the Singleton Pattern 67 | // Just an example of the simplest implementation 68 | public sealed class Spooler 69 | { 70 | private static Spooler instance = null; 71 | 72 | private Spooler() 73 | { 74 | 75 | } 76 | 77 | public static Spooler Instance => instance ??= new Spooler(); 78 | } 79 | ``` 80 | In the above example, it is possible for the instance to be created before the expression is evaluated, but the memory model doesn't guarantee that the new value of instance will be seen by other threads unless suitable memory barriers have been passed. 81 | 82 | In the line ` public static Spooler Instance => instance ??= new Spooler();` We simply make use of the [null-coalescing operator](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator) to check if our instance has been instantiated 83 | and if not create it or return the existing instantiated object. 84 | 85 | In a simple single thread application model the example will work, but this is not an optimal implementation. 86 | 87 | In the following, example we'll create an `abstract` class which our Singleton class will extend, to basically add some features we can reuse throughout our further examples. 88 | 89 | We'll add a Spool class which just preforms a very rudimentary implementation of a print queue which will serve for a demo purposes. 90 | 91 | ```c# 92 | public abstract class Spool 93 | { 94 | public List Queue { get; } = new List(); 95 | } 96 | ``` 97 | 98 | We will then simply inherit this class in our Singleton class 99 | 100 | ```c# 101 | public sealed class Spooler : Spool 102 | { 103 | private static Spooler instance = null; 104 | 105 | private Spooler() 106 | { 107 | 108 | } 109 | 110 | public static Spooler Instance => instance ??= new Spooler(); 111 | 112 | } 113 | ``` 114 | 115 | We'll also create a really simple console application to illustrate how we would implement and use this Singleton Pattern Implementation. 116 | 117 | ```c# 118 | class Program 119 | { 120 | static void Main(string[] args) 121 | { 122 | if (args == null) throw new ArgumentNullException(nameof(args)); 123 | 124 | for (int i = 0; i < 12; i++) 125 | { 126 | Spooler.Instance.Queue.Add(new PrintQueueItem{ DocumentName = $"test-document-{i}"}); 127 | } 128 | 129 | 130 | foreach (var queueItem in Spooler.Instance.Queue) 131 | { 132 | Console.WriteLine(queueItem.DocumentName); 133 | } 134 | } 135 | } 136 | ``` 137 | If we run a simple application we'll see that our application works as expected and there is only one instance of `Spooler` class and we can add values to it no problem. Interesting point to note here, is that because our Singleton class 138 | returns a reference to itself in the instance, we can then just use it in Fluent style i.e. we don't need to create a variable to reference to it. 139 | 140 | The problem comes in when we try to use this class in a Multi-Threaded environment. Which I will try to simulate by creating a load of tasks which start a new thread and attempts to add a document to our `Queue` 141 | 142 | 143 | 144 | ### Simple Thread Safe Singleton Implementation 145 | We could improve the above implementation by making use of a `lock` on the shared object to check if the instance has been created before creating a new one. 146 | 147 | Locking ensures that all reads occur logically after the lock is acquired while unlocking ensures all writes occur logically before the lock release. This ensures only one thread can create an instance 148 | 149 | This pattern may address the memory barrier issues, faced with the Simple Implementation, but unfortunately has a performance impact because a lock is acquired every time the instance is requested. 150 | 151 | ```c# 152 | public sealed class Spooler : Spool 153 | { 154 | private static Spooler instance; 155 | private static readonly object threadlock = new object(); 156 | 157 | 158 | public static Spooler Instance 159 | { 160 | get 161 | { 162 | lock (threadlock) 163 | { 164 | return instance ??= new Spooler(); 165 | } 166 | } 167 | } 168 | } 169 | 170 | ``` 171 | ### Double check Lock Singleton Pattern 172 | 173 | We could attempt the issues above by implementing a Double check Lock pattern. 174 | 175 | This pattern is not recommended and has a number of issues. 176 | 177 | ```c# 178 | public sealed class Spooler : Spool 179 | { 180 | 181 | private static Spooler instance; 182 | private static readonly object padlock = new object(); 183 | 184 | public static Spooler Instance 185 | { 186 | get 187 | { 188 | if (instance == null) 189 | { 190 | lock (padlock) 191 | { 192 | if (instance == null) 193 | { 194 | instance = new Spooler(); 195 | } 196 | } 197 | } 198 | return instance; 199 | } 200 | } 201 | } 202 | ``` 203 | 204 | This pattern with the help pf C# 6.0 + can be further updated by introducing the `volatile` keyword 205 | 206 | > The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. The compiler, the runtime system, and even hardware may rearrange reads and writes to memory locations for performance reasons. Fields that are declared volatile are not subject to these optimizations. Adding the volatile modifier ensures that all threads will observe volatile writes performed by any other thread in the order in which they were performed. There is no guarantee of a single total ordering of volatile writes as seen from all threads of execution. 207 | > 208 | 209 | [Volatile (C# Reference )](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/volatile) 210 | 211 | ```c# 212 | public sealed class Spooler : Spool 213 | { 214 | 215 | private static volatile Spooler instance; 216 | private static readonly object threadLock = new object(); 217 | 218 | public static Spooler Instance 219 | { 220 | get 221 | { 222 | if (instance != null) return instance; 223 | 224 | lock (threadLock) 225 | { 226 | if (instance == null) 227 | { 228 | instance = new Spooler(); 229 | } 230 | } 231 | 232 | return instance; 233 | } 234 | } 235 | } 236 | ``` 237 | 238 | ### Almost Lazy Singleton Pattern 239 | 240 | `static` constructors in C# execute only when an instance of the class is created or a static member is referenced, and to execute only once per AppDomain. The check for the type being newly constructed needs to be executed whatever else happens, it will be faster than adding extra checking as in the previous examples. 241 | 242 | This approach is still not ideal and actually has some issues because if you have static members other than Instance, the first reference to those members will involve creating the instance. There are also additional complications in that if one static constructor invokes another which invokes the first again. The laziness of type initializers are only guaranteed by .NET when the type isn't marked with a special flag called `beforefieldinit`. 243 | 244 | This pattern also incurs a performance hit. 245 | 246 | ```c# 247 | public sealed class Spooler : Spool 248 | { 249 | private static readonly Spooler instance = new Spooler(); 250 | 251 | // Explicit static constructor to tell C# compiler 252 | // not to mark type as beforefieldinit 253 | static Spooler() 254 | { 255 | } 256 | 257 | private Spooler() 258 | { 259 | } 260 | 261 | public static Spooler Instance => instance; 262 | } 263 | ``` 264 | ### Full Lazy Implementation 265 | 266 | In this pattern instantiation is triggered by the first reference to the static member of the nested class, which only occurs in Instance. 267 | 268 | ```c# 269 | public sealed class Spooler : Spool 270 | { 271 | 272 | private Spooler() 273 | { 274 | } 275 | 276 | public static Spooler Instance { get { return Nested.instance; } } 277 | 278 | private class Nested 279 | { 280 | // Explicit static constructor to tell C# compiler 281 | // not to mark type as beforefieldinit 282 | static Nested() 283 | { 284 | } 285 | 286 | internal static readonly Spooler instance = new Spooler(); 287 | } 288 | 289 | } 290 | ``` 291 | 292 | ### Generic Lazy Implementation 293 | 294 | The .net framework has some really cool features that and `System.Lazy` is one of those features to help provide lazy initialization with access from multiple threads. 295 | 296 | The code below implicitly uses `LazyThreadSafetyMode.ExecutionAndPublication` as the thread safety mode for the `Lazy`. 297 | 298 | simpler way to achieve laziness, using .NET 4 +, in my opinion tt also has the advantage that it's obviously lazy and it is clearly and implicitly stated in the code. 299 | 300 | ```c# 301 | public sealed class Spooler : Spool 302 | { 303 | private static readonly Lazy 304 | lazy = 305 | new Lazy 306 | (() => new Spooler()); 307 | 308 | public static Spooler Instance { get { return lazy.Value; } } 309 | 310 | private Spooler() 311 | { 312 | } 313 | } 314 | ``` 315 | 316 | #### Example Scenarios for Singleton Classes 317 | 318 | Singleton classes are typically used in applications to create utility classes. A utility classes typically have the following characteristics 319 | * Have no state of its own 320 | * All the methods can be class methods (static) rather than object methods 321 | * Provide methods for multiple other classes i.e. Common or shared code 322 | 323 | Other common usages for Singleton classes could be: 324 | 325 | * **Service Proxies:** invoking a service or API is an expensive operation. Creating Service proxy as a Singleton this overhead can be reduced. 326 | * **Facades:** Database connections are another example where Singleton can be used to improve performance and synchronization. 327 | **Logging:** I/O is resource consuming operation, having a single instance of a Logger, data can be persisted to log files. 328 | * **Data sharing:** Configuration values and constant values can be kept in Singleton to read by other components of the application. 329 | * **Caching:** Data fetching is a time consuming process whereas caching required data in the application memory avoids DB calls and Singleton can be used to handle the caching with thread synchronization. 330 | 331 | 332 | The Singleton Pattern should only be used when necessary as it can introduce a potential bottleneck for the application. Sometimes, the pattern ii may be viewed as an anti-pattern because it could introduce `Global State`. 333 | 334 | `Global state`, unknown dependencies within an application are introduced and it then becomes unclear as to how many types might depend on the information. Additionally, many frameworks and repositories already limit access 335 | when required, so introducing an additional mechanism might limit the performance unnecessarily. 336 | 337 | 338 | 339 | 340 | 341 | -------------------------------------------------------------------------------- /Singleton/singleton.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Threenine.Print", "src\Threenine.Print\Threenine.Print.csproj", "{212FE8F6-FD1F-4EA1-AC0A-18F07203C641}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "src\TestConsole\TestConsole.csproj", "{18041BBC-0361-49C0-999B-B4BD175208B1}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Threenine.Print.Tests", "test\Threenine.Print.Tests\Threenine.Print.Tests.csproj", "{23EF9C55-D5EE-42E4-AAA9-09B8297E8830}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Debug|x64.ActiveCfg = Debug|Any CPU 28 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Debug|x64.Build.0 = Debug|Any CPU 29 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Debug|x86.ActiveCfg = Debug|Any CPU 30 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Debug|x86.Build.0 = Debug|Any CPU 31 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Release|x64.ActiveCfg = Release|Any CPU 34 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Release|x64.Build.0 = Release|Any CPU 35 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Release|x86.ActiveCfg = Release|Any CPU 36 | {212FE8F6-FD1F-4EA1-AC0A-18F07203C641}.Release|x86.Build.0 = Release|Any CPU 37 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Debug|x64.ActiveCfg = Debug|Any CPU 40 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Debug|x64.Build.0 = Debug|Any CPU 41 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Debug|x86.ActiveCfg = Debug|Any CPU 42 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Debug|x86.Build.0 = Debug|Any CPU 43 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Release|x64.ActiveCfg = Release|Any CPU 46 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Release|x64.Build.0 = Release|Any CPU 47 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Release|x86.ActiveCfg = Release|Any CPU 48 | {18041BBC-0361-49C0-999B-B4BD175208B1}.Release|x86.Build.0 = Release|Any CPU 49 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Debug|x64.ActiveCfg = Debug|Any CPU 52 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Debug|x64.Build.0 = Debug|Any CPU 53 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Debug|x86.ActiveCfg = Debug|Any CPU 54 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Debug|x86.Build.0 = Debug|Any CPU 55 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Release|x64.ActiveCfg = Release|Any CPU 58 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Release|x64.Build.0 = Release|Any CPU 59 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Release|x86.ActiveCfg = Release|Any CPU 60 | {23EF9C55-D5EE-42E4-AAA9-09B8297E8830}.Release|x86.Build.0 = Release|Any CPU 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /Singleton/src/TestConsole/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Threenine.Print; 3 | using Threenine.Print.GenericLazy; 4 | 5 | namespace TestConsole 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | if (args == null) throw new ArgumentNullException(nameof(args)); 12 | 13 | for (int i = 0; i < 12; i++) 14 | { 15 | Spooler.Instance.Queue.Add(new PrintQueueItem{ DocumentName = $"test-document-{i}"}); 16 | } 17 | 18 | 19 | foreach (var queueItem in Spooler.Instance.Queue) 20 | { 21 | Console.WriteLine(queueItem.DocumentName); 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Singleton/src/TestConsole/TestConsole.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/AlmostLazy/Spooler.cs: -------------------------------------------------------------------------------- 1 | namespace Threenine.Print.AlmostLazy 2 | { 3 | public class Spooler : Spool 4 | { 5 | private static readonly Spooler instance = new Spooler(); 6 | 7 | // Explicit static constructor to tell C# compiler 8 | // not to mark type as beforefieldinit 9 | static Spooler() 10 | { 11 | } 12 | 13 | private Spooler() 14 | { 15 | } 16 | 17 | public static Spooler Instance => instance; 18 | } 19 | } -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/DoubleLock/Spooler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Threenine.Print.DoubleLock 4 | { 5 | /// 6 | /// Do not use this pattern 7 | /// 8 | public sealed class Spooler : Spool 9 | { 10 | 11 | private static volatile Spooler instance; 12 | private static readonly object threadLock = new object(); 13 | 14 | public static Spooler Instance 15 | { 16 | get 17 | { 18 | if (instance != null) return instance; 19 | 20 | lock (threadLock) 21 | { 22 | if (instance == null) 23 | { 24 | instance = new Spooler(); 25 | } 26 | } 27 | 28 | return instance; 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/FullLazy/Spooler.cs: -------------------------------------------------------------------------------- 1 | namespace Threenine.Print.FullLazy 2 | { 3 | public sealed class Spooler : Spool 4 | { 5 | 6 | private Spooler() 7 | { 8 | } 9 | 10 | public static Spooler Instance { get { return Nested.instance; } } 11 | 12 | private class Nested 13 | { 14 | // Explicit static constructor to tell C# compiler 15 | // not to mark type as beforefieldinit 16 | static Nested() 17 | { 18 | } 19 | 20 | internal static readonly Spooler instance = new Spooler(); 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/GenericLazy/Spooler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Threenine.Print.GenericLazy 4 | { 5 | public sealed class Spooler : Spool 6 | { 7 | private bool _disposed; 8 | private static volatile Lazy 9 | lazy = 10 | new Lazy 11 | (() => new Spooler()); 12 | 13 | public static Spooler Instance => lazy.Value; 14 | 15 | private Spooler() 16 | { 17 | } 18 | 19 | 20 | 21 | } 22 | } -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/PrintQueue.cs: -------------------------------------------------------------------------------- 1 | namespace Threenine.Print 2 | { 3 | public class PrintQueueItem 4 | { 5 | public string DocumentName { get; set; } 6 | 7 | } 8 | } -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/Simple/Spooler.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Threenine.Print.Simple 3 | { 4 | 5 | /// 6 | /// Simple Implementation of the Singleton Pattern. 7 | /// Not Thread safe and not good practice 8 | /// DO NOT USE 9 | /// 10 | public sealed class Spooler : Spool 11 | { 12 | private static Spooler _instance; 13 | 14 | public static Spooler Instance => _instance ??= new Spooler(); 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/SimpleThreadSafe/Spooler.cs: -------------------------------------------------------------------------------- 1 | namespace Threenine.Print.SimpleThreadSafe 2 | { 3 | public sealed class Spooler : Spool 4 | { 5 | private static Spooler instance; 6 | private static readonly object threadlock = new object(); 7 | 8 | 9 | public static Spooler Instance 10 | { 11 | get 12 | { 13 | lock (threadlock) 14 | { 15 | return instance ??= new Spooler(); 16 | } 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/Spool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Threenine.Print 4 | { 5 | public abstract class Spool 6 | { 7 | public List Queue { get; } = new List(); 8 | } 9 | } -------------------------------------------------------------------------------- /Singleton/src/Threenine.Print/Threenine.Print.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Singleton/test/Threenine.Print.Tests/DoubleLockTests.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using Threenine.Print.Simple; 4 | using Xunit; 5 | 6 | namespace Threenine.Print.Tests 7 | { 8 | public class DoubleLockTests 9 | { 10 | 11 | [Fact] 12 | public void ShouldCreateOneInstance() 13 | { 14 | 15 | var firstItem = new PrintQueueItem {DocumentName = "firstItem"}; 16 | var secondItem = new PrintQueueItem {DocumentName = "secondItem"}; 17 | 18 | var instance1 = Spooler.Instance; 19 | var instance2 = Spooler.Instance; 20 | 21 | Assert.Same(instance1, instance2); 22 | 23 | instance1.Queue.Add(firstItem); 24 | 25 | Assert.Equal(instance1.Queue.Count, instance2.Queue.Count); 26 | 27 | instance2.Queue.Add(secondItem); 28 | 29 | Assert.Equal(instance1.Queue.Count, instance2.Queue.Count); 30 | 31 | Assert.Same(instance1, instance2); 32 | 33 | 34 | 35 | 36 | } 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /Singleton/test/Threenine.Print.Tests/GenericLazyTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Threenine.Print.Simple; 3 | using Xunit; 4 | 5 | namespace Threenine.Print.Tests 6 | { 7 | public class GenericLazyTests 8 | { 9 | [Fact] 10 | public void ShouldCreateOneInstance() 11 | { 12 | 13 | var firstItem = new PrintQueueItem {DocumentName = "firstItem"}; 14 | var secondItem = new PrintQueueItem {DocumentName = "secondItem"}; 15 | 16 | var instance1 = Spooler.Instance; 17 | var instance2 = Spooler.Instance; 18 | 19 | Assert.Same(instance1, instance2); 20 | 21 | instance1.Queue.Add(firstItem); 22 | 23 | Assert.Equal(instance1.Queue.Count, instance2.Queue.Count); 24 | 25 | instance2.Queue.Add(secondItem); 26 | 27 | Assert.Equal(instance1.Queue.Count, instance2.Queue.Count); 28 | 29 | Assert.Same(instance1, instance2); 30 | 31 | 32 | 33 | 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Singleton/test/Threenine.Print.Tests/Threenine.Print.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /strategy/README.md: -------------------------------------------------------------------------------- 1 | # Strategy Pattern 2 | 3 | The *Strategy Pattern* is a *Behavioral Pattern* , [Software Design Patterns](https://garywoodfine.com/software-design-patterns/ "Software Design Patterns | Gary Woodfine") defined by the Gang of Four in [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/2PdkTck ) , it is also discussed in [Head First Design Patterns](https://amzn.to/32YhjIT "Head First Design Patterns"). [](https://amzn.to/2PdkTck) 4 | 5 | The Strategy pattern enables the definition and encapsulation of a number of algorithms and make them interchangeable. The algorithms may vary independently and may deliver different outcomes and enable different behaviour, however they can all be called using the same method signature. 6 | 7 | A typical example of how the Strategy pattern is implemented is illustrated in the diagram below. 8 | 9 | ![Stratey Pattern](https://garywoodfine.com/wp-content/uploads/2021/06/image.png) 10 | 11 | In Strategy pattern, a class behaviour or its algorithm can be changed at run time. Enabling the creation of objects which represent various strategies and a context object whose behaviour varies as per its strategy object. The strategy object changes the executing algorithm of the context object. 12 | 13 | Another name for the Strategy pattern is *Policy*, which in my opinion provides a better clue on how this pattern can be utilised in software applications. The most common scenario in enterprise software development where the Strategy pattern can be implemented in a Business Rules Engine. 14 | 15 | A business rules engine (BRE) is an application that manages decision processes using pre-defined logic to determine outcomes. BREs enable precise decision making, and are especially useful for complex dependencies, as well as in instances where regulatory or organizational rule changes frequently require logic changes. 16 | 17 | An example of this may be in a Bank loan software application, there are various rules that can be triggered based on responses to questions. If the answer is *yes* to one question or *no* to another question, the chances of approval for a loan could be impacted or further subsequent questions may be required to further clarify financial information. These rules help guide users through the application process and help the financial institution determine the eligibility and structure their business. 18 | 19 | I recently came across [Basic Rules Engine Design Pattern](https://tenmilesquare.com/basic-rules-engine-design-pattern/ "Basic Rules Engine Design Pattern | ten mile square"), where in the author just actually describes the typical implementation of the Strategy pattern, as you'll be able to tell from the class diagram he supplied, the implementation resembles the simplified Strategy Pattern above. 20 | 21 | ![Basic Rules Engine](https://garywoodfine.com/wp-content/uploads/2021/06/image-1.png "Basic Rules Engine") 22 | 23 | ### Implementation 24 | 25 | In our very simple implementation of the strategy pattern we'll build a simple console game, in which we'll invite the user to enter a number in the terminal and based on the value we'll return different values. Each of the return values will be returned by different strategies. 26 | 27 | let's start by declaring an interface for our strategies, which we'll define as `IRule` which will have two methods a `Verify` and an `Execute`. 28 | ```csharp 29 | public interface IRule 30 | { 31 | bool Verify(string choice); 32 | void Execute(); 33 | } 34 | ``` 35 | 36 | Essentially the `Verfify` method will taken in the input and determine if the strategy can be executed based on the input and provide a response back to calling application to signify whether it is appropriate to execute the strategy. 37 | 38 | The `Execute` will enable the calling application to execute the strategy. 39 | 40 | lets implement a first simple strategy class implementing the interface. We'll implementing a strategy to ensure the user has provided a valid number as input, We'll name our class `NotANumberRule.cs` the code for the class will be as follows. 41 | 42 | ```csharp 43 | public class NotANumberRule : IRule 44 | { 45 | public bool Verify(string choice) 46 | { 47 | return !choice.All(char.IsDigit); 48 | } 49 | public void Execute() 50 | { 51 | Console.WriteLine("We said enter a number "); 52 | } 53 | } 54 | ``` 55 | 56 | I've kept the implementation of our class as simple as possible as not to distract from the detail of the strategy pattern. In the Verify method we iterate through each char in the supplied string to ensure that it is a Numeric digit, if we find any alpha characters then we want t signify that our strategy should be executed because we want to notify the user that they should only enter a number. 57 | 58 | We can create any number of Rules classes providing the all implement the `IRule` interface. In the code sample I have implemented several rules each wil trigger on various conditions. 59 | 60 | We can now implement our simple Console application, in which we'll prompt the user to enter to choose a number between 1 and 10. 61 | 62 | ```csharp 63 | class Program 64 | { 65 | static void Main(string[] args) 66 | { 67 | string val; 68 | Console.Write("Enter a number between 1 & 10 : "); 69 | val = Console.ReadLine(); 70 | 71 | 72 | } 73 | ``` 74 | We now need to implement one more bit of code, which will basically retrieve all our Rules that we have defined in our application and determine which rule to execute based on the conditions. 75 | 76 | We use a some Reflection to retrieve all types in our application and check to see if they implement the `IRule` interface, if they do we will instantiate the class and call the `Verify` method to determine if we should call the `Execute` method 77 | 78 | ```csharp 79 | private static void ProcessInput(string input) 80 | { 81 | foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) 82 | { 83 | if (!typeof(IRule).IsAssignableFrom(type) || !type.IsPublic || type.IsInterface) continue; 84 | 85 | var rule = (IRule)Assembly.GetExecutingAssembly().CreateInstance(type.FullName, false); 86 | if (rule.Verify(input)) rule.Execute(); 87 | } 88 | } 89 | ``` 90 | 91 | With this method now in place we can add it to our Console application. 92 | 93 | ```csharp 94 | static void Main(string[] args) 95 | { 96 | 97 | string val; 98 | Console.Write("Enter a number between 1 & 10 : "); 99 | val = Console.ReadLine(); 100 | ProcessInput(val); 101 | 102 | } 103 | ``` 104 | 105 | If we attempt to run the application and attempt to enter any string that is not a number then we'll activate our NotANumber rule. 106 | 107 | ![img.png](img.png) 108 | 109 | Let's extend our application and implement another rule, which specifies if the user enters a number 2, that a random snarky comment is generated. 110 | 111 | ```csharp 112 | public class SnarkyCommentRule : IRule 113 | { 114 | public bool Verify(string choice) 115 | { 116 | if (!choice.All(char.IsDigit)) return false; 117 | var answer = Convert.ToInt32(choice); 118 | return answer == 2; 119 | } 120 | 121 | public void Execute() 122 | { 123 | var index = new Random().Next(responses.Count); 124 | Console.WriteLine(responses[index]); 125 | } 126 | 127 | private List responses => new List() 128 | { 129 | "To err is human to choose number 2 is plain stupid", 130 | "Of all the numbers to choose you picked 2. Why?", 131 | "A bird in the hand is worth 2 in the bush", 132 | "You picked a couple of ones", 133 | "Once bitten twice shy", 134 | "An arch bishop Desmond", 135 | "A vicar in a two two", 136 | }; 137 | } 138 | ``` 139 | If we run our application now and enter number 2 a random comment will be displayed to the user. 140 | 141 | ![img_1.png](img_1.png) -------------------------------------------------------------------------------- /strategy/StrategyClient/IRule.cs: -------------------------------------------------------------------------------- 1 | namespace StrategyClient 2 | { 3 | public interface IRule 4 | { 5 | bool Verify(string choice); 6 | void Execute(); 7 | } 8 | } -------------------------------------------------------------------------------- /strategy/StrategyClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace StrategyClient 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | 13 | string val; 14 | Console.Write("Enter a number between 1 & 10 : "); 15 | val = Console.ReadLine(); 16 | ProcessInput(val); 17 | 18 | } 19 | 20 | private static void ProcessInput(string input) 21 | { 22 | foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) 23 | { 24 | if (!typeof(IRule).IsAssignableFrom(type) || !type.IsPublic || type.IsInterface) continue; 25 | var rule = (IRule)Assembly.GetExecutingAssembly().CreateInstance(type.FullName, false); 26 | if (rule.Verify(input)) rule.Execute(); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /strategy/StrategyClient/Rules/ColoredTerminalRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace StrategyClient.Rules 5 | { 6 | public class ColoredTerminalRule : IRule 7 | { 8 | public bool Verify(string choice) 9 | { 10 | if (!choice.All(char.IsDigit)) return false; 11 | var answer = Convert.ToInt32(choice); 12 | return answer == 4; 13 | } 14 | 15 | public void Execute() 16 | { 17 | Console.ForegroundColor 18 | = ConsoleColor.Blue; 19 | 20 | Console.WriteLine("If you see blue you picked 4"); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /strategy/StrategyClient/Rules/NotANumberRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace StrategyClient.Rules 5 | { 6 | public class NotANumberRule : IRule 7 | { 8 | public bool Verify(string choice) 9 | { 10 | return !choice.All(char.IsDigit); 11 | } 12 | 13 | public void Execute() 14 | { 15 | Console.WriteLine("We said enter a number "); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /strategy/StrategyClient/Rules/NotInRangeRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace StrategyClient.Rules 5 | { 6 | public class NotInRangeRule : IRule 7 | { 8 | public bool Verify(string choice) 9 | { 10 | if (choice.All(char.IsDigit)) 11 | { 12 | var answer = Convert.ToInt32(choice); 13 | 14 | if (answer is < 0 or > 10) return true; 15 | 16 | } 17 | 18 | return false; 19 | } 20 | 21 | public void Execute() 22 | { 23 | Console.WriteLine("We said a number between 1 and 10!"); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /strategy/StrategyClient/Rules/ProfoundStatementRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace StrategyClient.Rules 6 | { 7 | public class ProfoundStatementRule : IRule 8 | { 9 | public bool Verify(string choice) 10 | { 11 | if (!choice.All(char.IsDigit)) return false; 12 | var answer = Convert.ToInt32(choice); 13 | return answer == 3; 14 | } 15 | 16 | public void Execute() 17 | { 18 | var index = new Random().Next(responses.Count); 19 | Console.WriteLine(responses[index]); 20 | } 21 | 22 | private List responses => new List() 23 | { 24 | "All endings are also beginnings. We just don't know it at the time.", 25 | "Art and love are the same thing: It’s the process of seeing yourself in things that are not you.", 26 | "The reason birds can fly and we can't is simply because they have perfect faith, for to have faith is to have wings.", 27 | "Till this moment I never knew myself.", 28 | "At the center of your being you have the answer; you know who you are and you know what you want.", 29 | "We can never know what to want, because, living only one life, we can neither compare it with our previous lives nor perfect it in our lives to come.", 30 | "I told him I believed in hell, and that certain people, like me, had to live in hell before they died, to make up for missing out on it after death, since they didn't believe in life after death, and what each person believed happened to him when he died.", 31 | "How satisfying it is to leave a mark on a blank surface. To make a map of my movement - no matter how temporary." 32 | 33 | 34 | }; 35 | } 36 | } -------------------------------------------------------------------------------- /strategy/StrategyClient/Rules/SnarkyCommentRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace StrategyClient.Rules 6 | { 7 | public class SnarkyCommentRule : IRule 8 | { 9 | public bool Verify(string choice) 10 | { 11 | if (!choice.All(char.IsDigit)) return false; 12 | var answer = Convert.ToInt32(choice); 13 | return answer == 2; 14 | } 15 | 16 | public void Execute() 17 | { 18 | var index = new Random().Next(responses.Count); 19 | Console.WriteLine(responses[index]); 20 | } 21 | 22 | private List responses => new List() 23 | { 24 | "To err is human to choose number 2 is plain stupid", 25 | "Of all the numbers to choose you picked 2. Why?", 26 | "A bird in the hand is worth 2 in the bush", 27 | "You picked a couple of ones", 28 | "Once bitten twice shy", 29 | "An arch bishop Desmond", 30 | "A vicar in a two two", 31 | }; 32 | } 33 | } -------------------------------------------------------------------------------- /strategy/StrategyClient/StrategyClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /strategy/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garywoodfine/software-design-patterns/4acd2c4c6a0092ef84056f4301a6b45e520e8de2/strategy/img.png -------------------------------------------------------------------------------- /strategy/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garywoodfine/software-design-patterns/4acd2c4c6a0092ef84056f4301a6b45e520e8de2/strategy/img_1.png -------------------------------------------------------------------------------- /strategy/strategy.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.6.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StrategyClient", "StrategyClient\StrategyClient.csproj", "{840DCE39-44C7-46AE-9F52-8721C47B2AF4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Debug|x64.ActiveCfg = Debug|Any CPU 24 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Debug|x64.Build.0 = Debug|Any CPU 25 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Debug|x86.ActiveCfg = Debug|Any CPU 26 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Debug|x86.Build.0 = Debug|Any CPU 27 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Release|x64.ActiveCfg = Release|Any CPU 30 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Release|x64.Build.0 = Release|Any CPU 31 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Release|x86.ActiveCfg = Release|Any CPU 32 | {840DCE39-44C7-46AE-9F52-8721C47B2AF4}.Release|x86.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | --------------------------------------------------------------------------------