I always had problems in grasping the full benefits/motif behind using the Factory design pattern (for this post, I will stick to Factory Method pattern
, specifically). True, there are (really) lots of posts over the internet on the topic. But I always had a hard time summarizing the pattern to the problem it comes to solve and how does it solve it.
After one more try researching the internet on the topic, reading posts and watching videos, I think I finally see what does it accomplish (particularly the 3rd bullet below), and that without it, I can't get rid of the problem it comes to solve (assuming I know no other design patterns).
Here's how I see it:
The problem:
- Object creation should not be scattered throughout our application, rather centralized in one single piece of code (this far this is not the pattern itself, just a "code construct"). Nothing really thrilling at this point yet - since this is merely the Single Responsibility (S from SOLID) software engineering principle being put to practice, once again.
- Object creation should be decoupled between the creating and the consuming parts. Otherwise, additions of new types / changes in existing types will require changing the consumer. Here I believe the pattern is helping us enforce the O from SOLID - open for extension, closed for modification. In other words, any changes in the requirements may extend the software design, but should never require modifying existing components.
- The Factory Method, particularly, is a way to enforce the Open/Closed principle - not only in the scenario described above - but specifically in adding new types (new requirements). If one goes with the "factory construct", without the factory method pattern, one has a code construct that accordingly chooses and creates, in a single piece of code, an instance of an object that implements the required interface the client consumes. This helps with S (or DRY), but without the factory method pattern - adding a new type still requires modifying the factory construct, and we again violate the Open/Closed principle. With the factory method pattern, on the other hand, adding new types does not require modifying existing code. The developer will extend the system, adding a new type, and that's all (this is the gain I had been missing till today).
Pseudo-code illustration:
/*-------------------- client ----------------------------*/
var p1 = new P1Factory.CreateProduct(...);
//new requirement, 2 weeks later
var p2 = new P2Factory.CreateProduct(...);
//2 weeks later
var p3 = new P3Factory.CreateProduct(...);
/*--------------------------------------------------------*/
/*
factory construct - not the ultimate solution, a starting point,
but not the pattern itself, since it still leaves us with (part of)
the problem we wanted to solve
*/
createProduct(enum type)
{
switch(type)
{
case type.P1:
return new P1();
case type.P2:
return new P2();
}
}
//factory method pattern:
interface IProduct
{
doWork();
}
abstract class Factory
{
IProduct CreateProduct(string[] settings);
}
class P1Factory : Factory
{
public IProduct CreateProduct(...)
{
return new P1();
}
}
public class P1 : IProduct
{
public void doWork() { }
}
So adding a new P4 type only requires extending the code base by adding a new class that implements both the interface and the abstract factory.
Is anything I wrote above wrong ? Am I still missing important points in understanding the motifs behind the pattern?
abstract class xxx
does not make a "Factory" an "Abstract Factory." Look at the Builder pattern which could be used in any factory pattern. Beware over-designing due to overly-literal Open/Close interpretation.