├── .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 | 
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 | [](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 | [](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 | 
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 | 
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 | [](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 | 
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 | [](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 | 
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 | 
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 | [](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 | [](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 | 
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 | 
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 | [](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 | 
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 | 
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 | 
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 | 
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------