0

In factory method, we have to write switch case or if statements to decide which instance to create and return. This violates the open close principle because - everytime a new product is to be added, the factory method code has to be updated.

Is there any way to make this dynamic - so that adding a new product does not require any change to the factory method?

For example: In python we can import package that has list of all available products. The factory method loads the dict data structure with all available products along with their class. Hence the factory code need not change when new product is added. It is sufficient that only the product is added to the package code that the factory uses to import the products classes.

variable
  • 8,262
  • 9
  • 95
  • 215
  • You could... but it really depends on how much you want it. You can use the reflection to find all types that are derived from a basetype. And based on some criteria... like name of type or of a public constant identifier on the type decide which one to activate. But its a bit heavy. And even though it is "open close". I'd advice to use these principles as tools that help you. Not as something that confine you to code that is hard to read, understand and maintain. If you want to go down the reflection path. I can add an example as answer if you want. – Davey van Tilburg Apr 20 '20 at 17:23

1 Answers1

0

I don't know if this is exactly something that can help you, but if you are using a parameterless constructor on your product classes, you can use this example. Of course, implementation can be different based on your needs. The product classes can be defined in a different assembly, too.

/// <summary>
/// Product common interface.
/// </summary>
public interface IProduct
{
}
/// <summary>
/// Definition of an apple product.
/// </summary>
public class Apple : IProduct
{
    public string Gauge { get; set; }
}
/// <summary>
/// Definition of a pear product.
/// </summary>
public class Pear : IProduct
{
    public string Sweetness { get; set; }
}
/// <summary>
/// Factory class.
/// </summary>
public class ProductFactory
{
    /// <summary>
    /// List of product types found in the current assembly.
    /// </summary>
    private static IList<Type> ProductTypes = new List<Type>();
    /// <summary>
    /// Initializes the list of product types.
    /// </summary>
    public static void Initialize()
    {
        foreach (Type prodType in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
            .Where(prodType => prodType.GetInterfaces().Contains(typeof(IProduct))))
        {
            ProductTypes.Add(prodType);
        }
    }
    /// <summary>
    /// Factory Get function.
    /// </summary>
    /// <typeparam name="T">Generic implementing IProduct.</typeparam>
    /// <returns>Instance of the generic implementing IProduct.</returns>
    public static T GetProduct<T>() where T : IProduct, new()
    {
        var productType = ProductTypes.Where(x => x.Name.Equals(typeof(T).Name)).FirstOrDefault();
        if (productType is null) return default;
        return (T)Activator.CreateInstance(productType);
    }
}
/// <summary>
/// Entrypoint class.
/// </summary>
public static class Program
{
    /// <summary>
    /// Entrypoint method.
    /// </summary>
    public static void Main(string[] args)
    {
        // Initialize the list of product types.
        ProductFactory.Initialize();
        // Get an apple.
        var apple = ProductFactory.GetProduct<Apple>();
        // Get a pear.
        var pear = ProductFactory.GetProduct<Pear>();
    }
}
FandangoOnCore
  • 269
  • 2
  • 9