mirror of
https://github.com/farcasclaudiu/design_pattens.git
synced 2026-06-22 07:01:29 +03:00
WIP creational patterns
This commit is contained in:
+20
-2
@@ -1,12 +1,30 @@
|
|||||||
using System;
|
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
|
namespace design_patterns
|
||||||
{
|
{
|
||||||
class Program
|
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}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace design_patterns.creational.abstract_factory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
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<string, IProductFactory> factories = new Dictionary<string, IProductFactory>() {
|
||||||
|
{"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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace design_patterns.creational.facadebuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// this sample covers the case when we have multiple builders
|
||||||
|
/// for the same class, that we want to chain
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace design_patterns.creational.fluentbuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace design_patterns.creational.functionalbuilder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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
|
||||||
|
/// </summary>
|
||||||
|
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<T, SELF>
|
||||||
|
where SELF : FunctionalBuilderBase<T, SELF>
|
||||||
|
where T : new()
|
||||||
|
{
|
||||||
|
private readonly List<Func<T, T>> actions = new List<Func<T, T>>();
|
||||||
|
|
||||||
|
public SELF Apply(Action<T> 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<Product, ProductBuilder> {
|
||||||
|
public ProductBuilder WithName(string name) =>
|
||||||
|
Apply(p => p.ProductName = name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// allow extending the builder
|
||||||
|
/// </summary>
|
||||||
|
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}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Factory method
|
||||||
|
/// provides an interface for creating objects in a superclass,
|
||||||
|
/// but allows subclasses to alter the type of objects
|
||||||
|
/// that will be created.
|
||||||
|
/// </summary>
|
||||||
|
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<ProductA>();
|
||||||
|
Console.WriteLine($"productA is of type { productA.GetType()}");
|
||||||
|
productA.ShowDetails();
|
||||||
|
|
||||||
|
var productB = productFactory.CreateProduct<ProductB>();
|
||||||
|
Console.WriteLine($"productB is of type { productB.GetType()}");
|
||||||
|
productB.ShowDetails();
|
||||||
|
|
||||||
|
var productAasync = await productFactory.CreateProductAsync<ProductA>();
|
||||||
|
Console.WriteLine($"productAasync is of type { productAasync.GetType()}");
|
||||||
|
productAasync.ShowDetails();
|
||||||
|
|
||||||
|
var productBasync = await productFactory.CreateProductAsync<ProductB>();
|
||||||
|
Console.WriteLine($"productBasync is of type { productBasync.GetType()}");
|
||||||
|
productBasync.ShowDetails();
|
||||||
|
|
||||||
|
var productC = productFactory.CreateProduct<ProductC>();
|
||||||
|
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() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom factory implemented just for ProductC
|
||||||
|
/// </summary>
|
||||||
|
public class ProductCFactory : ProductFactory {
|
||||||
|
|
||||||
|
public IProduct CreateProductC(string extraDetail)
|
||||||
|
{
|
||||||
|
var concreteC = base.CreateProduct<ProductC>();
|
||||||
|
concreteC.Details = "Product from custom factory " + extraDetail;
|
||||||
|
return concreteC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProductFactory : FactoryBase
|
||||||
|
{
|
||||||
|
public virtual T CreateProduct<T>() where T : class, IProduct {
|
||||||
|
var concrete = CreateInstance<T>();
|
||||||
|
concrete?.Init();
|
||||||
|
return concrete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<T> CreateProductAsync<T>() where T : class, IProduct {
|
||||||
|
var concrete = CreateInstance<T>();
|
||||||
|
await concrete?.InitAsync();
|
||||||
|
return concrete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FactoryBase
|
||||||
|
{
|
||||||
|
protected T CreateInstance<T>() where T : class {
|
||||||
|
// use of the following approach: activator of compiled expression
|
||||||
|
// var concrete = CreateInstanceWithActivator<T>();
|
||||||
|
var concrete = CreateInstanceWithLambda<T>();
|
||||||
|
return concrete;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create object instance with Activator
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">type of created instance</typeparam>
|
||||||
|
private T CreateInstanceWithActivator<T>() 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<Type, Func<object>> activators = new Dictionary<Type, Func<object>>();
|
||||||
|
/// <summary>
|
||||||
|
/// Create object instance with lambda compiled expression
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">type of created instance</typeparam>
|
||||||
|
private T CreateInstanceWithLambda<T>() where T : class
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
Func<object> activator;
|
||||||
|
if(!activators.TryGetValue(type, out activator)){
|
||||||
|
var ctor = type.GetConstructor(
|
||||||
|
BindingFlags.Instance | BindingFlags.CreateInstance |
|
||||||
|
BindingFlags.NonPublic,
|
||||||
|
null, new Type[] { }, null);
|
||||||
|
|
||||||
|
var ctorExpression = Expression.New(ctor);
|
||||||
|
activator = Expression.Lambda<Func<T>>(ctorExpression).Compile();
|
||||||
|
activators.Add(type, activator);
|
||||||
|
}
|
||||||
|
var concrete = activator() as T;
|
||||||
|
return concrete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user