C# – Confusion with Factory pattern regarding Liskov’s Substitution Principle, code maintainability and Unit Testing

cdesigndesign-patternslanguage-agnosticliskov-substitution

I have a confusion regarding Factory Pattern there are basically two ways You can implement that.

Approach 1:

public interface IProductFactory
{
    IProduct GetProductA();
    IProduct GetProductB();
    IProduct GetProductC();
}

public class ProductFactory : IProductFactory
{
    public IProduct GetProductA()
    {
        //some implementation goes here
    }
    public IProduct GetProductB()
    {
        //some implementation goes here
    }
    public IProduct GetProductC()
    {
        //some implementation goes here
    }
}

Approach 2:

public enum ProductType
{
    ProductA = 1,
    ProductB = 2,
    ProductC = 3
}

public interface IProductFactory
{
    IProduct GetProduct(ProductType productType);
}

public class ProductFactory : IProductFactory
{
    public IProduct GetProduct(ProductType productType)
    {
        switch (productType)
        {
            case ProductType.ProductA:
                //return IProduct with specific implementation
                break;
            case ProductType.ProductB:
                //return IProduct with specific implementation
                break;
            case ProductType.ProductC:
                //return IProduct with specific implementation
                break;
            default:
            //return null
        }
    }
}

Pros of both approaches

Approach 1:

  1. benefit of compile time checks that the implementation class will be
    forced to provide the corresponding methods.

Approach 2:

  1. Implementation will have a benefit of shorter no. of lines of code.
  2. Interface does not become fragile, it won't change as the new
    products keep on adding.

Cons of both approaches

Approach 1:

  1. Lines of code becomes large.
  2. As more number of Products are implemented, the interface changes
    and hence it is hard to follow Liskov's Substitution Principle.

Approach 2:

  1. Since it is hiding internal details of GetProduct, it is hard to
    tell what the Factory will return when a specific type of enum is
    passed.
  2. You might get into run time exceptions

I am still struggling to decide on which approach to use?


Note: I am only aware of these two approaches, there might be different approaches also.

Best Answer

Both approaches break Liskov Substitution Principle. Correct implementation will look smth. like:

public interface IProductFactory
{
    IProduct GetProduct();
}

public class Product1Factory : IProductFactory
{
    public IProduct GetProduct()
    {
        return new Product1(); //configure instance here
    }
}

public class Product2Factory : IProductFactory
{
    public IProduct GetProduct()
    {
        return new Product2(); //configure instance here
    }
}

public class Product3Factory : IProductFactory
{
    public IProduct GetProduct() //configure instance here
    {
        return new Product3();
    }
}
Related Topic