1

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?

  • Centralizing is not the main point of Factories - you could achieve that much easier with any kind of utility method + a private constructor. The big win of a Factory is the abstraction from concrete implementation types. – Kilian Foth Jul 20 at 12:57
  • First, object creation, broadly in the abstract, is not a responsibility; responsibilities should be more domain oriented. Second, your illustration shows the client changing source code routinely, whereas I would expect the factory to change so the client doesn't have to. – Erik Eidt Jul 20 at 13:40
  • @ErikEidt: I am now confused, but I think my "client" snippet is still ok because changes do not break a thing. The next software release will include new Factory instantiations. Otherwise - let's stick with your point. Even if the object instantiation is all wrapped into a factory construct, still, to instantiate types added between the last release and the new one - we still have to change somehow the client code to pass the parameter to the factory construct that will return an instance of the new type. Indeed, the point in question is always confusing... – Veverke Jul 20 at 17:57
  • @ErikEidt: I am assuming all the way that our goal is to achieve a situation where we will fully implement the open/closed principle. If this still breaks this principle, then how is this achieved... – Veverke Jul 20 at 17:58
  • 1
    "Factory method", "Factory", and "Abstract Factory" are 3 distinct sub-types addressing increasing complexity. "Factory Method" is a class method, probably static, but not a separate class. "Abstract Factory" pattern is a framework for very complex construction, the classic example is the pizza factory - multiple pizza types each with variable options. 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. – radarbob Jul 20 at 18:45
1

Is anything I wrote above wrong ? Am I still missing important points in understanding the motifs behind the pattern?

No, what you wrote seems pretty reasonable.

There are two big things I think might help:

  1. You seem to be a bit focused on SOLID, when your second bullet is more key. SOLID itself is meant to help decouple things, and generally lead to better software design. The Factory decouples the creation from the consumption, allowing you more flexibility on what type to create, and how to create it. Good software is the goal, not following the guidelines.
  2. Coupling isn’t something you solve. There is tight coupling and loose coupling but rarely ever no coupling for things that need to usefully work together. Indeed, loosening coupling at one point often leads to tighter coupling elsewhere. The Factory is a good example of this. Instead of coupling the consumers with the types they create fairly tightly, the Factory tightly couples with them while using an interface to more loosely couple with the consumers. That leaves you more free to change the code that is more likely to change, at the cost of a little complexity (the Factory itself) and tight coupling somewhere that is less likely to change.
| improve this answer | |
  • Thanks a lot for leaving your bit, particularly the 2nd remark. Adds relief :) – Veverke Jul 20 at 19:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.