7

Is this Simple Factory violating the Open Closed Principle?

The SimpleProductFactory needs to change every time a new concrete product needs to be created but it adheres to the single responsibility principle because that is the only reason it will ever change. Its sole purpose is so that the Client does not violate the open closed principle so I imagine it can't be a violation itself since obviously this code is needed somewhere.

I am not interested in changing the factory but whether this specific example is a violation or not.

Product

interface Product{
  public int getPrice();
}

Milk

class Milk implements Product{
  public int getPrice(){ return 5; }
}

Chips

class Chips implements Product{
  public int getPrice(){ return 3; }
}

SimpleProductFactory

class SimpleProductFactory{

  public Product createProduct(String productName){

    if(productName.equals("milk")){
      return new Milk();
    }
    else if(productName.equals("chips")){
      return new Chips();
    }
    return null;
  }
}

Client

class Client{
  public static void main(String[] args) {
    SimpleProductFactory productFactory = new SimpleProductFactory();
    Product prod = productFactory.createProduct("milk");
    System.out.println(prod.getPrice());

  }
}
  • Are you pointing to `SimpleProductFactory ` class ? – Ravi Dec 31 '17 at 15:45
  • @Ravi I am not sure what you mean. If this was a real program I imagine I would have SimpleProductFactory used in multiple places but SimpleProductFactory would not have more methods added to it. – LangLearn Korean Dec 31 '17 at 15:48
  • I think that you want to use an [abstract factory](https://stackoverflow.com/a/13030163/522444) for this. Edit: as Timothy Truckle mentions in his answer (1+) – Hovercraft Full Of Eels Dec 31 '17 at 15:49
  • I think what Ravi means is that if you inject a `SimpleProductFactory` into the `Client` rather than instantiate it directly, you can subclass the factory to change its behavior without violating OCP. – jaco0646 Nov 28 '18 at 14:21

4 Answers4

2

Is this Simple Factory violating the Open Closed Principle?

To answer your questions. "Yes, Simple Factory violates the Open Closed Principle for a reason."

The Simple Factory pattern supposed to be modified in order to help us choosing specific class to the caller. If we make this class conforming to open closed principle then we have to shift burden to some other class and this class will not serve the purpose of factory anymore. Not all principles are absolute. We need to weigh the benefits when using or when not using.

fabfas
  • 2,100
  • 1
  • 19
  • 20
2

In addition to Timothy Truckle answer about the service locator...

In Java 8 you might want to use method references and the Supplier interface to implement a generic factory for such simple use cases like yours.

E.g.

class SimpleProductFactory {

    private Map<String, Supplier<? extends Product>> supplierRegistry = new HashMap<>();

    public void addProductSupplier(String productName, Supplier<? extends Product> productSupplier) {
        supplierRegistry.put(productName, productSupplier);
    }

    public Product createProduct(String productName) {
        Product product = null;

        Supplier<? extends Product> productSupplier = supplierRegistry.get(productName);

        if (productSupplier != null) {
            product = productSupplier.get();
        }

        return product;
    }
} 

And your client code will look like this

class Client{
  public static void main(String[] args) {
    SimpleProductFactory productFactory = new SimpleProductFactory();

    productFactory.addProductSupplier("milk", Milk::new); // Constructor reference
    productFactory.addProductSupplier("chips", Chips::new);

    Product prod = productFactory.createProduct("milk");
    System.out.println(prod.getPrice());

  }
}

As you can see the simple factory is

  • open for extension, because you can simple add other product suppliers
  • closed for modification, because you don't need to change it when another product is implemented you just add it.

PS: With a bit more refactoring you can simply turn it into a real generic factory for any type.

René Link
  • 43,842
  • 12
  • 98
  • 127
0

The open/closed principle does not really apply to factories because after all they are the very source of the different typed objects...

On the other hand you could have an Abstract Factory looking up "real" factories using javas ServiceLoader. Then you could add more of that real factories even in their own jars without changing existing code...

Timothy Truckle
  • 13,829
  • 2
  • 24
  • 49
  • 1
    The OCP applies perfectly to both of the GoF factory patterns (Abstract Factory and Factory Method). It does not apply to the Simple Factory pattern because that was invented by Head First Design Patterns as a teaching aid. It is not intended for production code. – jaco0646 Nov 28 '18 at 14:10
  • @jaco0646, how does it apply to Factory Method? If you have new implementations they will have to be put somewhere, and this somewhere will thus have to be touched. – Felipe Martins Melo Aug 21 '19 at 10:13
  • @FelipeMartinsMelo The `ServiceLoader` looks up implementations of a given interface in the class path. The Code using a factory only needs to know the interface the factory implements (the *abstact factory*) and the interface the produced objects implement (both are the same for all concrete factories). You provide a new Factory by createing a file in the `META-INF` folder of the **new** jar having the FQN of the interface the factory implements (and the ServiceLoader looks for) as name containing the FQN of the new factory implementation as plain text. – Timothy Truckle Aug 21 '19 at 10:21
  • @jaco0646, yes, from this perspective I agree. – Felipe Martins Melo Aug 21 '19 at 11:08
0

The static factory example given above is still extendable for additional products like Icecream as follows. Even if we make the factory metod static still it can be extended (though not by conventionally overriding the method)

    class SimpleProductFactoryExt extends SimpleProductFactory {
        public Product createProduct(String productName) {
            if (productName.equals("Icecream")) {
                return new Milk();
            } else {
                return super.createProduct(productName);
            }
        }
    }
Kedar Tokekar
  • 408
  • 3
  • 10