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:
- benefit of compile time checks that the implementation class will be
forced to provide the corresponding methods.
Approach 2:
- Implementation will have a benefit of shorter no. of lines of code.
- Interface does not become fragile, it won’t change as the new
products keep on adding.
Cons of both approaches
Approach 1:
- Lines of code becomes large.
- As more number of Products are implemented, the interface changes
and hence it is hard to follow Liskov’s Substitution Principle.
Approach 2:
- 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. - 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.
2
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();
}
}
5
Approach 2 is the classical Factory Pattern. This pattern is nothing to do with LSP. To ensure LSP is not violated in the client programs, you need to carefully design IProduct
interface in such a way that the client program that is operating on IProduct
would continue to operate without any modification irrespective of the concrete product that actually implements IProduct
. If, you observe you are violating LSP redesign the interfaces.
For example it may be better to create one interface for a category of products if these categories differ in the way client communicates with them.
public interface IPerfume
{
//Methods specific perfume products
}
public interface IAutomobile
{
//Methods specific to automobiles
}
Then, you define one factory for each category of products. PerfumeFactory
to create perfume products and AutomobileFactory
to create automobiles.
The example may not sound fancy but that is all I can think of now.
1