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 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>(ctorExpression).Compile(); activators.Add(type, activator); } var concrete = activator() as T; return concrete; } } }