Showing posts with label Patterns Abstract factory pattern C#. Show all posts
Showing posts with label Patterns Abstract factory pattern C#. Show all posts

Wednesday 6 April 2022

Abstract factory - registering factory methods

Today, instead of relying on factories we often resort to dependency injection. However, you can 'blueprint' objects by offering a helper class to register both interfaces with concrete classes and default parameters if you want to instantiate a type of object with given parameters using an abstract factory class The following code shows how we can start building such a registration approach, supporting also unregistering factories and creating objects which are registered. You will have to add thread safety and better checking if a given type T and parameters P1..PN are registered, here is though a simple Abstract factory. Paste the code into Linqpad 6 or Linqpad 7 to test it out !
 
 
 void Main()
{

 var factory = new AbstractFactory(); 
 
 factory.Register<IDrive>(() => new FoulwheeledDrive());
 factory.Register<Car, string, string, IDrive>((string make, string model, IDrive drive) => new Car(make, model, drive)); 
 
 var car = factory.Create<Car, string, string, IDrive>("BMW", "M5", factory.Create<IDrive>());
 
 car.ToString().Dump();
 
 //can also unregister a factory method
 //factory.Unregister<Car, string, string, IDrive>();
 //var car2 = factory.Create<Car, string, string, IDrive>("BMW", "M5", factory.Create<IDrive>());
 
	
}

public interface IVehicle {
 string Model { get; set; }
 string Make { get; set; }
}

public interface IDrive {
 string Drive();
}

public class FoulwheeledDrive : IDrive {
 public string Drive() => "Car driving with four wheeled motion";
}

public class Car  {

  public string Make { get; set; }  
  public string Model { get; set; }
  
  public IDrive Drive { get; set; }
 
  public Car(string model, string make, IDrive drive){
    Model = model; 
 	Make = make;
	Drive = drive;
  }
  
  public override string ToString() => $"{Model} {Make} : {Drive.Drive()}";
 
}

public class AbstractFactory {

 private readonly Dictionary<Type, Delegate> _factories = new (); 
 
 
 public void Register<T>(Func<T> factory) => _factories[typeof(Func<T>)] = factory;
 public void Register<T,P>(Func<P,T> factory) => _factories[typeof(Func<P,T>)] = factory;
 public void Register<T,P1, P2>(Func<P1, P2, T> factory) => _factories[typeof(Func<P1, P2, T>)] = factory;
 public void Register<T, P1, P2, P3>(Func<P1, P2, P3, T> factory) => _factories[typeof(Func<P1, P2, P3, T>)] = factory;
 public void Register<T, P1, P2, P3, P4>(Func<T> factory) => _factories[typeof(Func<P1, P2, P3, P4, T>)] = factory;
 public void Register<T, P1, P2, P3, P4, P5>(Func<T> factory) => _factories[typeof(Func<P1, P2, P3, P4, P5, T>)] = factory;
 
 public void Unregister<T>() => _factories.Remove(typeof(Func<T>)); 
 public void Unregister<T,P>() => _factories.Remove(typeof(Func<P,T>));
 public void Unregister<T,P1, P2>() => _factories.Remove(typeof(Func<P1, P2, T>));
 public void Unregister<T,P1, P2, P3>() => _factories.Remove(typeof(Func<P1, P2, P3, T>)); 
 public void Unregister<T,P1, P2, P3, P4>() => _factories.Remove(typeof(Func<P1, P2, P3, P4, T>));
 public void Unregister<T,P1, P2, P3, P4, P5>() => _factories.Remove(typeof(Func<P1, P2, P3, P4, P5, T>));
 
 public T Create<T>() => ((Func<T>)_factories[typeof(Func<T>)])();
 public T Create<T, P>(P p) => ((Func<P,T>)_factories[typeof(Func<P,T>)])(p);
 public T Create<T, P1, P2>(P1 p1, P2 p2) => ((Func<P1, P2,T>)_factories[typeof(Func<P1, P2,T>)])(p1, p2);
 public T Create<T, P1, P2, P3>(P1 p1, P2 p2, P3 p3) => ((Func<P1, P2, P3, T>)_factories[typeof(Func<P1, P2, P3, T>)])(p1, p2, p3);
 public T Create<T, P1, P2, P3, P4>(P1 p1, P2 p2, P3 p3, P4 p4) => ((Func<P1, P2, P3, P4, T>)_factories[typeof(Func<P1, P2, P3, P4, T>)])(p1, p2, p3, p4);
 public T Create<T, P1, P2, P3, P4, P5>(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) => ((Func<P1, P2, P3, P4, P5, T>)_factories[typeof(Func<P1, P2, P3, T>)])(p1, p2, p3, p4, p5);
 
}


 
 

The registration of the factories then instantiate the instance. Note that we do not use the new operator when instantiating the object. We avoid using Activator.CreateInstance and reflection using this approach. Why use this pattern when we want to create 'blueprint' objects and want to have standard ways of instantiating them and for example avoid using concrete types. Most IoC frameworks allow you to create compound classes and supply parameters, so the abstract factory pattern seems a bit 'crude', but it is used many places still.