diff --git a/Program.cs b/Program.cs
index 368b7bc..702a022 100644
--- a/Program.cs
+++ b/Program.cs
@@ -1,12 +1,30 @@
using System;
+using System.Threading.Tasks;
+using design_patterns.creational.abstract_factory;
+using design_patterns.creational.fluentbuilder;
+using design_patterns.creational.functionalbuilder;
+using design_patterns.creational.factory;
+using design_patterns.creational.facadebuilder;
namespace design_patterns
{
class Program
{
- static void Main(string[] args)
+ static async Task Main(string[] args)
{
- Console.WriteLine("Hello World!");
+ try
+ {
+ // creational
+ // await FactorySample.Run();
+ // await AbstractFactorySample.Run();
+ // await FluentBuilderSample.Run();
+ // await FunctionalBuilderSample.Run();
+ await FacadeBuilderSample.Run();
+ }
+ catch (System.Exception ex)
+ {
+ Console.WriteLine($"EXCEPTION: {ex}");
+ }
}
}
}
diff --git a/creational/abstract_factory/AbstractFactorySample.cs b/creational/abstract_factory/AbstractFactorySample.cs
new file mode 100644
index 0000000..f303cc1
--- /dev/null
+++ b/creational/abstract_factory/AbstractFactorySample.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace design_patterns.creational.abstract_factory
+{
+ ///
+ /// Abstract Factory is a creational design pattern,
+ /// which solves the problem of creating entire product families
+ /// without specifying their concrete classes.
+ /// Complexity: 2/3
+ /// Popularity: 3/3
+ /// Usage examples:
+ /// The Abstract Factory pattern is pretty common in C# code.
+ /// Many frameworks and libraries use it to provide a way
+ /// to extend and customize their standard components.
+ /// Identification:
+ /// The pattern is easy to recognize by methods,
+ /// which return a factory object. Then, the factory is used
+ /// for creating specific sub-components.
+ ///
+ public class AbstractFactorySample
+ {
+ public static async Task Run()
+ {
+ Console.WriteLine("Creational - Abstract Factory");
+
+ var abstFactory = new WorkshopFactory();
+ var p1 = abstFactory.GetProduct("a");
+ Console.WriteLine($"product is of type {p1.GetType()}");
+ p1.ShowDetails();
+ var p2 = abstFactory.GetProduct("b");
+ Console.WriteLine($"product is of type {p2.GetType()}");
+ p2.ShowDetails();
+ try
+ {
+ var p3 = abstFactory.GetProduct("c");
+ p3.ShowDetails();
+ }
+ catch (System.Exception ex)
+ {
+ if(ex is ArgumentException exa && exa.Message.Contains("not available in existing factories")){
+ Console.WriteLine($"p3 cannot be create because there is no factory of this type.");
+ }else{
+ throw;
+ }
+ }
+
+ }
+ }
+
+ public interface IProduct
+ {
+ string Details {get;set;}
+ void ShowDetails();
+ }
+
+ public class ProductA : IProduct
+ {
+ public string Details { get; set; }
+
+ public void ShowDetails()
+ {
+ Console.WriteLine($"ProductA details: {this.Details}");
+ }
+ }
+
+ public class ProductB : IProduct
+ {
+ public string Details { get; set; }
+
+ public void ShowDetails()
+ {
+ Console.WriteLine($"ProductB details: {this.Details}");
+ }
+ }
+
+ public interface IProductFactory {
+ IProduct GetProduct();
+ }
+
+ public class ProductAFactory : IProductFactory
+ {
+ public IProduct GetProduct()
+ {
+ return new ProductA() {
+ Details = "ProductA " + Guid.NewGuid()
+ };
+ }
+ }
+
+ public class ProductBFactory : IProductFactory
+ {
+ public IProduct GetProduct()
+ {
+ return new ProductB() {
+ Details = "ProductB " + Guid.NewGuid()
+ };
+ }
+ }
+
+ public class WorkshopFactory
+ {
+ private Dictionary factories = new Dictionary() {
+ {"a", new ProductAFactory()},
+ {"b", new ProductBFactory()}
+ };
+ public IProduct GetProduct(string productType)
+ {
+ if(factories.TryGetValue(productType, out var factory)){
+ var concrete = factory.GetProduct();
+ // can do extra
+ concrete.Details += " " + Guid.NewGuid();
+ return concrete;
+ }
+ else {
+ throw new ArgumentException($"product type {productType} not available in existing factories.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/creational/builder/FacadeBuilderSample.cs b/creational/builder/FacadeBuilderSample.cs
new file mode 100644
index 0000000..f8aa5f6
--- /dev/null
+++ b/creational/builder/FacadeBuilderSample.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Threading.Tasks;
+
+namespace design_patterns.creational.facadebuilder
+{
+ ///
+ /// this sample covers the case when we have multiple builders
+ /// for the same class, that we want to chain
+ ///
+ public class FacadeBuilderSample
+ {
+ public static async Task Run() {
+ Console.WriteLine("Creational - Facade Builder");
+
+ var product = new ProductBuilder()
+ .Info
+ .WithName("New Product")
+ .WithDetails("product details")
+ .Price
+ .WithPrice(67.12m)
+ .WithDiscountLevel1(4)
+ .WithDiscountLevel2(12)
+ .Build();
+
+ Console.WriteLine("product: " + product.ToString());
+
+ // sample with implicit operator casting
+ Product product2 = new ProductBuilder()
+ .Info
+ .WithName("New Product 2")
+ .WithDetails("product details 2")
+ .Price
+ .WithPrice(23.11m)
+ .WithDiscountLevel1(5)
+ .WithDiscountLevel2(10);
+
+ Console.WriteLine("product: " + product2.ToString());
+ }
+ }
+
+ public class Product {
+ // first section handled by a builder
+ public string Name { get; set; }
+ public string Details { get; set; }
+
+ // second section handled by another builder
+ public decimal Price { get; set; }
+ public int DiscountPercentLevel1 { get; set; }
+ public int DiscountPercentLevel2 { get; set; }
+
+
+ public override string ToString()
+ {
+ return $"{Name} : {Details} : {Price} : {DiscountPercentLevel1} : {DiscountPercentLevel2}";
+ }
+ }
+
+ // this acts as a facade
+ public class ProductBuilder
+ {
+ protected Product instance = new Product();
+
+ public ProductInfoBuilder Info => new ProductInfoBuilder(instance);
+ public ProductPriceBuilder Price => new ProductPriceBuilder(instance);
+
+ public Product Build() {
+ return instance;
+ }
+
+ public static implicit operator Product(ProductBuilder pb) => pb.instance;
+ }
+
+ public class ProductInfoBuilder : ProductBuilder {
+ public ProductInfoBuilder(Product instance) {
+ this.instance = instance;
+ }
+
+ public ProductInfoBuilder WithName(string name) {
+ this.instance.Name = name;
+ return this;
+ }
+
+ public ProductInfoBuilder WithDetails(string details) {
+ this.instance.Details = details;
+ return this;
+ }
+ }
+
+ public class ProductPriceBuilder : ProductBuilder {
+ public ProductPriceBuilder(Product instance) {
+ this.instance = instance;
+ }
+
+ public ProductPriceBuilder WithPrice(decimal price) {
+ this.instance.Price = price;
+ return this;
+ }
+
+ public ProductPriceBuilder WithDiscountLevel1(int discount) {
+ this.instance.DiscountPercentLevel1 = discount;
+ return this;
+ }
+
+ public ProductPriceBuilder WithDiscountLevel2(int discount) {
+ this.instance.DiscountPercentLevel2 = discount;
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/creational/builder/FluentBuilderSample.cs b/creational/builder/FluentBuilderSample.cs
new file mode 100644
index 0000000..a9357d9
--- /dev/null
+++ b/creational/builder/FluentBuilderSample.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Threading.Tasks;
+
+namespace design_patterns.creational.fluentbuilder
+{
+ ///
+ /// Builder is a creational design pattern that
+ /// lets you construct complex objects step by step.
+ /// The pattern allows you to produce different types
+ /// and representations of an object using the same construction code.
+ ///
+ /// Fluent builder - limited as it cannot be extended via inheritance
+ ///
+ public class FluentBuilderSample
+ {
+ public static async Task Run() {
+ Console.WriteLine("Creational - Fluent Builder");
+
+ var product = ProductBuilder
+ .CreateProduct()
+ .WithName("New Product")
+ .WithCategory("Best category")
+ .WithAge(30)
+ .WithPrice(45.22m)
+ .Build();
+
+ Console.WriteLine("product: " + product.ToString());
+ }
+ }
+
+ public class Product {
+ public int Age { get; set; }
+ public decimal Price { get; set; }
+ public string ProductName { get; set; }
+ public string Category { get; set; }
+ public string Info { get; internal set; }
+
+ public override string ToString()
+ {
+ return $"{ProductName} : {Category} : {Age} : {Price}";
+ }
+ }
+
+ public interface IProductBuilder :
+ IProductName,
+ IProductAge,
+ IProductPrice,
+ IProductCategory {
+ Product Build();
+ }
+ public interface IProductName {
+ IProductBuilder WithName(string productName);
+ }
+
+ public interface IProductAge {
+ IProductBuilder WithAge(int productAge);
+ }
+
+ public interface IProductPrice {
+ IProductBuilder WithPrice(decimal productPrice);
+ }
+
+ public interface IProductCategory {
+ IProductBuilder WithCategory(string productCategory);
+ }
+
+ public class ProductBuilderWithInfo : ProductBuilder {
+
+ IProductBuilder WithInfo(string info){
+ this.instance.Info = info;
+ return this;
+ }
+
+ }
+
+
+ public abstract class ProductBuilderBase {
+
+ }
+
+ public class ProductBuilder :
+ IProductBuilder
+ {
+ // set as protected to favor inheritance for extensibility of builder class
+ protected Product instance;
+
+ protected ProductBuilder() {}
+ public static ProductBuilder CreateProduct()
+ {
+ var pb = new ProductBuilder() {
+ instance = new Product()
+ };
+ return pb;
+ }
+
+ public Product Build()
+ {
+ return instance;
+ }
+
+ public IProductBuilder WithAge(int productAge)
+ {
+ this.instance.Age = productAge;
+ return this;
+ }
+
+ public IProductBuilder WithCategory(string productCategory)
+ {
+ this.instance.Category = productCategory;
+ return this;
+ }
+
+ public IProductBuilder WithName(string productName)
+ {
+ this.instance.ProductName = productName;
+ return this;
+ }
+
+ public IProductBuilder WithPrice(decimal productPrice)
+ {
+ this.instance.Price = productPrice;
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/creational/builder/FunctionalBuilderSample.cs b/creational/builder/FunctionalBuilderSample.cs
new file mode 100644
index 0000000..0bf398e
--- /dev/null
+++ b/creational/builder/FunctionalBuilderSample.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace design_patterns.creational.functionalbuilder
+{
+ ///
+ /// Builder is a creational design pattern that
+ /// lets you construct complex objects step by step.
+ /// The pattern allows you to produce different types
+ /// and representations of an object using the same construction code.
+ ///
+ /// Functional builder - allow builder extension via extended methods
+ ///
+ public class FunctionalBuilderSample
+ {
+ public static async Task Run() {
+ Console.WriteLine("Creational - Functional Builder");
+
+ var product = new ProductBuilder()
+ .WithName("New Product")
+ .WithCategory("Best category")
+ .WithAge(30)
+ .WithPrice(45.22m)
+ .Build();
+
+ Console.WriteLine("product: " + product.ToString());
+ }
+ }
+
+ public abstract class FunctionalBuilderBase
+ where SELF : FunctionalBuilderBase
+ where T : new()
+ {
+ private readonly List> actions = new List>();
+
+ public SELF Apply(Action action) {
+ actions.Add(t => {
+ action(t);
+ return t;
+ });
+ return this as SELF;
+ }
+
+ public T Build() {
+ return actions.Aggregate(new T(), (t, f) => f(t));
+ }
+
+ }
+ public sealed class ProductBuilder : FunctionalBuilderBase {
+ public ProductBuilder WithName(string name) =>
+ Apply(p => p.ProductName = name);
+ }
+
+ ///
+ /// allow extending the builder
+ ///
+ public static class ProductBuilderExtensions {
+ public static ProductBuilder WithAge(this ProductBuilder builder, int age) =>
+ builder.Apply(p => p.Age = age);
+ public static ProductBuilder WithCategory(this ProductBuilder builder, string category) =>
+ builder.Apply(p => p.Category = category);
+ public static ProductBuilder WithPrice(this ProductBuilder builder, decimal price) =>
+ builder.Apply(p => p.Price = price);
+ }
+
+ public class Product {
+ public int Age { get; set; }
+ public decimal Price { get; set; }
+ public string ProductName { get; set; }
+ public string Category { get; set; }
+ public string Info { get; internal set; }
+
+ public override string ToString()
+ {
+ return $"{ProductName} : {Category} : {Age} : {Price}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/creational/factory/FactorySample.cs b/creational/factory/FactorySample.cs
new file mode 100644
index 0000000..f631ce3
--- /dev/null
+++ b/creational/factory/FactorySample.cs
@@ -0,0 +1,206 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Threading.Tasks;
+
+namespace design_patterns.creational.factory
+{
+ ///
+ /// Factory method
+ /// provides an interface for creating objects in a superclass,
+ /// but allows subclasses to alter the type of objects
+ /// that will be created.
+ ///
+ public class FactorySample
+ {
+ public static async Task Run()
+ {
+ Console.WriteLine("Creational - Factory");
+
+ // not allowed to create new Product
+ // var pA = new ProductA();
+
+ var productFactory = new ProductFactory();
+
+ var productA = productFactory.CreateProduct();
+ Console.WriteLine($"productA is of type { productA.GetType()}");
+ productA.ShowDetails();
+
+ var productB = productFactory.CreateProduct();
+ Console.WriteLine($"productB is of type { productB.GetType()}");
+ productB.ShowDetails();
+
+ var productAasync = await productFactory.CreateProductAsync();
+ Console.WriteLine($"productAasync is of type { productAasync.GetType()}");
+ productAasync.ShowDetails();
+
+ var productBasync = await productFactory.CreateProductAsync();
+ Console.WriteLine($"productBasync is of type { productBasync.GetType()}");
+ productBasync.ShowDetails();
+
+ var productC = productFactory.CreateProduct();
+ productC.ShowDetails();
+ var newProductC = new ProductCFactory().CreateProductC("my extra C product");
+ newProductC.ShowDetails();
+ }
+ }
+
+ public interface IProduct
+ {
+ void Init();
+ Task InitAsync();
+
+ void ShowDetails();
+ string Details {get;set;}
+ }
+
+ public abstract class ProductBase : IProduct
+ {
+ public string Details { get ; set; }
+
+ public virtual void Init()
+ {
+ // default implementation
+ this.Details = $"Default product detail {Guid.NewGuid()}";
+ }
+
+ public virtual Task InitAsync()
+ {
+ // default implementation
+ return Task.FromResult(true);
+ }
+
+ public virtual void ShowDetails()
+ {
+ // default implementation
+ Console.WriteLine($"Default show details for {this.GetType()}: {this.Details}");
+ }
+ }
+ public class ProductA : ProductBase {
+ // protect constructor and prevent user to create it
+ private ProductA() {}
+
+ public override void Init()
+ {
+ base.Init();
+ // fill details from some other source
+ this.Details = $"Details {Guid.NewGuid()}";
+ }
+
+ public override async Task InitAsync()
+ {
+ this.Init();
+ // do some other async initialization
+ // var x = await Do Some stuff
+ await Task.Delay(10);
+ }
+
+ public override void ShowDetails()
+ {
+ Console.WriteLine("ProductA detail: " + this.Details);
+ }
+ }
+
+ public class ProductB : ProductBase {
+ // protect constructor and prevent user to create it
+ private ProductB() {}
+
+ public override void Init()
+ {
+ base.Init();
+ // fill details from some other source
+ this.Details = $"Details {Guid.NewGuid()}";
+ }
+
+ public override async Task InitAsync()
+ {
+ this.Init();
+ // do some other async initialization
+ // var x = await Do Some stuff
+ await Task.Delay(10);
+ }
+ public override void ShowDetails()
+ {
+ Console.WriteLine("ProductB detail: " + this.Details);
+ }
+ }
+
+ public class ProductC : ProductBase
+ {
+ // protect constructor and prevent user to create it
+ private ProductC() {}
+ }
+
+ ///
+ /// Custom factory implemented just for ProductC
+ ///
+ public class ProductCFactory : ProductFactory {
+
+ public IProduct CreateProductC(string extraDetail)
+ {
+ var concreteC = base.CreateProduct();
+ concreteC.Details = "Product from custom factory " + extraDetail;
+ return concreteC;
+ }
+ }
+
+ public class ProductFactory : FactoryBase
+ {
+ public virtual T CreateProduct() where T : class, IProduct {
+ var concrete = CreateInstance();
+ concrete?.Init();
+ return concrete;
+ }
+
+ public virtual async Task CreateProductAsync() where T : class, IProduct {
+ var concrete = CreateInstance();
+ await concrete?.InitAsync();
+ return concrete;
+ }
+ }
+
+ public class FactoryBase
+ {
+ protected T CreateInstance() where T : class {
+ // use of the following approach: activator of compiled expression
+ // var concrete = CreateInstanceWithActivator();
+ var concrete = CreateInstanceWithLambda();
+ return concrete;
+ }
+
+ ///
+ /// Create object instance with Activator
+ ///
+ /// type of created instance
+ private T CreateInstanceWithActivator() where T : class
+ {
+ var concrete = Activator.CreateInstance(typeof(T), true) as T;
+ return concrete;
+ }
+
+ // used for caching lambda compilation expressions to reduce reflection overhead
+ private static Dictionary> activators = new Dictionary>();
+ ///
+ /// Create object instance with lambda compiled expression
+ ///
+ /// type of created instance
+ private T CreateInstanceWithLambda() where T : class
+ {
+ var type = typeof(T);
+ Func