├── .gitignore ├── Finished sample ├── Enterprise Patterns │ ├── EnterprisePatterns.sln │ └── EnterprisePatterns │ │ ├── DbContexts │ │ └── OrderDbContext.cs │ │ ├── DemoServices │ │ ├── RepositoryDemoService.cs │ │ └── UnitOfWorkDemoService.cs │ │ ├── EnterprisePatterns.csproj │ │ ├── Entities │ │ ├── Order.cs │ │ └── OrderLine.cs │ │ ├── Migrations │ │ ├── InitialMigration.Designer.cs │ │ ├── InitialMigration.cs │ │ └── OrderDbContextModelSnapshot.cs │ │ ├── Orders.db │ │ ├── Program.cs │ │ ├── Repositories │ │ ├── GenericEFCoreRepository.cs │ │ ├── GenericOrderRepository.cs │ │ ├── IOrderLineRepository.cs │ │ ├── IRepositoryOfT.cs │ │ └── OrderLineRepository.cs │ │ └── UnitsOfWork │ │ ├── CreateOrderWithOrderLinesUnitOfWork.cs │ │ ├── IUnitOfWork.cs │ │ └── UnitOfWork.cs └── Gang of Four Patterns │ ├── AbstractFactory │ ├── AbstractFactory.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── Adapter │ ├── Adapter.csproj │ ├── ClassAdapterImplementation.cs │ ├── ObjectAdapterImplementation.cs │ └── Program.cs │ ├── Bridge │ ├── Bridge.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── Builder │ ├── Builder.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── ChainOfResponsibility │ ├── ChainOfResponsibility.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── Command │ ├── Command.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── Composite │ ├── Composite.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── Decorator │ ├── Decorator.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── DesignPatterns.sln │ ├── Facade │ ├── Facade.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── FactoryMethod │ ├── FactoryMethod.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── Flyweight │ ├── Flyweight.csproj │ ├── Implementation.cs │ └── Program.cs │ ├── Interpreter │ ├── Implementation.cs │ ├── Interpreter.csproj │ └── Program.cs │ ├── Iterator │ ├── Implementation.cs │ ├── Iterator.csproj │ └── Program.cs │ ├── Mediator │ ├── Implementation.cs │ ├── Mediator.csproj │ └── Program.cs │ ├── Memento │ ├── Implementation.cs │ ├── Memento.csproj │ └── Program.cs │ ├── Observer │ ├── Implementation.cs │ ├── Observer.csproj │ └── Program.cs │ ├── Prototype │ ├── Implementation.cs │ ├── Program.cs │ └── Prototype.csproj │ ├── Proxy │ ├── Implementation.cs │ ├── Program.cs │ └── Proxy.csproj │ ├── Singleton │ ├── Implementation.cs │ ├── Program.cs │ └── Singleton.csproj │ ├── State │ ├── Implementation.cs │ ├── Program.cs │ └── State.csproj │ ├── Strategy │ ├── Implementation.cs │ ├── Program.cs │ └── Strategy.csproj │ ├── TemplateMethod │ ├── Implementation.cs │ ├── Program.cs │ └── TemplateMethod.csproj │ └── Visitor │ ├── Implementation.cs │ ├── Program.cs │ └── Visitor.csproj ├── LICENSE ├── README.md └── Starter files ├── Enterprise Patterns ├── EnterprisePatterns.sln └── EnterprisePatterns │ ├── DbContexts │ └── OrderDbContext.cs │ ├── DemoServices │ ├── RepositoryDemoService.cs │ └── UnitOfWorkDemoService.cs │ ├── EnterprisePatterns.csproj │ ├── Entities │ ├── Order.cs │ └── OrderLine.cs │ ├── Migrations │ ├── InitialMigration.Designer.cs │ ├── InitialMigration.cs │ └── OrderDbContextModelSnapshot.cs │ ├── Orders.db │ └── Program.cs └── Gang of Four Patterns ├── AbstractFactory ├── AbstractFactory.csproj ├── Implementation.cs └── Program.cs ├── Adapter ├── Adapter.csproj ├── Program.cs └── Properties │ └── launchSettings.json ├── Bridge ├── Bridge.csproj ├── Implementation.cs └── Program.cs ├── Builder ├── Builder.csproj ├── Implementation.cs └── Program.cs ├── ChainOfResponsibility ├── ChainOfResponsibility.csproj ├── Implementation.cs └── Program.cs ├── Command ├── Command.csproj ├── Implementation.cs └── Program.cs ├── Composite ├── Composite.csproj ├── Implementation.cs └── Program.cs ├── Decorator ├── Decorator.csproj ├── Implementation.cs └── Program.cs ├── DesignPatterns.sln ├── Facade ├── Facade.csproj ├── Implementation.cs └── Program.cs ├── FactoryMethod ├── FactoryMethod.csproj ├── Implementation.cs └── Program.cs ├── Flyweight ├── Flyweight.csproj ├── Implementation.cs └── Program.cs ├── Interpreter ├── Implementation.cs ├── Interpreter.csproj └── Program.cs ├── Iterator ├── Implementation.cs ├── Iterator.csproj └── Program.cs ├── Mediator ├── Implementation.cs ├── Mediator.csproj └── Program.cs ├── Memento ├── Implementation.cs ├── Memento.csproj └── Program.cs ├── Observer ├── Implementation.cs ├── Observer.csproj └── Program.cs ├── Prototype ├── Implementation.cs ├── Program.cs └── Prototype.csproj ├── Proxy ├── Implementation.cs ├── Program.cs └── Proxy.csproj ├── Singleton ├── Implementation.cs ├── Program.cs └── Singleton.csproj ├── State ├── Implementation.cs ├── Program.cs └── State.csproj ├── Strategy ├── Implementation.cs ├── Program.cs └── Strategy.csproj ├── TemplateMethod ├── Implementation.cs ├── Program.cs └── TemplateMethod.csproj └── Visitor ├── Implementation.cs ├── Program.cs └── Visitor.csproj /.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/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34601.278 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnterprisePatterns", "EnterprisePatterns\EnterprisePatterns.csproj", "{4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B22A0AEA-8A58-425A-B127-C9E5838FAD4A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/DbContexts/OrderDbContext.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | using System.IO; 4 | 5 | namespace EnterprisePatterns.DbContexts; 6 | 7 | public class OrderDbContext(DbContextOptions options) : DbContext(options) 8 | { 9 | public DbSet Orders { get; set; } 10 | public DbSet OrderLines { get; set; } 11 | 12 | // seed the database with data 13 | protected override void OnModelCreating(ModelBuilder modelBuilder) 14 | { 15 | modelBuilder.Entity().HasData( 16 | new("Shopping Spree") { Id = 1 }, 17 | new("Holiday Shopping") { Id = 2 }); 18 | 19 | modelBuilder.Entity().HasData( 20 | new("Shoes", 1) { Id = 1, OrderId = 1 }, 21 | new("Shirt", 2) { Id = 2, OrderId = 1 }, 22 | new("Pants", 1) { Id = 3, OrderId = 1 }, 23 | new("Socks", 5) { Id = 4, OrderId = 1 }, 24 | new("Sunglasses", 1) { Id = 5, OrderId = 2 }, 25 | new("Sunscreen", 2) { Id = 6, OrderId = 2 }); 26 | 27 | base.OnModelCreating(modelBuilder); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/DemoServices/RepositoryDemoService.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.Entities; 2 | using EnterprisePatterns.Repositories; 3 | 4 | namespace EnterprisePatterns.Services; 5 | 6 | public class RepositoryDemoService(IRepository _genericOrderRepository, 7 | IOrderLineRepository orderLineRepository) 8 | { 9 | private readonly IRepository _genericOrderRepository = _genericOrderRepository; 10 | private readonly IOrderLineRepository _orderLineRepository = orderLineRepository; 11 | 12 | public async Task RunAsync() 13 | { 14 | // load order 15 | var order = await _genericOrderRepository.Get(1); 16 | 17 | if (order != null) 18 | { 19 | // update order 20 | order.Description = "Updated description"; 21 | // save changes 22 | await _genericOrderRepository.SaveChanges(); 23 | 24 | // create a new orderline for the previously fetched order 25 | OrderLine orderLine = new("Skirt", 1) { OrderId = order.Id }; 26 | _orderLineRepository.Add(orderLine); 27 | 28 | // save changes 29 | await _orderLineRepository.SaveChanges(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/DemoServices/UnitOfWorkDemoService.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.Entities; 2 | using EnterprisePatterns.UnitsOfWork; 3 | 4 | namespace EnterprisePatterns.DemoServices; 5 | 6 | public class UnitOfWorkDemoService(CreateOrderWithOrderLinesUnitOfWork unitOfWork) 7 | { 8 | private readonly CreateOrderWithOrderLinesUnitOfWork _unitOfWork = unitOfWork; 9 | 10 | public async Task RunAsync() 11 | { 12 | // create an order 13 | Order orderToAdd = new Order("My new order"); 14 | _unitOfWork.OrderRepository.Add(orderToAdd); 15 | 16 | // create an order line 17 | OrderLine orderLineToAdd = new OrderLine("A product I bought", 1) 18 | { Order = orderToAdd }; 19 | _unitOfWork.OrderLineRepository.Add(orderLineToAdd); 20 | 21 | // persist the changes 22 | await _unitOfWork.CommitAsync(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/EnterprisePatterns.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | Always 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Entities/Order.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | 3 | namespace EnterprisePatterns.Entities; 4 | 5 | 6 | [Table("Orders")] 7 | public class Order(string? description) 8 | { 9 | public int Id { get; set; } 10 | public string? Description { get; set; } = description; 11 | public ICollection OrderLines { get; set; } = []; 12 | } -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Entities/OrderLine.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | 3 | namespace EnterprisePatterns.Entities; 4 | 5 | [Table("OrderLines")] 6 | public class OrderLine(string product, int amount) 7 | { 8 | public int Id { get; set; } 9 | public string Product { get; set; } = product; 10 | public int Amount { get; set; } = amount; 11 | public int OrderId { get; set; } 12 | public Order Order { get; set; } = null!; 13 | } 14 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Migrations/InitialMigration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using EnterprisePatterns.DbContexts; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | #nullable disable 9 | 10 | namespace EnterprisePatterns.Migrations 11 | { 12 | [DbContext(typeof(OrderDbContext))] 13 | [Migration("20240422092841_InitialMigration")] 14 | partial class InitialMigration 15 | { 16 | /// 17 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 18 | { 19 | #pragma warning disable 612, 618 20 | modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); 21 | 22 | modelBuilder.Entity("EnterprisePatterns.Entities.Order", b => 23 | { 24 | b.Property("Id") 25 | .ValueGeneratedOnAdd() 26 | .HasColumnType("INTEGER"); 27 | 28 | b.Property("Description") 29 | .HasColumnType("TEXT"); 30 | 31 | b.HasKey("Id"); 32 | 33 | b.ToTable("Orders"); 34 | 35 | b.HasData( 36 | new 37 | { 38 | Id = 1, 39 | Description = "Shopping Spree" 40 | }, 41 | new 42 | { 43 | Id = 2, 44 | Description = "Holiday Shopping" 45 | }); 46 | }); 47 | 48 | modelBuilder.Entity("EnterprisePatterns.Entities.OrderLine", b => 49 | { 50 | b.Property("Id") 51 | .ValueGeneratedOnAdd() 52 | .HasColumnType("INTEGER"); 53 | 54 | b.Property("Amount") 55 | .HasColumnType("INTEGER"); 56 | 57 | b.Property("OrderId") 58 | .HasColumnType("INTEGER"); 59 | 60 | b.Property("Product") 61 | .IsRequired() 62 | .HasColumnType("TEXT"); 63 | 64 | b.HasKey("Id"); 65 | 66 | b.HasIndex("OrderId"); 67 | 68 | b.ToTable("OrderLines"); 69 | 70 | b.HasData( 71 | new 72 | { 73 | Id = 1, 74 | Amount = 1, 75 | OrderId = 1, 76 | Product = "Shoes" 77 | }, 78 | new 79 | { 80 | Id = 2, 81 | Amount = 2, 82 | OrderId = 1, 83 | Product = "Shirt" 84 | }, 85 | new 86 | { 87 | Id = 3, 88 | Amount = 1, 89 | OrderId = 1, 90 | Product = "Pants" 91 | }, 92 | new 93 | { 94 | Id = 4, 95 | Amount = 5, 96 | OrderId = 1, 97 | Product = "Socks" 98 | }, 99 | new 100 | { 101 | Id = 5, 102 | Amount = 1, 103 | OrderId = 2, 104 | Product = "Sunglasses" 105 | }, 106 | new 107 | { 108 | Id = 6, 109 | Amount = 2, 110 | OrderId = 2, 111 | Product = "Sunscreen" 112 | }); 113 | }); 114 | 115 | modelBuilder.Entity("EnterprisePatterns.Entities.OrderLine", b => 116 | { 117 | b.HasOne("EnterprisePatterns.Entities.Order", null) 118 | .WithMany("OrderLines") 119 | .HasForeignKey("OrderId") 120 | .OnDelete(DeleteBehavior.Cascade) 121 | .IsRequired(); 122 | }); 123 | 124 | modelBuilder.Entity("EnterprisePatterns.Entities.Order", b => 125 | { 126 | b.Navigation("OrderLines"); 127 | }); 128 | #pragma warning restore 612, 618 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Migrations/InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | #pragma warning disable CA1814 // Prefer jagged arrays over multidimensional 6 | 7 | namespace EnterprisePatterns.Migrations 8 | { 9 | /// 10 | public partial class InitialMigration : Migration 11 | { 12 | /// 13 | protected override void Up(MigrationBuilder migrationBuilder) 14 | { 15 | migrationBuilder.CreateTable( 16 | name: "Orders", 17 | columns: table => new 18 | { 19 | Id = table.Column(type: "INTEGER", nullable: false) 20 | .Annotation("Sqlite:Autoincrement", true), 21 | Description = table.Column(type: "TEXT", nullable: true) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_Orders", x => x.Id); 26 | }); 27 | 28 | migrationBuilder.CreateTable( 29 | name: "OrderLines", 30 | columns: table => new 31 | { 32 | Id = table.Column(type: "INTEGER", nullable: false) 33 | .Annotation("Sqlite:Autoincrement", true), 34 | Product = table.Column(type: "TEXT", nullable: false), 35 | Amount = table.Column(type: "INTEGER", nullable: false), 36 | OrderId = table.Column(type: "INTEGER", nullable: false) 37 | }, 38 | constraints: table => 39 | { 40 | table.PrimaryKey("PK_OrderLines", x => x.Id); 41 | table.ForeignKey( 42 | name: "FK_OrderLines_Orders_OrderId", 43 | column: x => x.OrderId, 44 | principalTable: "Orders", 45 | principalColumn: "Id", 46 | onDelete: ReferentialAction.Cascade); 47 | }); 48 | 49 | migrationBuilder.InsertData( 50 | table: "Orders", 51 | columns: new[] { "Id", "Description" }, 52 | values: new object[,] 53 | { 54 | { 1, "Shopping Spree" }, 55 | { 2, "Holiday Shopping" } 56 | }); 57 | 58 | migrationBuilder.InsertData( 59 | table: "OrderLines", 60 | columns: new[] { "Id", "Amount", "OrderId", "Product" }, 61 | values: new object[,] 62 | { 63 | { 1, 1, 1, "Shoes" }, 64 | { 2, 2, 1, "Shirt" }, 65 | { 3, 1, 1, "Pants" }, 66 | { 4, 5, 1, "Socks" }, 67 | { 5, 1, 2, "Sunglasses" }, 68 | { 6, 2, 2, "Sunscreen" } 69 | }); 70 | 71 | migrationBuilder.CreateIndex( 72 | name: "IX_OrderLines_OrderId", 73 | table: "OrderLines", 74 | column: "OrderId"); 75 | } 76 | 77 | /// 78 | protected override void Down(MigrationBuilder migrationBuilder) 79 | { 80 | migrationBuilder.DropTable( 81 | name: "OrderLines"); 82 | 83 | migrationBuilder.DropTable( 84 | name: "Orders"); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Migrations/OrderDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using EnterprisePatterns.DbContexts; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 6 | 7 | #nullable disable 8 | 9 | namespace EnterprisePatterns.Migrations 10 | { 11 | [DbContext(typeof(OrderDbContext))] 12 | partial class OrderDbContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); 18 | 19 | modelBuilder.Entity("EnterprisePatterns.Entities.Order", b => 20 | { 21 | b.Property("Id") 22 | .ValueGeneratedOnAdd() 23 | .HasColumnType("INTEGER"); 24 | 25 | b.Property("Description") 26 | .HasColumnType("TEXT"); 27 | 28 | b.HasKey("Id"); 29 | 30 | b.ToTable("Orders"); 31 | 32 | b.HasData( 33 | new 34 | { 35 | Id = 1, 36 | Description = "Shopping Spree" 37 | }, 38 | new 39 | { 40 | Id = 2, 41 | Description = "Holiday Shopping" 42 | }); 43 | }); 44 | 45 | modelBuilder.Entity("EnterprisePatterns.Entities.OrderLine", b => 46 | { 47 | b.Property("Id") 48 | .ValueGeneratedOnAdd() 49 | .HasColumnType("INTEGER"); 50 | 51 | b.Property("Amount") 52 | .HasColumnType("INTEGER"); 53 | 54 | b.Property("OrderId") 55 | .HasColumnType("INTEGER"); 56 | 57 | b.Property("Product") 58 | .IsRequired() 59 | .HasColumnType("TEXT"); 60 | 61 | b.HasKey("Id"); 62 | 63 | b.HasIndex("OrderId"); 64 | 65 | b.ToTable("OrderLines"); 66 | 67 | b.HasData( 68 | new 69 | { 70 | Id = 1, 71 | Amount = 1, 72 | OrderId = 1, 73 | Product = "Shoes" 74 | }, 75 | new 76 | { 77 | Id = 2, 78 | Amount = 2, 79 | OrderId = 1, 80 | Product = "Shirt" 81 | }, 82 | new 83 | { 84 | Id = 3, 85 | Amount = 1, 86 | OrderId = 1, 87 | Product = "Pants" 88 | }, 89 | new 90 | { 91 | Id = 4, 92 | Amount = 5, 93 | OrderId = 1, 94 | Product = "Socks" 95 | }, 96 | new 97 | { 98 | Id = 5, 99 | Amount = 1, 100 | OrderId = 2, 101 | Product = "Sunglasses" 102 | }, 103 | new 104 | { 105 | Id = 6, 106 | Amount = 2, 107 | OrderId = 2, 108 | Product = "Sunscreen" 109 | }); 110 | }); 111 | 112 | modelBuilder.Entity("EnterprisePatterns.Entities.OrderLine", b => 113 | { 114 | b.HasOne("EnterprisePatterns.Entities.Order", null) 115 | .WithMany("OrderLines") 116 | .HasForeignKey("OrderId") 117 | .OnDelete(DeleteBehavior.Cascade) 118 | .IsRequired(); 119 | }); 120 | 121 | modelBuilder.Entity("EnterprisePatterns.Entities.Order", b => 122 | { 123 | b.Navigation("OrderLines"); 124 | }); 125 | #pragma warning restore 612, 618 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Orders.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinDockx/CSharpDesignPatterns/274f0a0fc7c361914a15f2357d43979a53f3e68c/Finished sample/Enterprise Patterns/EnterprisePatterns/Orders.db -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Program.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.DbContexts; 2 | using EnterprisePatterns.DemoServices; 3 | using EnterprisePatterns.Entities; 4 | using EnterprisePatterns.Repositories; 5 | using EnterprisePatterns.Services; 6 | using EnterprisePatterns.UnitsOfWork; 7 | using Microsoft.EntityFrameworkCore; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | 12 | using IHost host = Host.CreateDefaultBuilder(args) 13 | .ConfigureServices((_, services) => 14 | { 15 | // register services for DI 16 | services.AddLogging(configure => configure.AddDebug().AddConsole()); 17 | 18 | services.AddDbContext(o => o.UseSqlite("Data Source=Orders.db;")); 19 | 20 | services.AddScoped(); 21 | services.AddScoped(); 22 | 23 | //services.AddScoped, GenericOrderRepository>(); 24 | services.AddScoped, GenericEFCoreRepository>(); 25 | services.AddScoped(); 26 | 27 | services.AddScoped(); 28 | 29 | }).Build(); 30 | 31 | 32 | // For demo purposes: overall catch-all to log any exception that might 33 | // happen to the console & wait for key input afterwards so you can easily 34 | // inspect the issue. 35 | try 36 | { 37 | var logger = host.Services.GetRequiredService>(); 38 | logger.LogInformation("Host created."); 39 | 40 | // Run a demo service 41 | //await host.Services.GetRequiredService().RunAsync(); 42 | await host.Services.GetRequiredService().RunAsync(); 43 | } 44 | catch (Exception generalException) 45 | { 46 | // log the exception 47 | var logger = host.Services.GetRequiredService>(); 48 | logger.LogError(generalException, 49 | "An exception happened while running the integration service."); 50 | } 51 | 52 | Console.ReadKey(); 53 | await host.RunAsync(); -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Repositories/GenericEFCoreRepository.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.DbContexts; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace EnterprisePatterns.Repositories; 5 | 6 | public class GenericEFCoreRepository : IRepository where T : class 7 | { 8 | private readonly OrderDbContext _dbContext; 9 | private DbSet _dbSet; 10 | 11 | public GenericEFCoreRepository(OrderDbContext dbContext) 12 | { 13 | _dbContext = dbContext; 14 | _dbSet = _dbContext.Set(); 15 | } 16 | 17 | public void Add(T entity) 18 | { 19 | _dbSet.Add(entity); 20 | } 21 | 22 | public void Delete(T entity) 23 | { 24 | _dbSet.Remove(entity); 25 | } 26 | 27 | public async Task Get(int id) 28 | { 29 | return await _dbSet.FindAsync(id); 30 | } 31 | 32 | public async Task> GetAll() 33 | { 34 | return await _dbSet.ToListAsync(); 35 | } 36 | 37 | public async Task SaveChanges() 38 | { 39 | await _dbContext.SaveChangesAsync(); 40 | } 41 | 42 | public void Update(T entity) 43 | { 44 | // no code required 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Repositories/GenericOrderRepository.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.DbContexts; 2 | using EnterprisePatterns.Entities; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace EnterprisePatterns.Repositories; 6 | 7 | public class GenericOrderRepository(OrderDbContext dbContext) : IRepository 8 | { 9 | private readonly OrderDbContext _dbContext = dbContext; 10 | 11 | public void Add(Order entity) 12 | { 13 | _dbContext.Orders.Add(entity); 14 | } 15 | 16 | public void Delete(Order entity) 17 | { 18 | _dbContext.Orders.Remove(entity); 19 | } 20 | 21 | public async Task Get(int id) 22 | { 23 | return await _dbContext.Orders.FindAsync(id); 24 | } 25 | 26 | public async Task> GetAll() 27 | { 28 | return await _dbContext.Orders.ToListAsync(); 29 | } 30 | 31 | public async Task SaveChanges() 32 | { 33 | await _dbContext.SaveChangesAsync(); 34 | } 35 | 36 | public void Update(Order entity) 37 | { 38 | // no code required in this implementation 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Repositories/IOrderLineRepository.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.Entities; 2 | 3 | namespace EnterprisePatterns.Repositories; 4 | 5 | public interface IOrderLineRepository : IRepository 6 | { 7 | Task> GetAllByOrderId(int orderId); 8 | } 9 | 10 | 11 | //public interface IOrderLineRepository 12 | //{ 13 | // Task Get(int id); 14 | // Task> GetAll(); 15 | // Task> GetAllByOrderId(int orderId); 16 | // void Add(OrderLine entity); 17 | // void Delete(OrderLine entity); 18 | // void Update(OrderLine entity); 19 | // Task SaveChanges(); 20 | 21 | //} 22 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Repositories/IRepositoryOfT.cs: -------------------------------------------------------------------------------- 1 | namespace EnterprisePatterns.Repositories; 2 | 3 | public interface IRepository 4 | { 5 | Task Get(int id); 6 | 7 | Task> GetAll(); 8 | void Add(T entity); 9 | 10 | void Delete(T entity); 11 | 12 | void Update(T entity); 13 | Task SaveChanges(); 14 | } 15 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/Repositories/OrderLineRepository.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.DbContexts; 2 | using EnterprisePatterns.Entities; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace EnterprisePatterns.Repositories; 6 | 7 | public class OrderLineRepository(OrderDbContext orderDbContext) : 8 | GenericEFCoreRepository(orderDbContext), IOrderLineRepository 9 | { 10 | private readonly OrderDbContext _orderDbContext = orderDbContext; 11 | 12 | public async Task> GetAllByOrderId(int orderId) 13 | { 14 | return await _orderDbContext.OrderLines 15 | .Where(o => o.OrderId == orderId).ToListAsync(); 16 | } 17 | } 18 | 19 | //public class OrderLineRepository(OrderDbContext orderDbContext) : IOrderLineRepository 20 | //{ 21 | // private readonly OrderDbContext _orderDbContext = orderDbContext; 22 | 23 | // public async Task Get(int id) 24 | // { 25 | // return await _orderDbContext.OrderLines 26 | // .FirstOrDefaultAsync(o => o.Id == id); 27 | // } 28 | 29 | // public async Task> GetAll() 30 | // { 31 | // return await _orderDbContext.OrderLines.ToListAsync(); 32 | // } 33 | 34 | // public async Task> GetAllByOrderId(int orderId) 35 | // { 36 | // return await _orderDbContext.OrderLines 37 | // .Where(o => o.OrderId == orderId).ToListAsync(); 38 | // } 39 | // public void Add(OrderLine entity) 40 | // { 41 | // _orderDbContext.OrderLines.Add(entity); 42 | // } 43 | // public void Update(OrderLine entity) 44 | // { 45 | // // no code here 46 | // } 47 | 48 | // public void Delete(OrderLine entity) 49 | // { 50 | // _orderDbContext.OrderLines.Remove(entity); 51 | // } 52 | 53 | // public async Task SaveChanges() 54 | // { 55 | // await _orderDbContext.SaveChangesAsync(); 56 | // } 57 | //} 58 | 59 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/UnitsOfWork/CreateOrderWithOrderLinesUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.DbContexts; 2 | using EnterprisePatterns.Entities; 3 | using EnterprisePatterns.Repositories; 4 | 5 | namespace EnterprisePatterns.UnitsOfWork; 6 | 7 | public class CreateOrderWithOrderLinesUnitOfWork(OrderDbContext orderDbContext, 8 | IRepository orderRepository, 9 | IOrderLineRepository orderLineRepository) 10 | : UnitOfWork(orderDbContext) 11 | { 12 | public IRepository OrderRepository { get; } = orderRepository; 13 | public IOrderLineRepository OrderLineRepository { get; } = orderLineRepository; 14 | } 15 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/UnitsOfWork/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace EnterprisePatterns.UnitsOfWork; 2 | 3 | public interface IUnitOfWork 4 | { 5 | Task CommitAsync(); 6 | void Rollback(); 7 | } 8 | -------------------------------------------------------------------------------- /Finished sample/Enterprise Patterns/EnterprisePatterns/UnitsOfWork/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 |  2 | using EnterprisePatterns.DbContexts; 3 | 4 | namespace EnterprisePatterns.UnitsOfWork; 5 | 6 | public abstract class UnitOfWork(OrderDbContext orderDbContext) : IUnitOfWork 7 | { 8 | private readonly OrderDbContext _orderDbContext = orderDbContext; 9 | 10 | public async Task CommitAsync() 11 | { 12 | await _orderDbContext.SaveChangesAsync(); 13 | } 14 | 15 | public void Rollback() 16 | { 17 | _orderDbContext.ChangeTracker.Clear(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/AbstractFactory/AbstractFactory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/AbstractFactory/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | /// 4 | /// AbstractFactory 5 | /// 6 | public interface IShoppingCartPurchaseFactory 7 | { 8 | IDiscountService CreateDiscountService(); 9 | IShippingCostsService CreateShippingCostsService(); 10 | } 11 | 12 | /// 13 | /// AbstractProduct 14 | /// 15 | public interface IDiscountService 16 | { 17 | int DiscountPercentage { get; } 18 | } 19 | 20 | /// 21 | /// ConcreteProduct 22 | /// 23 | public class BelgiumDiscountService : IDiscountService 24 | { 25 | public int DiscountPercentage => 20; 26 | } 27 | 28 | /// 29 | /// ConcreteProduct 30 | /// 31 | public class FranceDiscountService : IDiscountService 32 | { 33 | public int DiscountPercentage => 10; 34 | } 35 | 36 | 37 | /// 38 | /// AbstractProduct 39 | /// 40 | public interface IShippingCostsService 41 | { 42 | decimal ShippingCosts { get; } 43 | } 44 | 45 | /// 46 | /// ConcreteProduct 47 | /// 48 | public class BelgiumShippingCostsService : IShippingCostsService 49 | { 50 | public decimal ShippingCosts => 20; 51 | } 52 | 53 | /// 54 | /// ConcreteProduct 55 | /// 56 | public class FranceShippingCostsService : IShippingCostsService 57 | { 58 | public decimal ShippingCosts => 25; 59 | } 60 | 61 | 62 | /// 63 | /// ConcreteFactory 64 | /// 65 | public class BelgiumShoppingCartPurchaseFactory : IShoppingCartPurchaseFactory 66 | { 67 | public IDiscountService CreateDiscountService() 68 | { 69 | return new BelgiumDiscountService(); 70 | } 71 | 72 | public IShippingCostsService CreateShippingCostsService() 73 | { 74 | return new BelgiumShippingCostsService(); 75 | } 76 | } 77 | 78 | /// 79 | /// ConcreteFactory 80 | /// 81 | public class FranceShoppingCartPurchaseFactory : IShoppingCartPurchaseFactory 82 | { 83 | public IDiscountService CreateDiscountService() 84 | { 85 | return new FranceDiscountService(); 86 | } 87 | 88 | public IShippingCostsService CreateShippingCostsService() 89 | { 90 | return new FranceShippingCostsService(); 91 | } 92 | } 93 | 94 | /// 95 | /// Client class 96 | /// 97 | public class ShoppingCart 98 | { 99 | private readonly IDiscountService _discountService; 100 | private readonly IShippingCostsService _shippingCostsService; 101 | private int _orderCosts; 102 | 103 | // Constructor 104 | public ShoppingCart(IShoppingCartPurchaseFactory factory) 105 | { 106 | _discountService = factory.CreateDiscountService(); 107 | _shippingCostsService = factory.CreateShippingCostsService(); 108 | // assume that the total cost of all the items we ordered = 200 euro 109 | _orderCosts = 200; 110 | } 111 | 112 | public void CalculateCosts() 113 | { 114 | Console.WriteLine($"Total costs = {_orderCosts - (_orderCosts / 100 * _discountService.DiscountPercentage) + _shippingCostsService.ShippingCosts}"); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/AbstractFactory/Program.cs: -------------------------------------------------------------------------------- 1 | using AbstractFactory; 2 | 3 | Console.Title = "Abstract Factory"; 4 | 5 | var belgiumShoppingCartPurchaseFactory = new BelgiumShoppingCartPurchaseFactory(); 6 | var shoppingCartForBelgium = new ShoppingCart(belgiumShoppingCartPurchaseFactory); 7 | shoppingCartForBelgium.CalculateCosts(); 8 | 9 | var franceShoppingCartPurchaseFactory = new FranceShoppingCartPurchaseFactory(); 10 | var shoppingCartForFrance = new ShoppingCart(franceShoppingCartPurchaseFactory); 11 | shoppingCartForFrance.CalculateCosts(); 12 | 13 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Adapter/Adapter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Adapter/ClassAdapterImplementation.cs: -------------------------------------------------------------------------------- 1 | namespace ClassAdapter 2 | { 3 | public class CityFromExternalSystem 4 | { 5 | public string Name { get; private set; } 6 | public string NickName { get; private set; } 7 | public int Inhabitants { get; private set; } 8 | 9 | public CityFromExternalSystem(string name, string nickName, int inhabitants) 10 | { 11 | Name = name; 12 | NickName = nickName; 13 | Inhabitants = inhabitants; 14 | } 15 | } 16 | 17 | /// 18 | /// Adaptee 19 | /// 20 | public class ExternalSystem 21 | { 22 | public CityFromExternalSystem GetCity() 23 | { 24 | return new CityFromExternalSystem("Antwerp", "'t Stad", 500000); 25 | } 26 | } 27 | 28 | public class City 29 | { 30 | public string FullName { get; private set; } 31 | public long Inhabitants { get; private set; } 32 | 33 | public City(string fullName, long inhabitants) 34 | { 35 | FullName = fullName; 36 | Inhabitants = inhabitants; 37 | } 38 | } 39 | 40 | /// 41 | /// Target 42 | /// 43 | public interface ICityAdapter 44 | { 45 | City GetCity(); 46 | } 47 | 48 | /// 49 | /// Adapter 50 | /// 51 | public class CityAdapter : ExternalSystem, ICityAdapter 52 | { 53 | public City GetCity() 54 | { 55 | // call into the external system 56 | var cityFromExternalSystem = base.GetCity(); 57 | 58 | // adapt the CityFromExternalCity to a City 59 | return new City( 60 | $"{cityFromExternalSystem.Name} - {cityFromExternalSystem.NickName}" 61 | , cityFromExternalSystem.Inhabitants); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Adapter/ObjectAdapterImplementation.cs: -------------------------------------------------------------------------------- 1 | namespace Adapter 2 | { 3 | public class CityFromExternalSystem 4 | { 5 | public string Name { get; private set; } 6 | public string NickName { get; private set; } 7 | public int Inhabitants { get; private set; } 8 | 9 | public CityFromExternalSystem(string name, string nickName, int inhabitants) 10 | { 11 | Name = name; 12 | NickName = nickName; 13 | Inhabitants = inhabitants; 14 | } 15 | } 16 | 17 | /// 18 | /// Adaptee 19 | /// 20 | public class ExternalSystem 21 | { 22 | public CityFromExternalSystem GetCity() 23 | { 24 | return new CityFromExternalSystem("Antwerp", "'t Stad", 500000); 25 | } 26 | } 27 | 28 | public class City 29 | { 30 | public string FullName { get; private set; } 31 | public long Inhabitants { get; private set; } 32 | 33 | public City(string fullName, long inhabitants) 34 | { 35 | FullName = fullName; 36 | Inhabitants = inhabitants; 37 | } 38 | } 39 | 40 | /// 41 | /// Target 42 | /// 43 | public interface ICityAdapter 44 | { 45 | City GetCity(); 46 | } 47 | 48 | /// 49 | /// Adapter 50 | /// 51 | public class CityAdapter : ICityAdapter 52 | { 53 | public ExternalSystem ExternalSystem { get; private set; } = new(); 54 | 55 | public City GetCity() 56 | { 57 | // call into the external system 58 | var cityFromExternalSystem = ExternalSystem.GetCity(); 59 | 60 | // adapt the CityFromExternalCity to a City 61 | return new City( 62 | $"{cityFromExternalSystem.Name} - {cityFromExternalSystem.NickName}" 63 | , cityFromExternalSystem.Inhabitants); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Adapter/Program.cs: -------------------------------------------------------------------------------- 1 | using ClassAdapter; 2 | 3 | Console.Title = "Adapter"; 4 | 5 | // object adapter example 6 | ICityAdapter adapter = new CityAdapter(); 7 | var city = adapter.GetCity(); 8 | 9 | Console.WriteLine($"{city.FullName}, {city.Inhabitants}"); 10 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Bridge/Bridge.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Bridge/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Bridge 2 | { 3 | /// 4 | /// Abstraction 5 | /// 6 | public abstract class Menu 7 | { 8 | public readonly ICoupon _coupon = null!; 9 | public abstract int CalculatePrice(); 10 | public Menu(ICoupon coupon) 11 | { 12 | _coupon = coupon; 13 | } 14 | } 15 | 16 | /// 17 | /// RefinedAbstraction 18 | /// 19 | public class VegetarianMenu : Menu 20 | { 21 | public VegetarianMenu(ICoupon coupon) : base(coupon) 22 | { 23 | } 24 | public override int CalculatePrice() 25 | { 26 | return 20 - _coupon.CouponValue; 27 | } 28 | } 29 | 30 | /// 31 | /// RefinedAbstraction 32 | /// 33 | public class MeatBasedMenu : Menu 34 | { 35 | public MeatBasedMenu(ICoupon coupon) : base(coupon) 36 | { 37 | } 38 | public override int CalculatePrice() 39 | { 40 | return 30 - _coupon.CouponValue; 41 | } 42 | } 43 | 44 | /// 45 | /// Implementor 46 | /// 47 | public interface ICoupon 48 | { 49 | int CouponValue { get; } 50 | } 51 | 52 | /// 53 | /// ConcreteImplementor 54 | /// 55 | public class NoCoupon : ICoupon 56 | { 57 | public int CouponValue { get => 0; } 58 | } 59 | 60 | /// 61 | /// ConcreteImplementor 62 | /// 63 | public class OneEuroCoupon : ICoupon 64 | { 65 | public int CouponValue { get => 1; } 66 | } 67 | 68 | /// 69 | /// ConcreteImplementor 70 | /// 71 | public class TwoEuroCoupon : ICoupon 72 | { 73 | public int CouponValue { get => 2; } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Bridge/Program.cs: -------------------------------------------------------------------------------- 1 | using Bridge; 2 | 3 | Console.Title = "Bridge"; 4 | 5 | var noCoupon = new NoCoupon(); 6 | var oneEuroCoupon = new OneEuroCoupon(); 7 | 8 | var meatBasedMenu = new MeatBasedMenu(noCoupon); 9 | Console.WriteLine($"Meat based menu, no coupon: {meatBasedMenu.CalculatePrice()} euro."); 10 | 11 | meatBasedMenu = new MeatBasedMenu(oneEuroCoupon); 12 | Console.WriteLine($"Meat based menu, one euro coupon: {meatBasedMenu.CalculatePrice()} euro."); 13 | 14 | var vegetarianMenu = new VegetarianMenu(noCoupon); 15 | Console.WriteLine($"Vegetarian menu, no coupon: {vegetarianMenu.CalculatePrice()} euro."); 16 | 17 | vegetarianMenu = new VegetarianMenu(oneEuroCoupon); 18 | Console.WriteLine($"Vegetarian menu, one euro coupon: {vegetarianMenu.CalculatePrice()} euro."); 19 | 20 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Builder/Builder.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Builder/Implementation.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace BuilderPattern 4 | { 5 | /// 6 | /// Product 7 | /// 8 | public class Car 9 | { 10 | private readonly List _parts = new(); 11 | private readonly string _carType; 12 | 13 | public Car(string carType) 14 | { 15 | _carType = carType; 16 | } 17 | 18 | public void AddPart(string part) 19 | { 20 | _parts.Add(part); 21 | } 22 | 23 | public override string ToString() 24 | { 25 | var sb = new StringBuilder(); 26 | foreach (string part in _parts) 27 | { 28 | sb.Append($"Car of type {_carType} has part {part}. "); 29 | } 30 | 31 | return sb.ToString(); 32 | } 33 | } 34 | 35 | /// 36 | /// Builder 37 | /// 38 | public abstract class CarBuilder 39 | { 40 | public Car Car { get; private set; } 41 | 42 | public CarBuilder(string carType) 43 | { 44 | Car = new Car(carType); 45 | } 46 | 47 | public abstract void BuildEngine(); 48 | public abstract void BuildFrame(); 49 | } 50 | 51 | /// 52 | /// ConcreteBuilder1 class 53 | /// 54 | public class MiniBuilder : CarBuilder 55 | { 56 | public MiniBuilder() 57 | : base("Mini") 58 | { 59 | } 60 | 61 | public override void BuildEngine() 62 | { 63 | Car.AddPart("'not a V8'"); 64 | } 65 | 66 | public override void BuildFrame() 67 | { 68 | Car.AddPart("'3-door with stripes'"); 69 | } 70 | } 71 | 72 | /// 73 | /// ConcreteBuilder2 class 74 | /// 75 | public class BMWBuilder : CarBuilder 76 | { 77 | // Invoke base class constructor 78 | public BMWBuilder() 79 | : base("BMW") 80 | { 81 | } 82 | 83 | public override void BuildEngine() 84 | { 85 | Car.AddPart("'a fancy V8 engine'"); 86 | } 87 | 88 | public override void BuildFrame() 89 | { 90 | Car.AddPart("'5-door with metallic finish'"); 91 | } 92 | } 93 | 94 | /// 95 | /// Director 96 | /// 97 | public class Garage 98 | { 99 | private CarBuilder? _builder; 100 | 101 | public Garage() 102 | { 103 | } 104 | 105 | public void Construct(CarBuilder builder) 106 | { 107 | _builder = builder; 108 | 109 | _builder.BuildEngine(); 110 | _builder.BuildFrame(); 111 | } 112 | 113 | public void Show() 114 | { 115 | Console.WriteLine(_builder?.Car.ToString()); 116 | } 117 | 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Builder/Program.cs: -------------------------------------------------------------------------------- 1 | using BuilderPattern; 2 | 3 | Console.Title = "Builder"; 4 | 5 | var garage = new Garage(); 6 | 7 | var miniBuilder = new MiniBuilder(); 8 | var bmwBuilder = new BMWBuilder(); 9 | 10 | garage.Construct(miniBuilder); 11 | Console.WriteLine(miniBuilder.Car.ToString()); 12 | // or: 13 | garage.Show(); 14 | 15 | garage.Construct(bmwBuilder); 16 | Console.WriteLine(bmwBuilder.Car.ToString()); 17 | // or: 18 | garage.Show(); 19 | 20 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/ChainOfResponsibility/ChainOfResponsibility.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/ChainOfResponsibility/Implementation.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace ChainOfResponsibility 4 | { 5 | public class Document 6 | { 7 | public string Title { get; set; } 8 | public DateTimeOffset LastModified { get; set; } 9 | public bool ApprovedByLitigation { get; set; } 10 | public bool ApprovedByManagement { get; set; } 11 | 12 | public Document( 13 | string title, 14 | DateTimeOffset lastModified, 15 | bool approvedByLitigation, 16 | bool approvedByManagement) 17 | { 18 | Title = title; 19 | LastModified = lastModified; 20 | ApprovedByLitigation = approvedByLitigation; 21 | ApprovedByManagement = approvedByManagement; 22 | } 23 | } 24 | 25 | 26 | /// 27 | /// Handler 28 | /// 29 | public interface IHandler where T : class 30 | { 31 | IHandler SetSuccessor(IHandler successor); 32 | void Handle(T request); 33 | } 34 | 35 | /// 36 | /// ConcreteHandler 37 | /// 38 | public class DocumentTitleHandler : IHandler 39 | { 40 | private IHandler? _successor; 41 | 42 | public void Handle(Document document) 43 | { 44 | if (document.Title == string.Empty) 45 | { 46 | // validation doesn't check out 47 | throw new ValidationException( 48 | new ValidationResult( 49 | "Title must be filled out", 50 | new List() { "Title" }), null, null); 51 | } 52 | 53 | // go to the next handler 54 | _successor?.Handle(document); 55 | } 56 | 57 | public IHandler SetSuccessor(IHandler successor) 58 | { 59 | _successor = successor; 60 | return successor; 61 | } 62 | } 63 | 64 | /// 65 | /// ConcreteHandler 66 | /// 67 | public class DocumentLastModifiedHandler : IHandler 68 | { 69 | private IHandler? _successor; 70 | 71 | public void Handle(Document document) 72 | { 73 | if (document.LastModified < DateTime.UtcNow.AddDays(-30)) 74 | { 75 | // validation doesn't check out 76 | throw new ValidationException( 77 | new ValidationResult( 78 | "Document must be modified in the last 30 days", 79 | new List() { "LastModified" }), null, null); 80 | } 81 | 82 | // go to the next handler 83 | _successor?.Handle(document); 84 | } 85 | 86 | public IHandler SetSuccessor(IHandler successor) 87 | { 88 | _successor = successor; 89 | return successor; 90 | } 91 | } 92 | 93 | /// 94 | /// ConcreteHandler 95 | /// 96 | public class DocumentApprovedByLitigationHandler : IHandler 97 | { 98 | private IHandler? _successor; 99 | 100 | public void Handle(Document document) 101 | { 102 | if (!document.ApprovedByLitigation) 103 | { 104 | // validation doesn't check out 105 | throw new ValidationException( 106 | new ValidationResult( 107 | "Document must be approved by litigation", 108 | new List() { "ApprovedByLitigation" }), null, null); 109 | } 110 | 111 | // go to the next handler 112 | _successor?.Handle(document); 113 | } 114 | 115 | public IHandler SetSuccessor(IHandler successor) 116 | { 117 | _successor = successor; 118 | return successor; 119 | } 120 | } 121 | 122 | /// 123 | /// ConcreteHandler 124 | /// 125 | public class DocumentApprovedByManagementHandler : IHandler 126 | { 127 | private IHandler? _successor; 128 | 129 | public void Handle(Document document) 130 | { 131 | if (!document.ApprovedByManagement) 132 | { 133 | // validation doesn't check out 134 | throw new ValidationException( 135 | new ValidationResult( 136 | "Document must be approved by management", 137 | new List() { "ApprovedByManagement" }), null, null); 138 | } 139 | 140 | // go to the next handler 141 | _successor?.Handle(document); 142 | } 143 | 144 | public IHandler SetSuccessor(IHandler successor) 145 | { 146 | _successor = successor; 147 | return successor; 148 | } 149 | } 150 | 151 | } -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/ChainOfResponsibility/Program.cs: -------------------------------------------------------------------------------- 1 | using ChainOfResponsibility; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | Console.Title = "Chain of Responsibility"; 5 | 6 | var validDocument = new Document("How to Avoid Java Development", 7 | DateTimeOffset.UtcNow, true, true); 8 | var invalidDocument = new Document("How to Avoid Java Development", 9 | DateTimeOffset.UtcNow, false, true); 10 | 11 | var documentHandlerChain = new DocumentTitleHandler(); 12 | documentHandlerChain 13 | .SetSuccessor(new DocumentLastModifiedHandler()) 14 | .SetSuccessor(new DocumentApprovedByLitigationHandler()) 15 | .SetSuccessor(new DocumentApprovedByManagementHandler()); 16 | 17 | try 18 | { 19 | documentHandlerChain.Handle(validDocument); 20 | Console.WriteLine("Valid document is valid."); 21 | documentHandlerChain.Handle(invalidDocument); 22 | Console.WriteLine("Invalid document is valid."); 23 | } 24 | catch (ValidationException validationException) 25 | { 26 | Console.WriteLine(validationException.Message); 27 | } 28 | 29 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Command/Command.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Command/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Command 2 | { 3 | public class Employee 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | 8 | public Employee(int id, string name) 9 | { 10 | Id = id; 11 | Name = name; 12 | } 13 | } 14 | public class Manager : Employee 15 | { 16 | public List Employees = new(); 17 | public Manager(int id, string name) 18 | : base(id, name) 19 | { 20 | } 21 | } 22 | 23 | /// 24 | /// Receiver (interface) 25 | /// 26 | public interface IEmployeeManagerRepository 27 | { 28 | void AddEmployee(int managerId, Employee employee); 29 | void RemoveEmployee(int managerId, Employee employee); 30 | bool HasEmployee(int managerId, int employeeId); 31 | void WriteDataStore(); 32 | } 33 | 34 | /// 35 | /// Receiver (implementation) 36 | /// 37 | public class EmployeeManagerRepository : IEmployeeManagerRepository 38 | { 39 | // for demo purposes, use an in-memory datastore as a fake "manager list" 40 | private List _managers = new List() 41 | { new Manager(1, "Katie"), new Manager(2, "Geoff") }; 42 | 43 | public void AddEmployee(int managerId, Employee employee) 44 | { 45 | // in real-life, add additional input & error checks 46 | _managers.First(m => m.Id == managerId).Employees.Add(employee); 47 | } 48 | 49 | public void RemoveEmployee(int managerId, Employee employee) 50 | { 51 | // in real-life, add additional input & error checks 52 | _managers.First(m => m.Id == managerId).Employees.Remove(employee); 53 | } 54 | 55 | public bool HasEmployee(int managerId, int employeeId) 56 | { 57 | // in real-life, add additional input & error checks 58 | return _managers.First(m => m.Id == managerId) 59 | .Employees.Any(e => e.Id == employeeId); 60 | } 61 | 62 | 63 | /// 64 | /// For demo purposes, write out the data store to the console window 65 | /// 66 | public void WriteDataStore() 67 | { 68 | foreach (var manager in _managers) 69 | { 70 | Console.WriteLine($"Manager {manager.Id}, {manager.Name}"); 71 | if (manager.Employees.Any()) 72 | { 73 | foreach (var employee in manager.Employees) 74 | { 75 | Console.WriteLine($"\t Employee {employee.Id}, {employee.Name}"); 76 | } 77 | } 78 | else 79 | { 80 | Console.WriteLine($"\t No employees."); 81 | } 82 | } 83 | } 84 | } 85 | 86 | /// 87 | /// Command 88 | /// 89 | public interface ICommand 90 | { 91 | void Execute(); 92 | bool CanExecute(); 93 | void Undo(); 94 | } 95 | 96 | /// 97 | /// ConcreteCommand 98 | /// 99 | public class AddEmployeeToManagerList : ICommand 100 | { 101 | private readonly IEmployeeManagerRepository _employeeManagerRepository; 102 | private readonly int _managerId; 103 | private readonly Employee? _employee; 104 | 105 | public AddEmployeeToManagerList( 106 | IEmployeeManagerRepository employeeManagerRepository, 107 | int managerId, 108 | Employee? employee) 109 | { 110 | _employeeManagerRepository = employeeManagerRepository; 111 | _managerId = managerId; 112 | _employee = employee; 113 | } 114 | 115 | public bool CanExecute() 116 | { 117 | // employee shouldn't be null 118 | if (_employee == null) 119 | { 120 | return false; 121 | } 122 | 123 | // employee shouldn't be on the manager's list already 124 | if (_employeeManagerRepository.HasEmployee(_managerId, _employee.Id)) 125 | { 126 | return false; 127 | } 128 | 129 | // other potential rule(s): ensure that an employee can only be added to 130 | // one manager's list at the same time, etc. 131 | return true; 132 | } 133 | 134 | public void Execute() 135 | { 136 | if (_employee == null) 137 | { 138 | return; 139 | } 140 | _employeeManagerRepository.AddEmployee(_managerId, _employee); 141 | } 142 | 143 | public void Undo() 144 | { 145 | if (_employee == null) 146 | { 147 | return; 148 | } 149 | 150 | _employeeManagerRepository.RemoveEmployee(_managerId, _employee); 151 | } 152 | } 153 | 154 | /// 155 | /// Invoker 156 | /// 157 | public class CommandManager 158 | { 159 | private readonly Stack _commands = new Stack(); 160 | 161 | public void Invoke(ICommand command) 162 | { 163 | if (command.CanExecute()) 164 | { 165 | command.Execute(); 166 | _commands.Push(command); 167 | } 168 | } 169 | 170 | public void Undo() 171 | { 172 | if (_commands.Any()) 173 | { 174 | _commands.Pop()?.Undo(); 175 | } 176 | } 177 | 178 | public void UndoAll() 179 | { 180 | while (_commands.Any()) 181 | { 182 | _commands.Pop()?.Undo(); 183 | } 184 | } 185 | } 186 | 187 | } -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Command/Program.cs: -------------------------------------------------------------------------------- 1 | using Command; 2 | 3 | Console.Title = "Command"; 4 | 5 | CommandManager commandManager = new(); 6 | IEmployeeManagerRepository repository = new EmployeeManagerRepository(); 7 | 8 | commandManager.Invoke( 9 | new AddEmployeeToManagerList(repository, 1, new Employee(111, "Kevin"))); 10 | repository.WriteDataStore(); 11 | 12 | commandManager.Undo(); 13 | repository.WriteDataStore(); 14 | 15 | commandManager.Invoke( 16 | new AddEmployeeToManagerList(repository, 1, new Employee(222, "Clara"))); 17 | repository.WriteDataStore(); 18 | 19 | commandManager.Invoke( 20 | new AddEmployeeToManagerList(repository, 2, new Employee(333, "Tom"))); 21 | repository.WriteDataStore(); 22 | 23 | // try adding the same employee again 24 | commandManager.Invoke( 25 | new AddEmployeeToManagerList(repository, 2, new Employee(333, "Tom"))); 26 | repository.WriteDataStore(); 27 | 28 | commandManager.UndoAll(); 29 | repository.WriteDataStore(); 30 | 31 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Composite/Composite.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Composite/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Composite 2 | { 3 | /// 4 | /// Component 5 | /// 6 | public abstract class FileSystemItem 7 | { 8 | public string Name { get; set; } 9 | 10 | public abstract long GetSize(); 11 | 12 | public FileSystemItem(string name) 13 | { 14 | Name = name; 15 | } 16 | } 17 | 18 | /// 19 | /// Leaf 20 | /// 21 | public class File : FileSystemItem 22 | { 23 | private long _size; 24 | public File(string name, long size) : base(name) 25 | { 26 | _size = size; 27 | } 28 | 29 | public override long GetSize() 30 | { 31 | return _size; 32 | } 33 | } 34 | 35 | /// 36 | /// Composite 37 | /// 38 | public class Directory : FileSystemItem 39 | { 40 | private List _fileSystemItems = new List(); 41 | 42 | private long _size; 43 | public Directory(string name, long size) : base(name) 44 | { 45 | _size = size; 46 | } 47 | 48 | public void Add(FileSystemItem itemToAdd) 49 | { 50 | _fileSystemItems.Add(itemToAdd); 51 | } 52 | 53 | public void Remove(FileSystemItem itemToRemove) 54 | { 55 | _fileSystemItems.Remove(itemToRemove); 56 | } 57 | 58 | public override long GetSize() 59 | { 60 | var treeSize = _size; 61 | foreach (var fileSystemItem in _fileSystemItems) 62 | { 63 | treeSize += fileSystemItem.GetSize(); 64 | } 65 | return treeSize; 66 | } 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Composite/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Composite"; 2 | 3 | var root = new Composite.Directory("root", 0); 4 | var topLevelFile = new Composite.File("toplevel.txt", 100); 5 | var topLevelDirectory1 = new Composite.Directory("topleveldirectory1", 4); 6 | var topLevelDirectory2 = new Composite.Directory("topleveldirectory2", 4); 7 | 8 | root.Add(topLevelFile); 9 | root.Add(topLevelDirectory1); 10 | root.Add(topLevelDirectory2); 11 | 12 | var subLevelFile1 = new Composite.File("sublevel1.txt", 200); 13 | var subLevelFile2 = new Composite.File("sublevel2.txt", 150); 14 | 15 | topLevelDirectory2.Add(subLevelFile1); 16 | topLevelDirectory2.Add(subLevelFile2); 17 | 18 | Console.WriteLine($"Size of topLevelDirectory1: {topLevelDirectory1.GetSize()}"); 19 | Console.WriteLine($"Size of topLevelDirectory2: {topLevelDirectory2.GetSize()}"); 20 | Console.WriteLine($"Size of root: {root.GetSize()}"); 21 | 22 | Console.ReadKey(); 23 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Decorator/Decorator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Decorator/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Decorator 2 | { 3 | /// 4 | /// Component (as interface) 5 | /// 6 | public interface IMailService 7 | { 8 | bool SendMail(string message); 9 | } 10 | 11 | /// 12 | /// ConcreteComponent1 13 | /// 14 | public class CloudMailService : IMailService 15 | { 16 | public bool SendMail(string message) 17 | { 18 | Console.WriteLine($"Message \"{message}\" " + 19 | $"sent via {nameof(CloudMailService)}."); 20 | return true; 21 | } 22 | } 23 | 24 | /// 25 | /// ConcreteComponent2 26 | /// 27 | public class OnPremiseMailService : IMailService 28 | { 29 | public bool SendMail(string message) 30 | { 31 | Console.WriteLine($"Message \"{message}\" " + 32 | $"sent via {nameof(OnPremiseMailService)}."); 33 | return true; 34 | } 35 | } 36 | 37 | /// 38 | /// Decorator 39 | /// 40 | public abstract class MailServiceDecoratorBase : IMailService 41 | { 42 | private readonly IMailService _mailService; 43 | public MailServiceDecoratorBase(IMailService mailService) 44 | { 45 | _mailService = mailService; 46 | } 47 | 48 | public virtual bool SendMail(string message) 49 | { 50 | return _mailService.SendMail(message); 51 | } 52 | } 53 | 54 | /// 55 | /// ConcreteDecorator1 56 | /// 57 | public class StatisticsDecorator : MailServiceDecoratorBase 58 | { 59 | public StatisticsDecorator(IMailService mailService) 60 | : base(mailService) 61 | { 62 | } 63 | 64 | public override bool SendMail(string message) 65 | { 66 | // Fake collecting statistics 67 | Console.WriteLine($"Collecting statistics in {nameof(StatisticsDecorator)}."); 68 | return base.SendMail(message); 69 | } 70 | } 71 | 72 | /// 73 | /// ConcreteDecorator2 74 | /// 75 | public class MessageDatabaseDecorator : MailServiceDecoratorBase 76 | { 77 | /// 78 | /// A list of sent messages - a "fake" database 79 | /// 80 | public List SentMessages { get; private set; } = new List(); 81 | 82 | public MessageDatabaseDecorator(IMailService mailService) 83 | : base(mailService) 84 | { } 85 | 86 | public override bool SendMail(string message) 87 | { 88 | if (base.SendMail(message)) 89 | { 90 | // store sent message 91 | SentMessages.Add(message); 92 | return true; 93 | }; 94 | 95 | return false; 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Decorator/Program.cs: -------------------------------------------------------------------------------- 1 | using Decorator; 2 | 3 | Console.Title = "Decorator"; 4 | 5 | // instantiate mail services 6 | var cloudMailService = new CloudMailService(); 7 | cloudMailService.SendMail("Hi there."); 8 | 9 | var onPremiseMailService = new OnPremiseMailService(); 10 | onPremiseMailService.SendMail("Hi there."); 11 | 12 | // add behavior 13 | var statisticsDecorator = new StatisticsDecorator(cloudMailService); 14 | statisticsDecorator.SendMail($"Hi there via {nameof(StatisticsDecorator)} wrapper."); 15 | 16 | // add behavior 17 | var messageDatabaseDecorator = new MessageDatabaseDecorator(onPremiseMailService); 18 | messageDatabaseDecorator.SendMail( 19 | $"Hi there via {nameof(MessageDatabaseDecorator)} wrapper, message 1."); 20 | messageDatabaseDecorator.SendMail( 21 | $"Hi there via {nameof(MessageDatabaseDecorator)} wrapper, message 2."); 22 | 23 | foreach (var message in messageDatabaseDecorator.SentMessages) 24 | { 25 | Console.WriteLine($"Stored message: \"{message}\""); 26 | } 27 | 28 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/DesignPatterns.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "A - Creational", "A - Creational", "{B0F237CE-4C3B-467B-AB3F-7FED90583AB9}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01 - Singleton", "01 - Singleton", "{482F9D07-99EE-4BF6-975F-295E89ECBCF9}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02 - Factory Method", "02 - Factory Method", "{E8783E99-05CD-4F71-B166-0FC1B90FC2C2}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03 - Abstract Factory", "03 - Abstract Factory", "{4763D927-A2E7-4322-8134-7D24DDD947F1}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FactoryMethod", "FactoryMethod\FactoryMethod.csproj", "{1CDF5F40-3F42-4339-9702-8BC7EA4D9DEE}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AbstractFactory", "AbstractFactory\AbstractFactory.csproj", "{51C54352-1897-4749-BD39-660613C2235B}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "04 -Builder", "04 -Builder", "{21752809-82A5-4212-8444-166F656129FC}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Builder", "Builder\Builder.csproj", "{8552B7D4-35B3-4916-8D5D-D0479802BA35}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "05 - Prototype", "05 - Prototype", "{E0164F8E-AB94-4D3F-879A-6DB5F19FE84B}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prototype", "Prototype\Prototype.csproj", "{1C69FD15-5FD4-49AF-B31F-F2A3407364EC}" 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "B - Structural", "B - Structural", "{BA4514FF-AA9E-402C-B074-5A16FF91FD6E}" 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "06 - Adapter", "06 - Adapter", "{60637BBF-C1E5-47EF-9994-7870B51913A3}" 29 | EndProject 30 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Adapter", "Adapter\Adapter.csproj", "{4E31DC34-C2C1-45C4-BC2D-21AFF98BDEB5}" 31 | EndProject 32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07 - Bridge", "07 - Bridge", "{7A473B8C-26AF-4CAE-B3E5-E0F091F7F4D2}" 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bridge", "Bridge\Bridge.csproj", "{75E6E304-74F2-4D88-8589-4366B1A7684D}" 35 | EndProject 36 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "08 - Decorator", "08 - Decorator", "{46719157-346D-4BCE-B34D-55D2B5F2E93A}" 37 | EndProject 38 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Decorator", "Decorator\Decorator.csproj", "{12768030-04EF-4B1D-A0E9-7C96F6B73DBE}" 39 | EndProject 40 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "09 - Composite", "09 - Composite", "{E262BBA8-94AE-495C-85E6-FA54BA6A90A2}" 41 | EndProject 42 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Composite", "Composite\Composite.csproj", "{898A0BFF-C8CA-451D-BA92-DACABABFF53E}" 43 | EndProject 44 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "10 - Facade", "10 - Facade", "{BA606C2E-5A34-4290-AC23-58E5482CD300}" 45 | EndProject 46 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Facade", "Facade\Facade.csproj", "{2F4804B4-98BC-4DF6-8362-E96AA344A29A}" 47 | EndProject 48 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "11 - Proxy", "11 - Proxy", "{59DA4BDF-CFF5-4F06-8878-67F7900F37D7}" 49 | EndProject 50 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Proxy", "Proxy\Proxy.csproj", "{3BDFB38E-00D4-434E-8985-5D6661931089}" 51 | EndProject 52 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "12 - Flyweight", "12 - Flyweight", "{449D2089-E1E9-4097-8996-5E6E3D80B24C}" 53 | EndProject 54 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flyweight", "Flyweight\Flyweight.csproj", "{ACCB5262-A3F5-4A77-864F-F27A3A3B5259}" 55 | EndProject 56 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singleton", "Singleton\Singleton.csproj", "{347B70DD-B108-4DB3-B36E-01DC8ADB9C2C}" 57 | EndProject 58 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "C - Behavioral", "C - Behavioral", "{EE357574-412D-4C9D-A167-5002DE164362}" 59 | EndProject 60 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "15 - Command", "15 - Command", "{7736AF2A-2B94-42FB-A793-5020973687C8}" 61 | EndProject 62 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Command", "Command\Command.csproj", "{BA3B3232-7FC9-43AF-9AFE-E4F5211B4F1D}" 63 | EndProject 64 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "16 - Memento", "16 - Memento", "{C5BDE3AF-1138-4E87-8810-611687FFA522}" 65 | EndProject 66 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Memento", "Memento\Memento.csproj", "{176C219A-5E5A-4710-B360-E09DBC80690D}" 67 | EndProject 68 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "20 - State", "20 - State", "{0CC41725-98B1-4F2E-ACB9-3A0F47F65E97}" 69 | EndProject 70 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "State", "State\State.csproj", "{9D29533E-7C7F-4B72-9331-81EA8A9957D7}" 71 | EndProject 72 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "14 - Strategy", "14 - Strategy", "{62068BDC-394F-4A1E-825B-C7DE137900BB}" 73 | EndProject 74 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strategy", "Strategy\Strategy.csproj", "{6E87E3A0-ECDB-47DD-B9E8-D9E31A27D812}" 75 | EndProject 76 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "17 - Mediator", "17 - Mediator", "{F71B79ED-777C-4707-9D38-CF15A476C55B}" 77 | EndProject 78 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mediator", "Mediator\Mediator.csproj", "{913A71BA-D3BC-41F1-BA44-888AB8658630}" 79 | EndProject 80 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "18 - Chain of Responsibility", "18 - Chain of Responsibility", "{71A327E1-BA13-466C-B1A6-FA4551E8A670}" 81 | EndProject 82 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChainOfResponsibility", "ChainOfResponsibility\ChainOfResponsibility.csproj", "{8768B99D-DC81-4951-BA46-8F1D10631C69}" 83 | EndProject 84 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "19 - Observer", "19 - Observer", "{162BE83E-9D99-494B-8464-BB0BEE0BCC81}" 85 | EndProject 86 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Observer", "Observer\Observer.csproj", "{8FA532ED-DB9D-47E7-AD53-31881953993A}" 87 | EndProject 88 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "21 - Iterator", "21 - Iterator", "{818D8FDD-1C97-48D8-AEEF-A7F193AA9187}" 89 | EndProject 90 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Iterator", "Iterator\Iterator.csproj", "{D4FEF27B-1C98-495E-93EF-1AC36ED810AB}" 91 | EndProject 92 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "22 - Visitor", "22 - Visitor", "{173BC868-E7AE-43AE-A05E-FD9BEEA980C7}" 93 | EndProject 94 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Visitor", "Visitor\Visitor.csproj", "{E3A37190-E73D-4F91-A249-9B5D18CF83AF}" 95 | EndProject 96 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "13 - Template Method", "13 - Template Method", "{1622583F-64EB-4F30-8D38-E897FF8FA140}" 97 | EndProject 98 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TemplateMethod", "TemplateMethod\TemplateMethod.csproj", "{97CB88F2-868A-457A-90EB-89F55B9CBBF4}" 99 | EndProject 100 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "23 - Interpreter", "23 - Interpreter", "{FBA7CDD7-9BF5-4272-9414-C9A4DB6D5BDB}" 101 | EndProject 102 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Interpreter", "Interpreter\Interpreter.csproj", "{39C0A16A-B9F1-444B-9D5D-F46BBB84F746}" 103 | EndProject 104 | Global 105 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 106 | Debug|Any CPU = Debug|Any CPU 107 | Release|Any CPU = Release|Any CPU 108 | EndGlobalSection 109 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 110 | {1CDF5F40-3F42-4339-9702-8BC7EA4D9DEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 111 | {1CDF5F40-3F42-4339-9702-8BC7EA4D9DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU 112 | {1CDF5F40-3F42-4339-9702-8BC7EA4D9DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU 113 | {1CDF5F40-3F42-4339-9702-8BC7EA4D9DEE}.Release|Any CPU.Build.0 = Release|Any CPU 114 | {51C54352-1897-4749-BD39-660613C2235B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 115 | {51C54352-1897-4749-BD39-660613C2235B}.Debug|Any CPU.Build.0 = Debug|Any CPU 116 | {51C54352-1897-4749-BD39-660613C2235B}.Release|Any CPU.ActiveCfg = Release|Any CPU 117 | {51C54352-1897-4749-BD39-660613C2235B}.Release|Any CPU.Build.0 = Release|Any CPU 118 | {8552B7D4-35B3-4916-8D5D-D0479802BA35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 119 | {8552B7D4-35B3-4916-8D5D-D0479802BA35}.Debug|Any CPU.Build.0 = Debug|Any CPU 120 | {8552B7D4-35B3-4916-8D5D-D0479802BA35}.Release|Any CPU.ActiveCfg = Release|Any CPU 121 | {8552B7D4-35B3-4916-8D5D-D0479802BA35}.Release|Any CPU.Build.0 = Release|Any CPU 122 | {1C69FD15-5FD4-49AF-B31F-F2A3407364EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 123 | {1C69FD15-5FD4-49AF-B31F-F2A3407364EC}.Debug|Any CPU.Build.0 = Debug|Any CPU 124 | {1C69FD15-5FD4-49AF-B31F-F2A3407364EC}.Release|Any CPU.ActiveCfg = Release|Any CPU 125 | {1C69FD15-5FD4-49AF-B31F-F2A3407364EC}.Release|Any CPU.Build.0 = Release|Any CPU 126 | {4E31DC34-C2C1-45C4-BC2D-21AFF98BDEB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 127 | {4E31DC34-C2C1-45C4-BC2D-21AFF98BDEB5}.Debug|Any CPU.Build.0 = Debug|Any CPU 128 | {4E31DC34-C2C1-45C4-BC2D-21AFF98BDEB5}.Release|Any CPU.ActiveCfg = Release|Any CPU 129 | {4E31DC34-C2C1-45C4-BC2D-21AFF98BDEB5}.Release|Any CPU.Build.0 = Release|Any CPU 130 | {75E6E304-74F2-4D88-8589-4366B1A7684D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 131 | {75E6E304-74F2-4D88-8589-4366B1A7684D}.Debug|Any CPU.Build.0 = Debug|Any CPU 132 | {75E6E304-74F2-4D88-8589-4366B1A7684D}.Release|Any CPU.ActiveCfg = Release|Any CPU 133 | {75E6E304-74F2-4D88-8589-4366B1A7684D}.Release|Any CPU.Build.0 = Release|Any CPU 134 | {12768030-04EF-4B1D-A0E9-7C96F6B73DBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 135 | {12768030-04EF-4B1D-A0E9-7C96F6B73DBE}.Debug|Any CPU.Build.0 = Debug|Any CPU 136 | {12768030-04EF-4B1D-A0E9-7C96F6B73DBE}.Release|Any CPU.ActiveCfg = Release|Any CPU 137 | {12768030-04EF-4B1D-A0E9-7C96F6B73DBE}.Release|Any CPU.Build.0 = Release|Any CPU 138 | {898A0BFF-C8CA-451D-BA92-DACABABFF53E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 139 | {898A0BFF-C8CA-451D-BA92-DACABABFF53E}.Debug|Any CPU.Build.0 = Debug|Any CPU 140 | {898A0BFF-C8CA-451D-BA92-DACABABFF53E}.Release|Any CPU.ActiveCfg = Release|Any CPU 141 | {898A0BFF-C8CA-451D-BA92-DACABABFF53E}.Release|Any CPU.Build.0 = Release|Any CPU 142 | {2F4804B4-98BC-4DF6-8362-E96AA344A29A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 143 | {2F4804B4-98BC-4DF6-8362-E96AA344A29A}.Debug|Any CPU.Build.0 = Debug|Any CPU 144 | {2F4804B4-98BC-4DF6-8362-E96AA344A29A}.Release|Any CPU.ActiveCfg = Release|Any CPU 145 | {2F4804B4-98BC-4DF6-8362-E96AA344A29A}.Release|Any CPU.Build.0 = Release|Any CPU 146 | {3BDFB38E-00D4-434E-8985-5D6661931089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 147 | {3BDFB38E-00D4-434E-8985-5D6661931089}.Debug|Any CPU.Build.0 = Debug|Any CPU 148 | {3BDFB38E-00D4-434E-8985-5D6661931089}.Release|Any CPU.ActiveCfg = Release|Any CPU 149 | {3BDFB38E-00D4-434E-8985-5D6661931089}.Release|Any CPU.Build.0 = Release|Any CPU 150 | {ACCB5262-A3F5-4A77-864F-F27A3A3B5259}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 151 | {ACCB5262-A3F5-4A77-864F-F27A3A3B5259}.Debug|Any CPU.Build.0 = Debug|Any CPU 152 | {ACCB5262-A3F5-4A77-864F-F27A3A3B5259}.Release|Any CPU.ActiveCfg = Release|Any CPU 153 | {ACCB5262-A3F5-4A77-864F-F27A3A3B5259}.Release|Any CPU.Build.0 = Release|Any CPU 154 | {347B70DD-B108-4DB3-B36E-01DC8ADB9C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 155 | {347B70DD-B108-4DB3-B36E-01DC8ADB9C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU 156 | {347B70DD-B108-4DB3-B36E-01DC8ADB9C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU 157 | {347B70DD-B108-4DB3-B36E-01DC8ADB9C2C}.Release|Any CPU.Build.0 = Release|Any CPU 158 | {BA3B3232-7FC9-43AF-9AFE-E4F5211B4F1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 159 | {BA3B3232-7FC9-43AF-9AFE-E4F5211B4F1D}.Debug|Any CPU.Build.0 = Debug|Any CPU 160 | {BA3B3232-7FC9-43AF-9AFE-E4F5211B4F1D}.Release|Any CPU.ActiveCfg = Release|Any CPU 161 | {BA3B3232-7FC9-43AF-9AFE-E4F5211B4F1D}.Release|Any CPU.Build.0 = Release|Any CPU 162 | {176C219A-5E5A-4710-B360-E09DBC80690D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 163 | {176C219A-5E5A-4710-B360-E09DBC80690D}.Debug|Any CPU.Build.0 = Debug|Any CPU 164 | {176C219A-5E5A-4710-B360-E09DBC80690D}.Release|Any CPU.ActiveCfg = Release|Any CPU 165 | {176C219A-5E5A-4710-B360-E09DBC80690D}.Release|Any CPU.Build.0 = Release|Any CPU 166 | {9D29533E-7C7F-4B72-9331-81EA8A9957D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 167 | {9D29533E-7C7F-4B72-9331-81EA8A9957D7}.Debug|Any CPU.Build.0 = Debug|Any CPU 168 | {9D29533E-7C7F-4B72-9331-81EA8A9957D7}.Release|Any CPU.ActiveCfg = Release|Any CPU 169 | {9D29533E-7C7F-4B72-9331-81EA8A9957D7}.Release|Any CPU.Build.0 = Release|Any CPU 170 | {6E87E3A0-ECDB-47DD-B9E8-D9E31A27D812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 171 | {6E87E3A0-ECDB-47DD-B9E8-D9E31A27D812}.Debug|Any CPU.Build.0 = Debug|Any CPU 172 | {6E87E3A0-ECDB-47DD-B9E8-D9E31A27D812}.Release|Any CPU.ActiveCfg = Release|Any CPU 173 | {6E87E3A0-ECDB-47DD-B9E8-D9E31A27D812}.Release|Any CPU.Build.0 = Release|Any CPU 174 | {913A71BA-D3BC-41F1-BA44-888AB8658630}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 175 | {913A71BA-D3BC-41F1-BA44-888AB8658630}.Debug|Any CPU.Build.0 = Debug|Any CPU 176 | {913A71BA-D3BC-41F1-BA44-888AB8658630}.Release|Any CPU.ActiveCfg = Release|Any CPU 177 | {913A71BA-D3BC-41F1-BA44-888AB8658630}.Release|Any CPU.Build.0 = Release|Any CPU 178 | {8768B99D-DC81-4951-BA46-8F1D10631C69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 179 | {8768B99D-DC81-4951-BA46-8F1D10631C69}.Debug|Any CPU.Build.0 = Debug|Any CPU 180 | {8768B99D-DC81-4951-BA46-8F1D10631C69}.Release|Any CPU.ActiveCfg = Release|Any CPU 181 | {8768B99D-DC81-4951-BA46-8F1D10631C69}.Release|Any CPU.Build.0 = Release|Any CPU 182 | {8FA532ED-DB9D-47E7-AD53-31881953993A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 183 | {8FA532ED-DB9D-47E7-AD53-31881953993A}.Debug|Any CPU.Build.0 = Debug|Any CPU 184 | {8FA532ED-DB9D-47E7-AD53-31881953993A}.Release|Any CPU.ActiveCfg = Release|Any CPU 185 | {8FA532ED-DB9D-47E7-AD53-31881953993A}.Release|Any CPU.Build.0 = Release|Any CPU 186 | {D4FEF27B-1C98-495E-93EF-1AC36ED810AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 187 | {D4FEF27B-1C98-495E-93EF-1AC36ED810AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 188 | {D4FEF27B-1C98-495E-93EF-1AC36ED810AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 189 | {D4FEF27B-1C98-495E-93EF-1AC36ED810AB}.Release|Any CPU.Build.0 = Release|Any CPU 190 | {E3A37190-E73D-4F91-A249-9B5D18CF83AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 191 | {E3A37190-E73D-4F91-A249-9B5D18CF83AF}.Debug|Any CPU.Build.0 = Debug|Any CPU 192 | {E3A37190-E73D-4F91-A249-9B5D18CF83AF}.Release|Any CPU.ActiveCfg = Release|Any CPU 193 | {E3A37190-E73D-4F91-A249-9B5D18CF83AF}.Release|Any CPU.Build.0 = Release|Any CPU 194 | {97CB88F2-868A-457A-90EB-89F55B9CBBF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 195 | {97CB88F2-868A-457A-90EB-89F55B9CBBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU 196 | {97CB88F2-868A-457A-90EB-89F55B9CBBF4}.Release|Any CPU.ActiveCfg = Release|Any CPU 197 | {97CB88F2-868A-457A-90EB-89F55B9CBBF4}.Release|Any CPU.Build.0 = Release|Any CPU 198 | {39C0A16A-B9F1-444B-9D5D-F46BBB84F746}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 199 | {39C0A16A-B9F1-444B-9D5D-F46BBB84F746}.Debug|Any CPU.Build.0 = Debug|Any CPU 200 | {39C0A16A-B9F1-444B-9D5D-F46BBB84F746}.Release|Any CPU.ActiveCfg = Release|Any CPU 201 | {39C0A16A-B9F1-444B-9D5D-F46BBB84F746}.Release|Any CPU.Build.0 = Release|Any CPU 202 | EndGlobalSection 203 | GlobalSection(SolutionProperties) = preSolution 204 | HideSolutionNode = FALSE 205 | EndGlobalSection 206 | GlobalSection(NestedProjects) = preSolution 207 | {482F9D07-99EE-4BF6-975F-295E89ECBCF9} = {B0F237CE-4C3B-467B-AB3F-7FED90583AB9} 208 | {E8783E99-05CD-4F71-B166-0FC1B90FC2C2} = {B0F237CE-4C3B-467B-AB3F-7FED90583AB9} 209 | {4763D927-A2E7-4322-8134-7D24DDD947F1} = {B0F237CE-4C3B-467B-AB3F-7FED90583AB9} 210 | {1CDF5F40-3F42-4339-9702-8BC7EA4D9DEE} = {E8783E99-05CD-4F71-B166-0FC1B90FC2C2} 211 | {51C54352-1897-4749-BD39-660613C2235B} = {4763D927-A2E7-4322-8134-7D24DDD947F1} 212 | {21752809-82A5-4212-8444-166F656129FC} = {B0F237CE-4C3B-467B-AB3F-7FED90583AB9} 213 | {8552B7D4-35B3-4916-8D5D-D0479802BA35} = {21752809-82A5-4212-8444-166F656129FC} 214 | {E0164F8E-AB94-4D3F-879A-6DB5F19FE84B} = {B0F237CE-4C3B-467B-AB3F-7FED90583AB9} 215 | {1C69FD15-5FD4-49AF-B31F-F2A3407364EC} = {E0164F8E-AB94-4D3F-879A-6DB5F19FE84B} 216 | {60637BBF-C1E5-47EF-9994-7870B51913A3} = {BA4514FF-AA9E-402C-B074-5A16FF91FD6E} 217 | {4E31DC34-C2C1-45C4-BC2D-21AFF98BDEB5} = {60637BBF-C1E5-47EF-9994-7870B51913A3} 218 | {7A473B8C-26AF-4CAE-B3E5-E0F091F7F4D2} = {BA4514FF-AA9E-402C-B074-5A16FF91FD6E} 219 | {75E6E304-74F2-4D88-8589-4366B1A7684D} = {7A473B8C-26AF-4CAE-B3E5-E0F091F7F4D2} 220 | {46719157-346D-4BCE-B34D-55D2B5F2E93A} = {BA4514FF-AA9E-402C-B074-5A16FF91FD6E} 221 | {12768030-04EF-4B1D-A0E9-7C96F6B73DBE} = {46719157-346D-4BCE-B34D-55D2B5F2E93A} 222 | {E262BBA8-94AE-495C-85E6-FA54BA6A90A2} = {BA4514FF-AA9E-402C-B074-5A16FF91FD6E} 223 | {898A0BFF-C8CA-451D-BA92-DACABABFF53E} = {E262BBA8-94AE-495C-85E6-FA54BA6A90A2} 224 | {BA606C2E-5A34-4290-AC23-58E5482CD300} = {BA4514FF-AA9E-402C-B074-5A16FF91FD6E} 225 | {2F4804B4-98BC-4DF6-8362-E96AA344A29A} = {BA606C2E-5A34-4290-AC23-58E5482CD300} 226 | {59DA4BDF-CFF5-4F06-8878-67F7900F37D7} = {BA4514FF-AA9E-402C-B074-5A16FF91FD6E} 227 | {3BDFB38E-00D4-434E-8985-5D6661931089} = {59DA4BDF-CFF5-4F06-8878-67F7900F37D7} 228 | {449D2089-E1E9-4097-8996-5E6E3D80B24C} = {BA4514FF-AA9E-402C-B074-5A16FF91FD6E} 229 | {ACCB5262-A3F5-4A77-864F-F27A3A3B5259} = {449D2089-E1E9-4097-8996-5E6E3D80B24C} 230 | {347B70DD-B108-4DB3-B36E-01DC8ADB9C2C} = {482F9D07-99EE-4BF6-975F-295E89ECBCF9} 231 | {7736AF2A-2B94-42FB-A793-5020973687C8} = {EE357574-412D-4C9D-A167-5002DE164362} 232 | {BA3B3232-7FC9-43AF-9AFE-E4F5211B4F1D} = {7736AF2A-2B94-42FB-A793-5020973687C8} 233 | {C5BDE3AF-1138-4E87-8810-611687FFA522} = {EE357574-412D-4C9D-A167-5002DE164362} 234 | {176C219A-5E5A-4710-B360-E09DBC80690D} = {C5BDE3AF-1138-4E87-8810-611687FFA522} 235 | {0CC41725-98B1-4F2E-ACB9-3A0F47F65E97} = {EE357574-412D-4C9D-A167-5002DE164362} 236 | {9D29533E-7C7F-4B72-9331-81EA8A9957D7} = {0CC41725-98B1-4F2E-ACB9-3A0F47F65E97} 237 | {62068BDC-394F-4A1E-825B-C7DE137900BB} = {EE357574-412D-4C9D-A167-5002DE164362} 238 | {6E87E3A0-ECDB-47DD-B9E8-D9E31A27D812} = {62068BDC-394F-4A1E-825B-C7DE137900BB} 239 | {F71B79ED-777C-4707-9D38-CF15A476C55B} = {EE357574-412D-4C9D-A167-5002DE164362} 240 | {913A71BA-D3BC-41F1-BA44-888AB8658630} = {F71B79ED-777C-4707-9D38-CF15A476C55B} 241 | {71A327E1-BA13-466C-B1A6-FA4551E8A670} = {EE357574-412D-4C9D-A167-5002DE164362} 242 | {8768B99D-DC81-4951-BA46-8F1D10631C69} = {71A327E1-BA13-466C-B1A6-FA4551E8A670} 243 | {162BE83E-9D99-494B-8464-BB0BEE0BCC81} = {EE357574-412D-4C9D-A167-5002DE164362} 244 | {8FA532ED-DB9D-47E7-AD53-31881953993A} = {162BE83E-9D99-494B-8464-BB0BEE0BCC81} 245 | {818D8FDD-1C97-48D8-AEEF-A7F193AA9187} = {EE357574-412D-4C9D-A167-5002DE164362} 246 | {D4FEF27B-1C98-495E-93EF-1AC36ED810AB} = {818D8FDD-1C97-48D8-AEEF-A7F193AA9187} 247 | {173BC868-E7AE-43AE-A05E-FD9BEEA980C7} = {EE357574-412D-4C9D-A167-5002DE164362} 248 | {E3A37190-E73D-4F91-A249-9B5D18CF83AF} = {173BC868-E7AE-43AE-A05E-FD9BEEA980C7} 249 | {1622583F-64EB-4F30-8D38-E897FF8FA140} = {EE357574-412D-4C9D-A167-5002DE164362} 250 | {97CB88F2-868A-457A-90EB-89F55B9CBBF4} = {1622583F-64EB-4F30-8D38-E897FF8FA140} 251 | {FBA7CDD7-9BF5-4272-9414-C9A4DB6D5BDB} = {EE357574-412D-4C9D-A167-5002DE164362} 252 | {39C0A16A-B9F1-444B-9D5D-F46BBB84F746} = {FBA7CDD7-9BF5-4272-9414-C9A4DB6D5BDB} 253 | EndGlobalSection 254 | GlobalSection(ExtensibilityGlobals) = postSolution 255 | SolutionGuid = {F84E1DA5-656F-4C72-9FBA-4A07AD3EE35E} 256 | EndGlobalSection 257 | EndGlobal 258 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Facade/Facade.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Facade/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Facade 2 | { 3 | /// 4 | /// Subsystem class 5 | /// 6 | public class OrderService 7 | { 8 | public bool HasEnoughOrders(int customerId) 9 | { 10 | // does the customer have enough orders? 11 | // fake calculation for demo purposes 12 | return (customerId > 5); 13 | } 14 | } 15 | 16 | /// 17 | /// Subsystem class 18 | /// 19 | public class CustomerDiscountBaseService 20 | { 21 | public double CalculateDiscountBase(int customerId) 22 | { 23 | // fake calculation for demo purposes 24 | return (customerId > 8) ? 10 : 20; 25 | } 26 | } 27 | 28 | /// 29 | /// Subsystem class 30 | /// 31 | public class DayOfTheWeekFactorService 32 | { 33 | public double CalculateDayOfTheWeekFactor() 34 | { 35 | // fake calculation for demo purposes 36 | switch (DateTime.UtcNow.DayOfWeek) 37 | { 38 | case DayOfWeek.Saturday: 39 | case DayOfWeek.Sunday: 40 | return 0.8; 41 | default: 42 | return 1.2; 43 | } 44 | } 45 | } 46 | 47 | /// 48 | /// Facade 49 | /// 50 | public class DiscountFacade 51 | { 52 | private readonly OrderService _orderService = new(); 53 | private readonly CustomerDiscountBaseService _customerDiscountBaseService = new(); 54 | private readonly DayOfTheWeekFactorService _dayOfTheWeekFactorService = new(); 55 | 56 | public double CalculateDiscountPercentage(int customerId) 57 | { 58 | if (!_orderService.HasEnoughOrders(customerId)) 59 | { 60 | return 0; 61 | } 62 | 63 | return _customerDiscountBaseService.CalculateDiscountBase(customerId) 64 | * _dayOfTheWeekFactorService.CalculateDayOfTheWeekFactor(); 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Facade/Program.cs: -------------------------------------------------------------------------------- 1 | using Facade; 2 | 3 | Console.Title = "Facade"; 4 | 5 | var facade = new DiscountFacade(); 6 | Console.WriteLine($"Discount percentage for customer with id 1: " + 7 | $"{facade.CalculateDiscountPercentage(1)}"); 8 | Console.WriteLine($"Discount percentage for customer with id 10: " + 9 | $"{facade.CalculateDiscountPercentage(10)}"); 10 | 11 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/FactoryMethod/FactoryMethod.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/FactoryMethod/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace FactoryMethod 2 | { 3 | /// 4 | /// Product 5 | /// 6 | public abstract class DiscountService 7 | { 8 | public abstract int DiscountPercentage { get; } 9 | public override string ToString() => GetType().Name; 10 | 11 | } 12 | 13 | /// 14 | /// ConcreteProduct 15 | /// 16 | public class CountryDiscountService : DiscountService 17 | { 18 | private readonly string _countryIdentifier; 19 | 20 | public CountryDiscountService(string countryIdentifier) 21 | { 22 | _countryIdentifier = countryIdentifier; 23 | } 24 | 25 | public override int DiscountPercentage 26 | { 27 | get 28 | { 29 | switch (_countryIdentifier) 30 | { 31 | // if you're from Belgium, you get a better discount :) 32 | case "BE": 33 | return 20; 34 | default: 35 | return 10; 36 | } 37 | } 38 | } 39 | } 40 | 41 | /// 42 | /// ConcreteProduct 43 | /// 44 | public class CodeDiscountService : DiscountService 45 | { 46 | private readonly Guid _code; 47 | 48 | public CodeDiscountService(Guid code) 49 | { 50 | _code = code; 51 | } 52 | 53 | public override int DiscountPercentage 54 | { 55 | // each code returns the same fixed percentage, but a code is only 56 | // valid once - include a check to so whether the code's been used before 57 | // ... 58 | get => 15; 59 | } 60 | } 61 | 62 | /// 63 | /// Creator 64 | /// 65 | public abstract class DiscountFactory 66 | { 67 | public abstract DiscountService CreateDiscountService(); 68 | } 69 | 70 | /// 71 | /// ConcreteCreator 72 | /// 73 | public class CountryDiscountFactory : DiscountFactory 74 | { 75 | private readonly string _countryIdentifier; 76 | public CountryDiscountFactory(string countryIdentifier) 77 | { 78 | _countryIdentifier = countryIdentifier; 79 | } 80 | 81 | public override DiscountService CreateDiscountService() 82 | { 83 | return new CountryDiscountService(_countryIdentifier); 84 | } 85 | } 86 | 87 | /// 88 | /// ConcreteCreator 89 | /// 90 | public class CodeDiscountFactory : DiscountFactory 91 | { 92 | private readonly Guid _code; 93 | 94 | public CodeDiscountFactory(Guid code) 95 | { 96 | _code = code; 97 | } 98 | public override DiscountService CreateDiscountService() 99 | { 100 | return new CodeDiscountService(_code); 101 | } 102 | } 103 | 104 | 105 | 106 | } 107 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/FactoryMethod/Program.cs: -------------------------------------------------------------------------------- 1 | using FactoryMethod; 2 | 3 | Console.Title = "Factory Method"; 4 | 5 | var factories = new List { 6 | new CodeDiscountFactory(Guid.NewGuid()), 7 | new CountryDiscountFactory("BE") }; 8 | 9 | foreach (var factory in factories) 10 | { 11 | var discountService = factory.CreateDiscountService(); 12 | Console.WriteLine($"Percentage {discountService.DiscountPercentage} " + 13 | $"coming from {discountService}"); 14 | } 15 | 16 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Flyweight/Flyweight.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Flyweight/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Flyweight 2 | { 3 | /// 4 | /// Flyweight 5 | /// 6 | public interface ICharacter 7 | { 8 | void Draw(string fontFamily, int fontSize); 9 | } 10 | 11 | /// 12 | /// Concrete Flyweight 13 | /// 14 | public class CharacterA : ICharacter 15 | { 16 | private char _actualCharacter = 'a'; 17 | private string _fontFamily = string.Empty; 18 | private int _fontSize; 19 | 20 | public void Draw(string fontFamily, int fontSize) 21 | { 22 | _fontFamily = fontFamily; 23 | _fontSize = fontSize; 24 | Console.WriteLine($"Drawing {_actualCharacter}, {_fontFamily} {_fontSize}"); 25 | } 26 | } 27 | 28 | /// 29 | /// Concrete Flyweight 30 | /// 31 | public class CharacterB : ICharacter 32 | { 33 | private char _actualCharacter = 'b'; 34 | private string _fontFamily = string.Empty; 35 | private int _fontSize; 36 | 37 | public void Draw(string fontFamily, int fontSize) 38 | { 39 | _fontFamily = fontFamily; 40 | _fontSize = fontSize; 41 | Console.WriteLine($"Drawing {_actualCharacter}, {_fontFamily} {_fontSize}"); 42 | } 43 | } 44 | 45 | /// 46 | /// FlyweightFactory 47 | /// 48 | public class CharacterFactory 49 | { 50 | private readonly Dictionary _characters = new(); 51 | 52 | public ICharacter? GetCharacter(char characterIdentifier) 53 | { 54 | // Does the character dictionary contain the one we need? 55 | if (_characters.ContainsKey(characterIdentifier)) 56 | { 57 | Console.WriteLine("Character reuse"); 58 | return _characters[characterIdentifier]; 59 | } 60 | 61 | // The character isn't in the dictionary. 62 | // Create it, store it, return it. 63 | Console.WriteLine("Character construction"); 64 | switch (characterIdentifier) 65 | { 66 | case 'a': 67 | _characters[characterIdentifier] = new CharacterA(); 68 | return _characters[characterIdentifier]; 69 | case 'b': 70 | _characters[characterIdentifier] = new CharacterB(); 71 | return _characters[characterIdentifier]; 72 | // and so on... 73 | } 74 | 75 | return null; 76 | } 77 | 78 | public ICharacter CreateParagraph(List characters, int location) 79 | { 80 | return new Paragraph(characters, location); 81 | } 82 | } 83 | 84 | 85 | /// 86 | /// Unshared Concrete Flyweight 87 | /// 88 | public class Paragraph : ICharacter 89 | { 90 | private int _location; 91 | private List _characters = new(); 92 | 93 | public Paragraph(List characters, int location) 94 | { 95 | _characters = characters; 96 | _location = location; 97 | } 98 | 99 | public void Draw(string fontFamily, int fontSize) 100 | { 101 | Console.WriteLine($"Drawing in paragraph at location {_location}"); 102 | foreach (var character in _characters) 103 | { 104 | character.Draw(fontFamily, fontSize); 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Flyweight/Program.cs: -------------------------------------------------------------------------------- 1 | using Flyweight; 2 | 3 | Console.Title = "Flyweight"; 4 | 5 | var aBunchOfCharacters = "abba"; 6 | 7 | var characterFactory = new CharacterFactory(); 8 | 9 | // Get the flyweight(s) 10 | var characterObject = characterFactory.GetCharacter(aBunchOfCharacters[0]); 11 | // Pass through extrinsic state 12 | characterObject?.Draw("Arial", 12); 13 | 14 | characterObject = characterFactory.GetCharacter(aBunchOfCharacters[1]); 15 | characterObject?.Draw("Trebuchet MS", 14); 16 | 17 | characterObject = characterFactory.GetCharacter(aBunchOfCharacters[2]); 18 | characterObject?.Draw("Times New Roman", 16); 19 | 20 | characterObject = characterFactory.GetCharacter(aBunchOfCharacters[3]); 21 | characterObject?.Draw("Comic Sans", 18); 22 | 23 | // create unshared concrete flyweight (paragraph) 24 | var paragraph = characterFactory.CreateParagraph( 25 | new List() { characterObject }, 1); 26 | 27 | // draw the paragraph 28 | paragraph.Draw("Lucinda", 12); 29 | 30 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Interpreter/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Interpreter 2 | { 3 | 4 | /// 5 | /// Context 6 | /// 7 | public class RomanContext 8 | { 9 | public int Input { get; set; } 10 | public string Output { get; set; } = string.Empty; 11 | public RomanContext(int input) 12 | { 13 | Input = input; 14 | } 15 | } 16 | 17 | /// 18 | /// AbstractExpression 19 | /// 20 | public abstract class RomanExpression 21 | { 22 | public abstract void Interpret(RomanContext value); 23 | } 24 | 25 | // 9 = IX 26 | // 8 = VIII 27 | // 7 = VII 28 | // 6 = VI 29 | // 5 = V 30 | // 4 = IV 31 | // 3 = III 32 | // 2 = II 33 | // 1 = I 34 | 35 | // simplified - each combination is reachable with subtraction and these 4: 36 | // 9 = IX 37 | // 5 = V 38 | // 4 = IV 39 | // 1 = I 40 | 41 | /// 42 | /// TerminalExpression 43 | /// 44 | public class RomanOneExpression : RomanExpression 45 | { 46 | public override void Interpret(RomanContext value) 47 | { 48 | while ((value.Input - 9) >= 0) 49 | { 50 | value.Output += "IX"; 51 | value.Input -= 9; 52 | } 53 | 54 | while ((value.Input - 5) >= 0) 55 | { 56 | value.Output += "V"; 57 | value.Input -= 5; 58 | } 59 | 60 | while ((value.Input - 4) >= 0) 61 | { 62 | value.Output += "IV"; 63 | value.Input -= 4; 64 | } 65 | 66 | while ((value.Input - 1) >= 0) 67 | { 68 | value.Output += "I"; 69 | value.Input -= 1; 70 | } 71 | 72 | } 73 | } 74 | 75 | 76 | // 90 = XC 77 | // 80 = LIII 78 | // 70 = LII 79 | // 60 = LX 80 | // 50 = L 81 | // 40 = XL 82 | // 30 = XXX 83 | // 20 = XX 84 | // 10 = X 85 | 86 | // simplified - each combination is reachable with substraction and these 4: 87 | // 90 = XC 88 | // 50 = L 89 | // 40 = XL 90 | // 10 = X 91 | 92 | /// 93 | /// TerminalExpression 94 | /// 95 | public class RomanTenExpression : RomanExpression 96 | { 97 | public override void Interpret(RomanContext value) 98 | { 99 | while ((value.Input - 90) >= 0) 100 | { 101 | value.Output += "XC"; 102 | value.Input -= 90; 103 | } 104 | 105 | while ((value.Input - 50) >= 0) 106 | { 107 | value.Output += "L"; 108 | value.Input -= 50; 109 | } 110 | 111 | while ((value.Input - 40) >= 0) 112 | { 113 | value.Output += "XL"; 114 | value.Input -= 40; 115 | } 116 | 117 | while ((value.Input - 10) >= 0) 118 | { 119 | value.Output += "X"; 120 | value.Input -= 10; 121 | } 122 | } 123 | } 124 | 125 | // 900 = CM 126 | // 800 = DCCC 127 | // 700 = DCC 128 | // 600 = DC 129 | // 500 = D 130 | // 400 = CD 131 | // 300 = CCC 132 | // 200 = CC 133 | // 100 = C 134 | 135 | // simplified - each combination is reachable with substraction and these 4: 136 | // 900 = CM 137 | // 500 = D 138 | // 400 = CD 139 | // 100 = C 140 | 141 | /// 142 | /// TerminalExpression 143 | /// 144 | public class RomanHunderdExpression : RomanExpression 145 | { 146 | public override void Interpret(RomanContext value) 147 | { 148 | while ((value.Input - 900) >= 0) 149 | { 150 | value.Output += "CM"; 151 | value.Input -= 900; 152 | } 153 | 154 | while ((value.Input - 500) >= 0) 155 | { 156 | value.Output += "D"; 157 | value.Input -= 500; 158 | } 159 | 160 | while ((value.Input - 400) >= 0) 161 | { 162 | value.Output += "CD"; 163 | value.Input -= 400; 164 | } 165 | 166 | while ((value.Input - 100) >= 0) 167 | { 168 | value.Output += "C"; 169 | value.Input -= 100; 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Interpreter/Interpreter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Interpreter/Program.cs: -------------------------------------------------------------------------------- 1 | using Interpreter; 2 | 3 | Console.Title = "Interpreter"; 4 | 5 | var expressions = new List 6 | { 7 | new RomanHunderdExpression(), 8 | new RomanTenExpression(), 9 | new RomanOneExpression(), 10 | }; 11 | 12 | var context = new RomanContext(5); 13 | foreach (var expression in expressions) 14 | { 15 | expression.Interpret(context); 16 | } 17 | Console.WriteLine($"Translating Arabic numerals to Roman numerals: 5 = {context.Output}"); 18 | 19 | context = new RomanContext(81); 20 | foreach (var expression in expressions) 21 | { 22 | expression.Interpret(context); 23 | } 24 | Console.WriteLine($"Translating Arabic numerals to Roman numerals: 81 = {context.Output}"); 25 | 26 | context = new RomanContext(733); 27 | foreach (var expression in expressions) 28 | { 29 | expression.Interpret(context); 30 | } 31 | Console.WriteLine($"Translating Arabic numerals to Roman numerals: 733 = {context.Output}"); 32 | 33 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Iterator/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Iterator 2 | { 3 | public class Person 4 | { 5 | public string Name { get; set; } 6 | public string Country { get; set; } 7 | 8 | public Person(string name, string country) 9 | { 10 | Name = name; 11 | Country = country; 12 | } 13 | } 14 | 15 | /// 16 | /// Iterator 17 | /// 18 | public interface IPeopleIterator 19 | { 20 | Person First(); 21 | Person Next(); 22 | bool IsDone { get; } 23 | Person CurrentItem { get; } 24 | } 25 | 26 | /// 27 | /// Aggregate 28 | /// 29 | public interface IPeopleCollection 30 | { 31 | IPeopleIterator CreateIterator(); 32 | } 33 | 34 | /// 35 | /// ConcreteAggregate 36 | /// 37 | public class PeopleCollection : List, IPeopleCollection 38 | { 39 | public IPeopleIterator CreateIterator() 40 | { 41 | return new PeopleIterator(this); 42 | } 43 | } 44 | 45 | /// 46 | /// ConcreteIterator 47 | /// 48 | public class PeopleIterator : IPeopleIterator 49 | { 50 | private PeopleCollection _peopleCollection; 51 | private int _current = 0; 52 | 53 | public PeopleIterator(PeopleCollection collection) 54 | { 55 | _peopleCollection = collection; 56 | } 57 | 58 | public bool IsDone 59 | { 60 | get { return _current >= _peopleCollection.Count; } 61 | } 62 | 63 | 64 | public Person CurrentItem 65 | { 66 | get 67 | { 68 | return _peopleCollection 69 | .OrderBy(p => p.Name).ToList()[_current]; 70 | } 71 | } 72 | 73 | 74 | public Person First() 75 | { 76 | _current = 0; 77 | return _peopleCollection 78 | .OrderBy(p => p.Name).ToList()[_current]; 79 | } 80 | 81 | public Person Next() 82 | { 83 | _current++; 84 | if (!IsDone) 85 | { 86 | return _peopleCollection 87 | .OrderBy(p => p.Name).ToList()[_current]; 88 | } 89 | else 90 | { 91 | return null; 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Iterator/Iterator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Iterator/Program.cs: -------------------------------------------------------------------------------- 1 | using Iterator; 2 | 3 | Console.Title = "Iterator"; 4 | 5 | // create the collection 6 | PeopleCollection people = new(); 7 | 8 | people.Add(new Person("Kevin Dockx", "Belgium")); 9 | people.Add(new Person("Gill Cleeren", "Belgium")); 10 | people.Add(new Person("Roland Guijt", "The Netherlands")); 11 | people.Add(new Person("Thomas Claudius Huber", "Germany")); 12 | 13 | // create the iterator 14 | var peopleIterator = people.CreateIterator(); 15 | 16 | // use the iterator to run through the people 17 | // in the collection; they should come out 18 | // in alphabetical order 19 | for (Person person = peopleIterator.First(); 20 | !peopleIterator.IsDone; 21 | person = peopleIterator.Next()) 22 | { 23 | Console.WriteLine(person?.Name); 24 | } 25 | 26 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Mediator/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator 2 | { 3 | //public abstract class ChatRoom 4 | //{ 5 | // public abstract void Register(TeamMember teamMember); 6 | // public abstract void Send(string from, string message); 7 | //} 8 | 9 | /// 10 | /// Mediator 11 | /// 12 | public interface IChatRoom 13 | { 14 | void Register(TeamMember teamMember); 15 | void Send(string from, string message); 16 | void Send(string from, string to, string message); 17 | void SendTo(string from, string message) where T : TeamMember; 18 | 19 | 20 | } 21 | 22 | /// 23 | /// Colleague 24 | /// 25 | public abstract class TeamMember 26 | { 27 | private IChatRoom? _chatroom; 28 | public string Name { get; set; } 29 | public TeamMember(string name) 30 | { 31 | Name = name; 32 | } 33 | 34 | internal void SetChatroom(IChatRoom chatRoom) 35 | { 36 | _chatroom = chatRoom; 37 | } 38 | public void Send(string to, string message) 39 | { 40 | _chatroom?.Send(Name, to, message); 41 | } 42 | 43 | public void SendTo(string message) where T : TeamMember 44 | { 45 | _chatroom?.SendTo(Name, message); 46 | } 47 | 48 | public void Send(string message) 49 | { 50 | _chatroom?.Send(Name, message); 51 | } 52 | 53 | public virtual void Receive(string from, string message) 54 | { 55 | Console.WriteLine($"Message {from} to {Name}: {message}"); 56 | } 57 | } 58 | 59 | /// 60 | /// ConcreteColleague 61 | /// 62 | public class Lawyer : TeamMember 63 | { 64 | public Lawyer(string name) : base(name) 65 | { 66 | } 67 | 68 | public override void Receive(string from, string message) 69 | { 70 | Console.WriteLine($"{nameof(Lawyer)} {Name} received: "); 71 | base.Receive(from, message); 72 | } 73 | } 74 | 75 | /// 76 | /// ConcreteColleague 77 | /// 78 | public class AccountManager : TeamMember 79 | { 80 | public AccountManager(string name) : base(name) 81 | { 82 | } 83 | 84 | public override void Receive(string from, string message) 85 | { 86 | Console.WriteLine($"{nameof(AccountManager)} {Name} received: "); 87 | base.Receive(from, message); 88 | } 89 | } 90 | 91 | 92 | /// 93 | /// ConcreteMediator 94 | /// 95 | public class TeamChatRoom : IChatRoom 96 | { 97 | private readonly Dictionary teamMembers = new(); 98 | 99 | public void Register(TeamMember teamMember) 100 | { 101 | teamMember.SetChatroom(this); 102 | if (!teamMembers.ContainsKey(teamMember.Name)) 103 | { 104 | teamMembers.Add(teamMember.Name, teamMember); 105 | } 106 | } 107 | 108 | public void Send(string from, string message) 109 | { 110 | foreach (var teamMember in teamMembers.Values) 111 | { 112 | teamMember.Receive(from, message); 113 | } 114 | } 115 | 116 | public void Send(string from, string to, string message) 117 | { 118 | var teamMember = teamMembers[to]; 119 | teamMember?.Receive(from, message); 120 | } 121 | 122 | public void SendTo(string from, string message) where T : TeamMember 123 | { 124 | foreach (var teamMember in teamMembers.Values.OfType()) 125 | { 126 | teamMember.Receive(from, message); 127 | } 128 | } 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Mediator/Mediator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Mediator/Program.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | 3 | Console.Title = "Mediator"; 4 | 5 | TeamChatRoom teamChatroom = new(); 6 | 7 | var sven = new Lawyer("Sven"); 8 | var kenneth = new Lawyer("Kenneth"); 9 | var ann = new AccountManager("Ann"); 10 | var john = new AccountManager("John"); 11 | var kylie = new AccountManager("Kylie"); 12 | 13 | teamChatroom.Register(sven); 14 | teamChatroom.Register(kenneth); 15 | teamChatroom.Register(ann); 16 | teamChatroom.Register(john); 17 | teamChatroom.Register(kylie); 18 | 19 | ann.Send("Hi everyone, can someone have a look at file ABC123? I need a compliance check."); 20 | sven.Send("On it!"); 21 | sven.Send("Ann", "Could you join me in a Teams call?"); 22 | sven.Send("Ann", "All good :)"); 23 | ann.SendTo("The file was approved!"); 24 | 25 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Memento/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Memento 2 | { 3 | public class Employee 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | 8 | public Employee(int id, string name) 9 | { 10 | Id = id; 11 | Name = name; 12 | } 13 | } 14 | public class Manager : Employee 15 | { 16 | public List Employees = new(); 17 | public Manager(int id, string name) 18 | : base(id, name) 19 | { 20 | } 21 | } 22 | 23 | /// 24 | /// Receiver (interface) 25 | /// 26 | public interface IEmployeeManagerRepository 27 | { 28 | void AddEmployee(int managerId, Employee employee); 29 | void RemoveEmployee(int managerId, Employee employee); 30 | bool HasEmployee(int managerId, int employeeId); 31 | void WriteDataStore(); 32 | } 33 | 34 | /// 35 | /// Receiver (implementation) 36 | /// 37 | public class EmployeeManagerRepository : IEmployeeManagerRepository 38 | { 39 | // for demo purposes, use an in-memory datastore as a fake "manager list" 40 | private List _managers = new List() 41 | { new Manager(1, "Katie"), new Manager(2, "Geoff") }; 42 | 43 | public void AddEmployee(int managerId, Employee employee) 44 | { 45 | // in real-life, add additional input & error checks 46 | _managers.First(m => m.Id == managerId).Employees.Add(employee); 47 | } 48 | 49 | public void RemoveEmployee(int managerId, Employee employee) 50 | { 51 | // in real-life, add additional input & error checks 52 | _managers.First(m => m.Id == managerId).Employees.Remove(employee); 53 | } 54 | 55 | public bool HasEmployee(int managerId, int employeeId) 56 | { 57 | // in real-life, add additional input & error checks 58 | return _managers.First(m => m.Id == managerId) 59 | .Employees.Any(e => e.Id == employeeId); 60 | } 61 | 62 | 63 | /// 64 | /// For demo purposes, write out the data store to the console window 65 | /// 66 | public void WriteDataStore() 67 | { 68 | foreach (var manager in _managers) 69 | { 70 | Console.WriteLine($"Manager {manager.Id}, {manager.Name}"); 71 | if (manager.Employees.Any()) 72 | { 73 | foreach (var employee in manager.Employees) 74 | { 75 | Console.WriteLine($"\t Employee {employee.Id}, {employee.Name}"); 76 | } 77 | } 78 | else 79 | { 80 | Console.WriteLine($"\t No employees."); 81 | } 82 | } 83 | } 84 | } 85 | 86 | /// 87 | /// Command 88 | /// 89 | public interface ICommand 90 | { 91 | void Execute(); 92 | bool CanExecute(); 93 | void Undo(); 94 | } 95 | 96 | 97 | /// 98 | /// Memento 99 | /// 100 | public class AddEmployeeToManagerListMemento 101 | { 102 | public int ManagerId { get; private set; } 103 | public Employee? Employee { get; private set; } 104 | 105 | public AddEmployeeToManagerListMemento(int managerId, Employee? employee) 106 | { 107 | ManagerId = managerId; 108 | Employee = employee; 109 | } 110 | } 111 | 112 | /// 113 | /// ConcreteCommand & Originator 114 | /// 115 | public class AddEmployeeToManagerList : ICommand 116 | { 117 | private readonly IEmployeeManagerRepository _employeeManagerRepository; 118 | private int _managerId; 119 | private Employee? _employee; 120 | 121 | public AddEmployeeToManagerList( 122 | IEmployeeManagerRepository employeeManagerRepository, 123 | int managerId, 124 | Employee? employee) 125 | { 126 | _employeeManagerRepository = employeeManagerRepository; 127 | _managerId = managerId; 128 | _employee = employee; 129 | } 130 | 131 | public AddEmployeeToManagerListMemento CreateMemento() 132 | { 133 | return new AddEmployeeToManagerListMemento(_managerId, _employee); 134 | } 135 | 136 | public void RestoreMemento(AddEmployeeToManagerListMemento memento) 137 | { 138 | _managerId = memento.ManagerId; 139 | _employee = memento.Employee; 140 | } 141 | 142 | public bool CanExecute() 143 | { 144 | // employee shouldn't be null 145 | if (_employee == null) 146 | { 147 | return false; 148 | } 149 | 150 | // employee shouldn't be on the manager's list already 151 | if (_employeeManagerRepository.HasEmployee(_managerId, _employee.Id)) 152 | { 153 | return false; 154 | } 155 | 156 | // other potential rule(s): ensure that an employee can only be added to 157 | // one manager's list at the same time, etc. 158 | return true; 159 | } 160 | 161 | public void Execute() 162 | { 163 | if (_employee == null) 164 | { 165 | return; 166 | } 167 | _employeeManagerRepository.AddEmployee(_managerId, _employee); 168 | } 169 | 170 | public void Undo() 171 | { 172 | if (_employee == null) 173 | { 174 | return; 175 | } 176 | 177 | _employeeManagerRepository.RemoveEmployee(_managerId, _employee); 178 | } 179 | } 180 | 181 | /// 182 | /// Invoker & Caretaker 183 | /// 184 | public class CommandManager 185 | { 186 | private readonly Stack _mementos = new(); 187 | private AddEmployeeToManagerList? _command; 188 | 189 | public void Invoke(ICommand command) 190 | { 191 | // if the command has not been stored yet, store it - we will 192 | // reuse it instead of storing different instances 193 | 194 | if (_command == null) 195 | { 196 | _command = (AddEmployeeToManagerList)command; 197 | } 198 | 199 | if (command.CanExecute()) 200 | { 201 | command.Execute(); 202 | _mementos.Push(((AddEmployeeToManagerList)command).CreateMemento()); 203 | } 204 | } 205 | 206 | public void Undo() 207 | { 208 | if (_mementos.Any()) 209 | { 210 | _command?.RestoreMemento(_mementos.Pop()); 211 | _command?.Undo(); 212 | } 213 | } 214 | 215 | public void UndoAll() 216 | { 217 | while (_mementos.Any()) 218 | { 219 | _command?.RestoreMemento(_mementos.Pop()); 220 | _command?.Undo(); 221 | } 222 | } 223 | } 224 | } -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Memento/Memento.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Memento/Program.cs: -------------------------------------------------------------------------------- 1 | using Memento; 2 | 3 | Console.Title = "Memento"; 4 | 5 | CommandManager commandManager = new(); 6 | IEmployeeManagerRepository repository = new EmployeeManagerRepository(); 7 | 8 | commandManager.Invoke( 9 | new AddEmployeeToManagerList(repository, 1, new Employee(111, "Kevin"))); 10 | repository.WriteDataStore(); 11 | 12 | commandManager.Undo(); 13 | repository.WriteDataStore(); 14 | 15 | commandManager.Invoke( 16 | new AddEmployeeToManagerList(repository, 1, new Employee(222, "Clara"))); 17 | repository.WriteDataStore(); 18 | 19 | commandManager.Invoke( 20 | new AddEmployeeToManagerList(repository, 2, new Employee(333, "Tom"))); 21 | repository.WriteDataStore(); 22 | 23 | // try adding the same employee again 24 | commandManager.Invoke( 25 | new AddEmployeeToManagerList(repository, 2, new Employee(333, "Tom"))); 26 | repository.WriteDataStore(); 27 | 28 | commandManager.UndoAll(); 29 | repository.WriteDataStore(); 30 | 31 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Observer/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Observer 2 | { 3 | public class TicketChange 4 | { 5 | public int Amount { get; private set; } 6 | public int ArtistId { get; private set; } 7 | 8 | public TicketChange(int artistId, int amount) 9 | { 10 | ArtistId = artistId; 11 | Amount = amount; 12 | } 13 | } 14 | 15 | /// 16 | /// Subject 17 | /// 18 | public abstract class TicketChangeNotifier 19 | { 20 | private List _observers = new(); 21 | 22 | public void AddObserver(ITicketChangeListener observer) 23 | { 24 | _observers.Add(observer); 25 | } 26 | public void RemoveObserver(ITicketChangeListener observer) 27 | { 28 | _observers.Remove(observer); 29 | } 30 | 31 | public void Notify(TicketChange ticketChange) 32 | { 33 | foreach (var observer in _observers) 34 | { 35 | observer.ReceiveTicketChangeNotification(ticketChange); 36 | } 37 | } 38 | } 39 | 40 | /// 41 | /// Observer 42 | /// 43 | public interface ITicketChangeListener 44 | { 45 | void ReceiveTicketChangeNotification(TicketChange ticketChange); 46 | } 47 | 48 | /// 49 | /// ConcreteSubject 50 | /// 51 | public class OrderService : TicketChangeNotifier 52 | { 53 | public void CompleteTicketSale(int artistId, int amount) 54 | { 55 | // change local datastore. Datastore omitted in demo implementation. 56 | Console.WriteLine($"{nameof(OrderService)} is changing its state."); 57 | // notify observers 58 | Console.WriteLine($"{nameof(OrderService)} is notifying observers..."); 59 | Notify(new TicketChange(artistId, amount)); 60 | } 61 | } 62 | 63 | /// 64 | /// ConcreteObserver 65 | /// 66 | public class TicketResellerService : ITicketChangeListener 67 | { 68 | public void ReceiveTicketChangeNotification(TicketChange ticketChange) 69 | { 70 | // update local datastore (datastore omitted in demo implementation) 71 | Console.WriteLine($"{nameof(TicketResellerService)} notified " + 72 | $"of ticket change: artist {ticketChange.ArtistId}, amount " + 73 | $"{ticketChange.Amount}"); 74 | } 75 | } 76 | 77 | /// 78 | /// ConcreteObserver 79 | /// 80 | public class TicketStockService : ITicketChangeListener 81 | { 82 | public void ReceiveTicketChangeNotification(TicketChange ticketChange) 83 | { 84 | // update local datastore (datastore omitted in demo implementation) 85 | Console.WriteLine($"{nameof(TicketStockService)} notified " + 86 | $"of ticket change: artist {ticketChange.ArtistId}, amount " + 87 | $"{ticketChange.Amount}"); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Observer/Observer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Observer/Program.cs: -------------------------------------------------------------------------------- 1 | using Observer; 2 | 3 | Console.Title = "Observer"; 4 | 5 | TicketStockService ticketStockService = new(); 6 | TicketResellerService ticketResellerService = new(); 7 | OrderService orderService = new(); 8 | 9 | // add two observers 10 | orderService.AddObserver(ticketStockService); 11 | orderService.AddObserver(ticketResellerService); 12 | 13 | // notify 14 | orderService.CompleteTicketSale(1, 2); 15 | 16 | Console.WriteLine(); 17 | 18 | // remove one observer 19 | orderService.RemoveObserver(ticketResellerService); 20 | 21 | // notify 22 | orderService.CompleteTicketSale(2, 4); 23 | 24 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Prototype/Implementation.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Prototype 4 | { 5 | /// 6 | /// Prototype 7 | /// 8 | public abstract class Person 9 | { 10 | public abstract string Name { get; set; } 11 | 12 | public abstract Person Clone(bool deepClone); 13 | } 14 | 15 | /// 16 | /// ConcretePrototype1 17 | /// 18 | public class Manager : Person 19 | { 20 | public override string Name { get; set; } 21 | 22 | public Manager(string name) 23 | { 24 | Name = name; 25 | } 26 | 27 | public override Person Clone(bool deepClone = false) 28 | { 29 | if (deepClone) 30 | { 31 | var objectAsJson = JsonSerializer.Serialize(this); 32 | return JsonSerializer.Deserialize(objectAsJson); 33 | } 34 | 35 | return (Person)MemberwiseClone(); 36 | } 37 | } 38 | 39 | /// 40 | /// ConcretePrototype2 41 | /// 42 | public class Employee : Person 43 | { 44 | public Manager Manager { get; set; } 45 | public override string Name { get; set; } 46 | 47 | public Employee(string name, Manager manager) 48 | { 49 | Name = name; 50 | Manager = manager; 51 | } 52 | 53 | public override Person Clone(bool deepClone = false) 54 | { 55 | if (deepClone) 56 | { 57 | var objectAsJson = JsonSerializer.Serialize(this); 58 | return JsonSerializer.Deserialize(objectAsJson); 59 | } 60 | return (Person)MemberwiseClone(); 61 | } 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Prototype/Program.cs: -------------------------------------------------------------------------------- 1 | using Prototype; 2 | 3 | Console.Title = "Prototype"; 4 | 5 | var manager = new Manager("Cindy"); 6 | var managerClone = (Manager)manager.Clone(); 7 | Console.WriteLine($"Manager was cloned: {managerClone.Name}"); 8 | 9 | var employee = new Employee("Kevin", managerClone); 10 | var employeeClone = (Employee)employee.Clone(true); 11 | Console.WriteLine($"Employee was cloned: {employeeClone.Name}," + 12 | $" with manager {employeeClone.Manager.Name}"); 13 | 14 | // change the manager name 15 | managerClone.Name = "Karen"; 16 | Console.WriteLine($"Employee was cloned: {employeeClone.Name}, " + 17 | $"with manager {employeeClone.Manager.Name}"); 18 | 19 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Prototype/Prototype.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Proxy/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Proxy 2 | { 3 | /// 4 | /// Subject 5 | /// 6 | public interface IDocument 7 | { 8 | void DisplayDocument(); 9 | } 10 | 11 | /// 12 | /// RealSubject 13 | /// 14 | public class Document : IDocument 15 | { 16 | public string? Title { get; private set; } 17 | public string? Content { get; private set; } 18 | public int AuthorId { get; private set; } 19 | public DateTimeOffset LastAccessed { get; private set; } 20 | private string _fileName; 21 | 22 | public Document(string fileName) 23 | { 24 | _fileName = fileName; 25 | LoadDocument(fileName); 26 | } 27 | 28 | private void LoadDocument(string fileName) 29 | { 30 | Console.WriteLine("Executing expensive action: loading a file from disk"); 31 | // fake loading... 32 | Thread.Sleep(1000); 33 | 34 | Title = "An expensive document"; 35 | Content = "Lots and lots of content"; 36 | AuthorId = 1; 37 | LastAccessed = DateTimeOffset.UtcNow; 38 | } 39 | 40 | public void DisplayDocument() 41 | { 42 | Console.WriteLine($"Title: {Title}, Content: {Content}"); 43 | } 44 | } 45 | 46 | /// 47 | /// Proxy 48 | /// 49 | public class DocumentProxy : IDocument 50 | { 51 | // avoid creating the document until we need it 52 | private Lazy _document; 53 | private string _fileName; 54 | 55 | public DocumentProxy(string fileName) 56 | { 57 | _fileName = fileName; 58 | _document = new Lazy(() => new Document(_fileName)); 59 | } 60 | 61 | public void DisplayDocument() 62 | { 63 | _document.Value.DisplayDocument(); 64 | } 65 | } 66 | 67 | /// 68 | /// Proxy 69 | /// 70 | public class ProtectedDocumentProxy : IDocument 71 | { 72 | private string _fileName; 73 | private string _userRole; 74 | private DocumentProxy _documentProxy; 75 | 76 | public ProtectedDocumentProxy(string fileName, 77 | string userRole) 78 | { 79 | _fileName = fileName; 80 | _userRole = userRole; 81 | _documentProxy = new DocumentProxy(_fileName); 82 | } 83 | 84 | public void DisplayDocument() 85 | { 86 | Console.WriteLine($"Entering DisplayDocument " + 87 | $"in {nameof(ProtectedDocumentProxy)}."); 88 | 89 | if (_userRole != "Viewer") 90 | { 91 | throw new UnauthorizedAccessException(); 92 | } 93 | 94 | _documentProxy.DisplayDocument(); 95 | 96 | Console.WriteLine($"Exiting DisplayDocument " + 97 | $"in {nameof(ProtectedDocumentProxy)}."); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Proxy/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Proxy"; 2 | 3 | // without proxy 4 | Console.WriteLine("Constructing document."); 5 | var myDocument = new Proxy.Document("MyDocument.pdf"); 6 | Console.WriteLine("Document constructed."); 7 | myDocument.DisplayDocument(); 8 | 9 | Console.WriteLine(); 10 | 11 | // with proxy 12 | Console.WriteLine("Constructing document proxy."); 13 | var myDocumentProxy = new Proxy.DocumentProxy("MyDocument.pdf"); 14 | Console.WriteLine("Document proxy constructed."); 15 | myDocumentProxy.DisplayDocument(); 16 | 17 | Console.WriteLine(); 18 | 19 | // with chained proxy 20 | Console.WriteLine("Constructing protected document proxy."); 21 | var myProtectedDocumentProxy = new Proxy.ProtectedDocumentProxy("MyDocument.pdf", "Viewer"); 22 | Console.WriteLine("Protected document proxy constructed."); 23 | myProtectedDocumentProxy.DisplayDocument(); 24 | 25 | Console.WriteLine(); 26 | 27 | // with chained proxy, no access 28 | Console.WriteLine("Constructing protected document proxy."); 29 | myProtectedDocumentProxy = new Proxy.ProtectedDocumentProxy("MyDocument.pdf", "AnotherRole"); 30 | Console.WriteLine("Protected document proxy constructed."); 31 | myProtectedDocumentProxy.DisplayDocument(); 32 | 33 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Proxy/Proxy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Singleton/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Singleton 2 | { 3 | /// 4 | /// Singleton 5 | /// 6 | public class Logger 7 | { 8 | // Lazy 9 | private static readonly Lazy _lazyLogger 10 | = new Lazy(() => new Logger()); 11 | 12 | // private static Logger? _instance; 13 | 14 | /// 15 | /// Instance 16 | /// 17 | public static Logger Instance 18 | { 19 | get 20 | { 21 | return _lazyLogger.Value; 22 | //if (_instance == null) 23 | //{ 24 | // _instance = new Logger(); 25 | //} 26 | //return _instance; 27 | } 28 | } 29 | 30 | protected Logger() 31 | { 32 | } 33 | 34 | /// 35 | /// SingletonOperation 36 | /// 37 | public void Log(string message) 38 | { 39 | Console.WriteLine($"Message to log: {message}"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Singleton/Program.cs: -------------------------------------------------------------------------------- 1 | using Singleton; 2 | 3 | Console.Title = "Singleton"; 4 | 5 | // call the property getter twice 6 | var instance1 = Logger.Instance; 7 | var instance2 = Logger.Instance; 8 | 9 | if (instance1 == instance2 && instance2 == Logger.Instance) 10 | { 11 | Console.WriteLine("Instances are the same."); 12 | } 13 | 14 | instance1.Log($"Message from {nameof(instance1)}"); 15 | // or 16 | instance1.Log($"Message from {nameof(instance2)}"); 17 | // or 18 | Logger.Instance.Log($"Message from {nameof(Logger.Instance)}"); 19 | 20 | Console.ReadLine(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Singleton/Singleton.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/State/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace State 2 | { 3 | /// 4 | /// State 5 | /// 6 | public abstract class BankAccountState 7 | { 8 | public BankAccount BankAccount { get; protected set; } = null!; 9 | public decimal Balance { get; protected set; } 10 | 11 | public abstract void Deposit(decimal amount); 12 | public abstract void Withdraw(decimal amount); 13 | 14 | } 15 | 16 | /// 17 | /// ConcreteState 18 | /// 19 | public class GoldState : BankAccountState 20 | { 21 | public GoldState(decimal balance, BankAccount bankAccount) 22 | { 23 | Balance = balance; 24 | BankAccount = bankAccount; 25 | } 26 | 27 | public override void Deposit(decimal amount) 28 | { 29 | Console.WriteLine($"In {GetType()}, depositing " + 30 | $"{amount} + 10% bonus: {amount / 10}"); 31 | Balance += amount + (amount / 10); 32 | } 33 | 34 | public override void Withdraw(decimal amount) 35 | { 36 | Console.WriteLine($"In {GetType()}, withdrawing {amount} from {Balance}"); 37 | // change state to overdrawn when withdrawing results in less than zero 38 | Balance -= amount; 39 | if (Balance < 1000 && Balance >= 0) 40 | { 41 | // change state to regular 42 | BankAccount.BankAccountState = new RegularState(Balance, BankAccount); 43 | } 44 | else if (Balance < 0) 45 | { 46 | // change state to overdrawn 47 | BankAccount.BankAccountState = new OverdrawnState(Balance, BankAccount); 48 | } 49 | } 50 | } 51 | 52 | /// 53 | /// ConcreteState 54 | /// 55 | public class RegularState : BankAccountState 56 | { 57 | public RegularState(decimal balance, BankAccount bankAccount) 58 | { 59 | Balance = balance; 60 | BankAccount = bankAccount; 61 | } 62 | 63 | public override void Deposit(decimal amount) 64 | { 65 | Console.WriteLine($"In {GetType()}, depositing {amount}"); 66 | Balance += amount; 67 | if (Balance >= 1000) 68 | { 69 | // change state to gold 70 | BankAccount.BankAccountState = new GoldState(Balance, BankAccount); 71 | } 72 | } 73 | 74 | public override void Withdraw(decimal amount) 75 | { 76 | Console.WriteLine($"In {GetType()}, withdrawing {amount} from {Balance}"); 77 | // change state to overdrawn when withdrawing results in less than zero 78 | Balance -= amount; 79 | if (Balance < 0) 80 | { 81 | // change state to overdrawn 82 | BankAccount.BankAccountState = new OverdrawnState(Balance, BankAccount); 83 | } 84 | } 85 | } 86 | 87 | /// 88 | /// ConcreteState 89 | /// 90 | public class OverdrawnState : BankAccountState 91 | { 92 | public OverdrawnState(decimal balance, BankAccount bankAccount) 93 | { 94 | Balance = balance; 95 | BankAccount = bankAccount; 96 | } 97 | 98 | public override void Deposit(decimal amount) 99 | { 100 | Console.WriteLine($"In {GetType()}, depositing {amount}"); 101 | Balance += amount; 102 | if (Balance >= 0) 103 | { 104 | // change state to regular 105 | BankAccount.BankAccountState = new RegularState(Balance, BankAccount); 106 | } 107 | } 108 | 109 | public override void Withdraw(decimal amount) 110 | { 111 | // cannot withdraw 112 | Console.WriteLine($"In {GetType()}, cannot withdraw, balance {Balance}"); 113 | } 114 | } 115 | 116 | 117 | /// 118 | /// Context 119 | /// 120 | public class BankAccount 121 | { 122 | public BankAccountState BankAccountState { get; set; } 123 | public decimal Balance { get { return BankAccountState.Balance; } } 124 | 125 | public BankAccount() 126 | { 127 | BankAccountState = new RegularState(200, this); 128 | } 129 | 130 | /// 131 | /// Request a deposit 132 | /// 133 | public void Deposit(decimal amount) 134 | { 135 | // let the current state handle the deposit 136 | BankAccountState.Deposit(amount); 137 | } 138 | 139 | /// 140 | /// Request a withdrawal 141 | /// 142 | public void Withdraw(decimal amount) 143 | { 144 | // let the current state handle the withdrawel 145 | BankAccountState.Withdraw(amount); 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/State/Program.cs: -------------------------------------------------------------------------------- 1 | using State; 2 | 3 | Console.Title = "State"; 4 | 5 | BankAccount bankAccount = new(); 6 | bankAccount.Deposit(100); 7 | bankAccount.Withdraw(500); 8 | bankAccount.Withdraw(100); 9 | // deposit enough to go to gold 10 | bankAccount.Deposit(2000); 11 | // should be in gold now 12 | bankAccount.Deposit(100); 13 | // back to overdraw 14 | bankAccount.Withdraw(3000); 15 | // should transition to regular 16 | bankAccount.Deposit(3000); 17 | // should still be in regular 18 | bankAccount.Deposit(100); 19 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/State/State.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Strategy/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Strategy 2 | { 3 | /// 4 | /// Strategy 5 | /// 6 | public interface IExportService 7 | { 8 | void Export(Order order); 9 | } 10 | 11 | /// 12 | /// ConcreteStrategy 13 | /// 14 | public class JsonExportService : IExportService 15 | { 16 | public void Export(Order order) 17 | { 18 | Console.WriteLine($"Exporting {order.Name} to Json."); 19 | } 20 | } 21 | 22 | /// 23 | /// ConcreteStrategy 24 | /// 25 | public class XMLExportService : IExportService 26 | { 27 | public void Export(Order order) 28 | { 29 | Console.WriteLine($"Exporting {order.Name} to XML."); 30 | } 31 | } 32 | 33 | /// 34 | /// ConcreteStrategy 35 | /// 36 | public class CSVExportService : IExportService 37 | { 38 | public void Export(Order order) 39 | { 40 | Console.WriteLine($"Exporting {order.Name} to CSV."); 41 | } 42 | } 43 | 44 | 45 | /// 46 | /// Context 47 | /// 48 | public class Order 49 | { 50 | public string Customer { get; set; } 51 | public int Amount { get; set; } 52 | public string Name { get; set; } 53 | public string? Description { get; set; } 54 | 55 | public Order(string customer, int amount, string name) 56 | { 57 | Customer = customer; 58 | Amount = amount; 59 | Name = name; 60 | } 61 | 62 | public void Export(IExportService exportService) 63 | { 64 | if (exportService is null) 65 | { 66 | throw new ArgumentNullException(nameof(exportService)); 67 | } 68 | 69 | exportService.Export(this); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Strategy/Program.cs: -------------------------------------------------------------------------------- 1 | using Strategy; 2 | 3 | Console.Title = "Strategy"; 4 | 5 | var order = new Order("Marvin Software", 5, "Visual Studio License"); 6 | order.Export(new CSVExportService()); 7 | order.Export(new JsonExportService()); 8 | 9 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Strategy/Strategy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/TemplateMethod/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace TemplateMethod 2 | { 3 | /// 4 | /// AbstractClass 5 | /// 6 | public abstract class MailParser 7 | { 8 | public virtual void FindServer() 9 | { 10 | Console.WriteLine("Finding server..."); 11 | } 12 | 13 | public abstract void AuthenticateToServer(); 14 | 15 | public string ParseHtmlMailBody(string identifier) 16 | { 17 | Console.WriteLine("Parsing HTML mail body..."); 18 | return $"This is the body of mail with id {identifier}"; 19 | } 20 | 21 | /// 22 | /// Template method 23 | /// 24 | public string ParseMailBody(string identifier) 25 | { 26 | Console.WriteLine("Parsing mail body (in template method)..."); 27 | FindServer(); 28 | AuthenticateToServer(); 29 | return ParseHtmlMailBody(identifier); 30 | } 31 | } 32 | 33 | public class ExchangeMailParser : MailParser 34 | { 35 | public override void AuthenticateToServer() 36 | { 37 | Console.WriteLine("Connecting to Exchange"); 38 | } 39 | } 40 | 41 | public class ApacheMailParser : MailParser 42 | { 43 | public override void AuthenticateToServer() 44 | { 45 | Console.WriteLine("Connecting to Apache"); 46 | } 47 | } 48 | 49 | public class EudoraMailParser : MailParser 50 | { 51 | public override void FindServer() 52 | { 53 | Console.WriteLine("Finding Eudora server through a custom algorithm..."); 54 | } 55 | public override void AuthenticateToServer() 56 | { 57 | Console.WriteLine("Connecting to Eudora"); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/TemplateMethod/Program.cs: -------------------------------------------------------------------------------- 1 | using TemplateMethod; 2 | 3 | Console.Title = "Template Method"; 4 | 5 | ExchangeMailParser exchangeMailParser = new(); 6 | Console.WriteLine(exchangeMailParser.ParseMailBody("bf3a298c-9990-4b02-873d-3d3c98ad16d2")); 7 | Console.WriteLine(); 8 | 9 | ApacheMailParser apacheMailParser = new(); 10 | Console.WriteLine(apacheMailParser.ParseMailBody("07b8a8c7-c598-4b6c-9049-ecce9fe4a56b")); 11 | Console.WriteLine(); 12 | 13 | EudoraMailParser eudoraMailParser = new(); 14 | Console.WriteLine(eudoraMailParser.ParseMailBody("789fe935-ced2-4fbd-865b-172909560a93")); 15 | 16 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/TemplateMethod/TemplateMethod.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Visitor/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | /// 4 | /// ConcreteElement 5 | /// 6 | public class Customer : IElement 7 | { 8 | public decimal AmountOrdered { get; private set; } 9 | public decimal Discount { get; set; } 10 | public string Name { get; private set; } 11 | 12 | public Customer(string name, decimal amountOrdered) 13 | { 14 | Name = name; 15 | AmountOrdered = amountOrdered; 16 | } 17 | 18 | public void Accept(IVisitor visitor) 19 | { 20 | // visitor.VisitCustomer(this); 21 | visitor.Visit(this); 22 | Console.WriteLine($"Visited {nameof(Customer)} {Name}, " + 23 | $"discount given: {Discount}"); 24 | } 25 | } 26 | 27 | /// 28 | /// ConcreteElement 29 | /// 30 | public class Employee : IElement 31 | { 32 | public int YearsEmployed { get; private set; } 33 | public decimal Discount { get; set; } 34 | public string Name { get; private set; } 35 | 36 | public Employee(string name, int yearsEmployed) 37 | { 38 | Name = name; 39 | YearsEmployed = yearsEmployed; 40 | } 41 | 42 | public void Accept(IVisitor visitor) 43 | { 44 | // visitor.VisitEmployee(this); 45 | visitor.Visit(this); 46 | Console.WriteLine($"Visited {nameof(Employee)} " + 47 | $"{Name}, discount given: {Discount}"); 48 | } 49 | } 50 | 51 | ///// 52 | ///// Visitor 53 | ///// 54 | //public interface IVisitor 55 | //{ 56 | // void VisitCustomer(Customer customer); 57 | // void VisitEmployee(Employee employee); 58 | //} 59 | 60 | /// 61 | /// Visitor (alternative) 62 | /// 63 | public interface IVisitor 64 | { 65 | void Visit(IElement element); 66 | } 67 | 68 | 69 | /// 70 | /// Element 71 | /// 72 | public interface IElement 73 | { 74 | void Accept(IVisitor visitor); 75 | } 76 | 77 | /// 78 | /// ConcreteVisitor 79 | /// 80 | public class DiscountVisitor : IVisitor 81 | { 82 | public decimal TotalDiscountGiven { get; set; } 83 | 84 | public void Visit(IElement element) 85 | { 86 | if (element is Customer) 87 | { 88 | VisitCustomer((Customer)element); 89 | } 90 | else if (element is Employee) 91 | { 92 | VisitEmployee((Employee)element); 93 | } 94 | } 95 | 96 | private void VisitCustomer(Customer customer) 97 | { 98 | // percentage of total amount 99 | var discount = customer.AmountOrdered / 10; 100 | // set it on the customer 101 | customer.Discount = discount; 102 | // add it to the total amount 103 | TotalDiscountGiven += discount; 104 | } 105 | 106 | private void VisitEmployee(Employee employee) 107 | { 108 | // fixed value depending on the amount of years employed 109 | var discount = employee.YearsEmployed < 10 ? 100 : 200; 110 | // set it on the employee 111 | employee.Discount = discount; 112 | // add it to the total amount 113 | TotalDiscountGiven += discount; 114 | } 115 | } 116 | 117 | /// 118 | /// ObjectStructure 119 | /// 120 | public class Container 121 | { 122 | public List Employees { get; set; } = new(); 123 | public List Customers { get; set; } = new(); 124 | 125 | public void Accept(IVisitor visitor) 126 | { 127 | foreach (var employee in Employees) 128 | { 129 | employee.Accept(visitor); 130 | } 131 | foreach (var customer in Customers) 132 | { 133 | customer.Accept(visitor); 134 | } 135 | } 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Visitor/Program.cs: -------------------------------------------------------------------------------- 1 | using Visitor; 2 | 3 | Console.Title = "Visitor"; 4 | 5 | // create container & add concrete elements 6 | var container = new Container(); 7 | 8 | container.Customers.Add(new Customer("Sophie", 500)); 9 | container.Customers.Add(new Customer("Karen", 1000)); 10 | container.Customers.Add(new Customer("Sven", 800)); 11 | container.Employees.Add(new Employee("Kevin", 18)); 12 | container.Employees.Add(new Employee("Tom", 5)); 13 | 14 | // create visitor 15 | DiscountVisitor discountVisitor = new(); 16 | 17 | // pass it through 18 | container.Accept(discountVisitor); 19 | 20 | // write out gathered amount 21 | Console.WriteLine($"Total discount: {discountVisitor.TotalDiscountGiven}"); 22 | 23 | Console.ReadKey(); -------------------------------------------------------------------------------- /Finished sample/Gang of Four Patterns/Visitor/Visitor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kevin Dockx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C# Design Patterns 2 | Fully functioning sample code for my C# Design Patterns course over at Pluralsight, currently targeting .NET 8. 3 | 4 | The **main** branch exactly matches the course. 5 | The **latest-and-greatest** branch contains changes that were incorporated after recording. Most often these changes are language features that are relativley new and/or in preview, like file-scoped namespaces, primary constructors, switch expressions and so on. Most of these changes will probably make it into the main branch when course updates happen, but if you don't want to wait for that you can already check it out - enjoy :-) 6 | 7 | All my courses can be found at https://app.pluralsight.com/profile/author/kevin-dockx 8 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34601.278 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnterprisePatterns", "EnterprisePatterns\EnterprisePatterns.csproj", "{4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {4CA9C8F7-6B1C-4E17-9011-5A9A40E82E8B}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B22A0AEA-8A58-425A-B127-C9E5838FAD4A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/DbContexts/OrderDbContext.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | using System.IO; 4 | 5 | namespace EnterprisePatterns.DbContexts; 6 | 7 | public class OrderDbContext(DbContextOptions options) : DbContext(options) 8 | { 9 | public DbSet Orders { get; set; } 10 | public DbSet OrderLines { get; set; } 11 | 12 | // seed the database with data 13 | protected override void OnModelCreating(ModelBuilder modelBuilder) 14 | { 15 | modelBuilder.Entity().HasData( 16 | new("Shopping Spree") { Id = 1 }, 17 | new("Holiday Shopping") { Id = 2 }); 18 | 19 | modelBuilder.Entity().HasData( 20 | new("Shoes", 1) { Id = 1, OrderId = 1 }, 21 | new("Shirt", 2) { Id = 2, OrderId = 1 }, 22 | new("Pants", 1) { Id = 3, OrderId = 1 }, 23 | new("Socks", 5) { Id = 4, OrderId = 1 }, 24 | new("Sunglasses", 1) { Id = 5, OrderId = 2 }, 25 | new("Sunscreen", 2) { Id = 6, OrderId = 2 }); 26 | 27 | base.OnModelCreating(modelBuilder); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/DemoServices/RepositoryDemoService.cs: -------------------------------------------------------------------------------- 1 | namespace EnterprisePatterns.Services; 2 | 3 | public class RepositoryDemoService() 4 | { 5 | public async Task RunAsync() 6 | { 7 | throw new NotImplementedException(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/DemoServices/UnitOfWorkDemoService.cs: -------------------------------------------------------------------------------- 1 | namespace EnterprisePatterns.DemoServices; 2 | 3 | public class UnitOfWorkDemoService 4 | { 5 | public async Task RunAsync() 6 | { 7 | throw new NotImplementedException(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/EnterprisePatterns.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/Entities/Order.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | 3 | namespace EnterprisePatterns.Entities; 4 | 5 | 6 | [Table("Orders")] 7 | public class Order(string? description) 8 | { 9 | public int Id { get; set; } 10 | public string? Description { get; set; } = description; 11 | public ICollection OrderLines { get; set; } = []; 12 | } -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/Entities/OrderLine.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | 3 | namespace EnterprisePatterns.Entities; 4 | 5 | [Table("OrderLines")] 6 | public class OrderLine(string product, int amount) 7 | { 8 | public int Id { get; set; } 9 | public string Product { get; set; } = product; 10 | public int Amount { get; set; } = amount; 11 | public int OrderId { get; set; } 12 | public Order Order { get; set; } = null!; 13 | } 14 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/Migrations/InitialMigration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using EnterprisePatterns.DbContexts; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | 8 | #nullable disable 9 | 10 | namespace EnterprisePatterns.Migrations 11 | { 12 | [DbContext(typeof(OrderDbContext))] 13 | [Migration("20240422092841_InitialMigration")] 14 | partial class InitialMigration 15 | { 16 | /// 17 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 18 | { 19 | #pragma warning disable 612, 618 20 | modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); 21 | 22 | modelBuilder.Entity("EnterprisePatterns.Entities.Order", b => 23 | { 24 | b.Property("Id") 25 | .ValueGeneratedOnAdd() 26 | .HasColumnType("INTEGER"); 27 | 28 | b.Property("Description") 29 | .HasColumnType("TEXT"); 30 | 31 | b.HasKey("Id"); 32 | 33 | b.ToTable("Orders"); 34 | 35 | b.HasData( 36 | new 37 | { 38 | Id = 1, 39 | Description = "Shopping Spree" 40 | }, 41 | new 42 | { 43 | Id = 2, 44 | Description = "Holiday Shopping" 45 | }); 46 | }); 47 | 48 | modelBuilder.Entity("EnterprisePatterns.Entities.OrderLine", b => 49 | { 50 | b.Property("Id") 51 | .ValueGeneratedOnAdd() 52 | .HasColumnType("INTEGER"); 53 | 54 | b.Property("Amount") 55 | .HasColumnType("INTEGER"); 56 | 57 | b.Property("OrderId") 58 | .HasColumnType("INTEGER"); 59 | 60 | b.Property("Product") 61 | .IsRequired() 62 | .HasColumnType("TEXT"); 63 | 64 | b.HasKey("Id"); 65 | 66 | b.HasIndex("OrderId"); 67 | 68 | b.ToTable("OrderLines"); 69 | 70 | b.HasData( 71 | new 72 | { 73 | Id = 1, 74 | Amount = 1, 75 | OrderId = 1, 76 | Product = "Shoes" 77 | }, 78 | new 79 | { 80 | Id = 2, 81 | Amount = 2, 82 | OrderId = 1, 83 | Product = "Shirt" 84 | }, 85 | new 86 | { 87 | Id = 3, 88 | Amount = 1, 89 | OrderId = 1, 90 | Product = "Pants" 91 | }, 92 | new 93 | { 94 | Id = 4, 95 | Amount = 5, 96 | OrderId = 1, 97 | Product = "Socks" 98 | }, 99 | new 100 | { 101 | Id = 5, 102 | Amount = 1, 103 | OrderId = 2, 104 | Product = "Sunglasses" 105 | }, 106 | new 107 | { 108 | Id = 6, 109 | Amount = 2, 110 | OrderId = 2, 111 | Product = "Sunscreen" 112 | }); 113 | }); 114 | 115 | modelBuilder.Entity("EnterprisePatterns.Entities.OrderLine", b => 116 | { 117 | b.HasOne("EnterprisePatterns.Entities.Order", null) 118 | .WithMany("OrderLines") 119 | .HasForeignKey("OrderId") 120 | .OnDelete(DeleteBehavior.Cascade) 121 | .IsRequired(); 122 | }); 123 | 124 | modelBuilder.Entity("EnterprisePatterns.Entities.Order", b => 125 | { 126 | b.Navigation("OrderLines"); 127 | }); 128 | #pragma warning restore 612, 618 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/Migrations/InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | #pragma warning disable CA1814 // Prefer jagged arrays over multidimensional 6 | 7 | namespace EnterprisePatterns.Migrations 8 | { 9 | /// 10 | public partial class InitialMigration : Migration 11 | { 12 | /// 13 | protected override void Up(MigrationBuilder migrationBuilder) 14 | { 15 | migrationBuilder.CreateTable( 16 | name: "Orders", 17 | columns: table => new 18 | { 19 | Id = table.Column(type: "INTEGER", nullable: false) 20 | .Annotation("Sqlite:Autoincrement", true), 21 | Description = table.Column(type: "TEXT", nullable: true) 22 | }, 23 | constraints: table => 24 | { 25 | table.PrimaryKey("PK_Orders", x => x.Id); 26 | }); 27 | 28 | migrationBuilder.CreateTable( 29 | name: "OrderLines", 30 | columns: table => new 31 | { 32 | Id = table.Column(type: "INTEGER", nullable: false) 33 | .Annotation("Sqlite:Autoincrement", true), 34 | Product = table.Column(type: "TEXT", nullable: false), 35 | Amount = table.Column(type: "INTEGER", nullable: false), 36 | OrderId = table.Column(type: "INTEGER", nullable: false) 37 | }, 38 | constraints: table => 39 | { 40 | table.PrimaryKey("PK_OrderLines", x => x.Id); 41 | table.ForeignKey( 42 | name: "FK_OrderLines_Orders_OrderId", 43 | column: x => x.OrderId, 44 | principalTable: "Orders", 45 | principalColumn: "Id", 46 | onDelete: ReferentialAction.Cascade); 47 | }); 48 | 49 | migrationBuilder.InsertData( 50 | table: "Orders", 51 | columns: new[] { "Id", "Description" }, 52 | values: new object[,] 53 | { 54 | { 1, "Shopping Spree" }, 55 | { 2, "Holiday Shopping" } 56 | }); 57 | 58 | migrationBuilder.InsertData( 59 | table: "OrderLines", 60 | columns: new[] { "Id", "Amount", "OrderId", "Product" }, 61 | values: new object[,] 62 | { 63 | { 1, 1, 1, "Shoes" }, 64 | { 2, 2, 1, "Shirt" }, 65 | { 3, 1, 1, "Pants" }, 66 | { 4, 5, 1, "Socks" }, 67 | { 5, 1, 2, "Sunglasses" }, 68 | { 6, 2, 2, "Sunscreen" } 69 | }); 70 | 71 | migrationBuilder.CreateIndex( 72 | name: "IX_OrderLines_OrderId", 73 | table: "OrderLines", 74 | column: "OrderId"); 75 | } 76 | 77 | /// 78 | protected override void Down(MigrationBuilder migrationBuilder) 79 | { 80 | migrationBuilder.DropTable( 81 | name: "OrderLines"); 82 | 83 | migrationBuilder.DropTable( 84 | name: "Orders"); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/Migrations/OrderDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using EnterprisePatterns.DbContexts; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 6 | 7 | #nullable disable 8 | 9 | namespace EnterprisePatterns.Migrations 10 | { 11 | [DbContext(typeof(OrderDbContext))] 12 | partial class OrderDbContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); 18 | 19 | modelBuilder.Entity("EnterprisePatterns.Entities.Order", b => 20 | { 21 | b.Property("Id") 22 | .ValueGeneratedOnAdd() 23 | .HasColumnType("INTEGER"); 24 | 25 | b.Property("Description") 26 | .HasColumnType("TEXT"); 27 | 28 | b.HasKey("Id"); 29 | 30 | b.ToTable("Orders"); 31 | 32 | b.HasData( 33 | new 34 | { 35 | Id = 1, 36 | Description = "Shopping Spree" 37 | }, 38 | new 39 | { 40 | Id = 2, 41 | Description = "Holiday Shopping" 42 | }); 43 | }); 44 | 45 | modelBuilder.Entity("EnterprisePatterns.Entities.OrderLine", b => 46 | { 47 | b.Property("Id") 48 | .ValueGeneratedOnAdd() 49 | .HasColumnType("INTEGER"); 50 | 51 | b.Property("Amount") 52 | .HasColumnType("INTEGER"); 53 | 54 | b.Property("OrderId") 55 | .HasColumnType("INTEGER"); 56 | 57 | b.Property("Product") 58 | .IsRequired() 59 | .HasColumnType("TEXT"); 60 | 61 | b.HasKey("Id"); 62 | 63 | b.HasIndex("OrderId"); 64 | 65 | b.ToTable("OrderLines"); 66 | 67 | b.HasData( 68 | new 69 | { 70 | Id = 1, 71 | Amount = 1, 72 | OrderId = 1, 73 | Product = "Shoes" 74 | }, 75 | new 76 | { 77 | Id = 2, 78 | Amount = 2, 79 | OrderId = 1, 80 | Product = "Shirt" 81 | }, 82 | new 83 | { 84 | Id = 3, 85 | Amount = 1, 86 | OrderId = 1, 87 | Product = "Pants" 88 | }, 89 | new 90 | { 91 | Id = 4, 92 | Amount = 5, 93 | OrderId = 1, 94 | Product = "Socks" 95 | }, 96 | new 97 | { 98 | Id = 5, 99 | Amount = 1, 100 | OrderId = 2, 101 | Product = "Sunglasses" 102 | }, 103 | new 104 | { 105 | Id = 6, 106 | Amount = 2, 107 | OrderId = 2, 108 | Product = "Sunscreen" 109 | }); 110 | }); 111 | 112 | modelBuilder.Entity("EnterprisePatterns.Entities.OrderLine", b => 113 | { 114 | b.HasOne("EnterprisePatterns.Entities.Order", null) 115 | .WithMany("OrderLines") 116 | .HasForeignKey("OrderId") 117 | .OnDelete(DeleteBehavior.Cascade) 118 | .IsRequired(); 119 | }); 120 | 121 | modelBuilder.Entity("EnterprisePatterns.Entities.Order", b => 122 | { 123 | b.Navigation("OrderLines"); 124 | }); 125 | #pragma warning restore 612, 618 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/Orders.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinDockx/CSharpDesignPatterns/274f0a0fc7c361914a15f2357d43979a53f3e68c/Starter files/Enterprise Patterns/EnterprisePatterns/Orders.db -------------------------------------------------------------------------------- /Starter files/Enterprise Patterns/EnterprisePatterns/Program.cs: -------------------------------------------------------------------------------- 1 | using EnterprisePatterns.DbContexts; 2 | using EnterprisePatterns.DemoServices; 3 | using EnterprisePatterns.Services; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using Microsoft.Extensions.Logging; 8 | 9 | using IHost host = Host.CreateDefaultBuilder(args) 10 | .ConfigureServices((_, services) => 11 | { 12 | // register services for DI 13 | services.AddLogging(configure => configure.AddDebug().AddConsole()); 14 | 15 | services.AddDbContext(o => o.UseSqlite("Data Source=Orders.db;")); 16 | 17 | services.AddScoped(); 18 | services.AddScoped(); 19 | 20 | }).Build(); 21 | 22 | 23 | // For demo purposes: overall catch-all to log any exception that might 24 | // happen to the console & wait for key input afterwards so you can easily 25 | // inspect the issue. 26 | try 27 | { 28 | var logger = host.Services.GetRequiredService>(); 29 | logger.LogInformation("Host created."); 30 | 31 | // Run a demo service 32 | await host.Services.GetRequiredService().RunAsync(); 33 | } 34 | catch (Exception generalException) 35 | { 36 | // log the exception 37 | var logger = host.Services.GetRequiredService>(); 38 | logger.LogError(generalException, 39 | "An exception happened while running the integration service."); 40 | } 41 | 42 | Console.ReadKey(); 43 | await host.RunAsync(); -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/AbstractFactory/AbstractFactory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/AbstractFactory/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace AbstractFactory 2 | { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/AbstractFactory/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Abstract Factory"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Adapter/Adapter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Adapter/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Adapter"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Adapter/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "WSL": { 4 | "commandName": "WSL2", 5 | "distributionName": "" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Bridge/Bridge.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Bridge/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Bridge 2 | { 3 | } -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Bridge/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Bridge"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Builder/Builder.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Builder/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace BuilderPattern 2 | { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Builder/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Builder"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/ChainOfResponsibility/ChainOfResponsibility.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/ChainOfResponsibility/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace ChainOfResponsibility 2 | { 3 | } -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/ChainOfResponsibility/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Chain of Responsibility"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Command/Command.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Command/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Command 2 | { 3 | } -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Command/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Command"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Composite/Composite.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Composite/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Composite 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Composite/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Composite"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Decorator/Decorator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Decorator/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Decorator 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Decorator/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Decorator"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Facade/Facade.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Facade/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Facade 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Facade/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Facade"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/FactoryMethod/FactoryMethod.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/FactoryMethod/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace FactoryMethod 2 | { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/FactoryMethod/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Factory Method"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Flyweight/Flyweight.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Flyweight/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Flyweight 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Flyweight/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Flyweight"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Interpreter/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Interpreter 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Interpreter/Interpreter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Interpreter/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Interpreter"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Iterator/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Iterator 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Iterator/Iterator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Iterator/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Iterator"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Mediator/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Mediator/Mediator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Mediator/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Mediator"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Memento/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Memento 2 | { 3 | } -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Memento/Memento.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Memento/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Memento"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Observer/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Observer 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Observer/Observer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Observer/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Observer"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Prototype/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Prototype 2 | { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Prototype/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Prototype"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Prototype/Prototype.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Proxy/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Proxy 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Proxy/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Proxy"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Proxy/Proxy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Singleton/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Singleton 2 | { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Singleton/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Singleton"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Singleton/Singleton.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/State/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace State 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/State/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "State"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/State/State.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Strategy/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Strategy 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Strategy/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Strategy"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Strategy/Strategy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/TemplateMethod/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace TemplateMethod 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/TemplateMethod/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Template Method"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/TemplateMethod/TemplateMethod.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Visitor/Implementation.cs: -------------------------------------------------------------------------------- 1 | namespace Visitor 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Visitor/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Title = "Visitor"; -------------------------------------------------------------------------------- /Starter files/Gang of Four Patterns/Visitor/Visitor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------