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