Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory design pattern and violation of OCP (Open-Closed Principle)

The factory in this tutorial obviously violates the OCP. Every time a shape is added to the system, we need to add it in the factory to support it. I'm thinking of another implementation and I'd like to know if there are any drawbacks.

public class ShapeFactory {

   //use getShape method to get object of type shape
   public Shape getShape(Class<? extends Shape> shapeType){
      return shapeType.newInstance();
   }
}

This implementation looks it doesn't violate OCP, and isn't complex. Is there any reason that I can't find any factory design pattern tutorial that mentions it?

like image 372
Youssef13 Avatar asked Apr 12 '20 07:04

Youssef13


People also ask

Does factory pattern violates Open-Closed Principle?

No, it doesn't violate the Open/Closed principle at all. Open/Closed means you can modify the way a system works without modifying the code that already exists.

Which design pattern uses Open-Closed Principle?

In a nutshell, the developer must need to change only a specific part of the code (a class or a function) every time a requirement changes. Using a statically typed language like Java, C#, etc. the open/closed principle is generally achieved by using inheritance and polymorphism.

Which solid principle is followed by factory design pattern?

The factory method pattern follows the Open-Closed principle. The product and creators are closed for modification but can extend and create new products and concrete factories. Here, we depend on abstraction not on concrete implementations.

Why is the Open-Closed Principle so important in the decorator pattern?

The open-closed principle encourages extension over modification which is exactly what the decorator pattern does — if you realize that your existing component needs additional logic, the decorator pattern allows you to add this without modifying the original class in any way.


1 Answers

There are a few drawbacks to this method.

Firstly, when the Class passed to getShape requires a constructor argument, the .newInstance will fail. For example:

public class Circle {
   public Circle(int diameter) {
      //something
   }
}

You could get into reflection by using getConstructor and figuring out what arguments to pass, but that is complex and error prone. And you lose type safety at compilation time. And how would the factory class know what values to pass to diameter?

One of the advantages of the factory design pattern is that the caller doesn't have to know what implementing class is used when calling. Take the following example:

public class ShapeFactory {
   public Shape getCircle(int diameter){
      return new Circle(int diameter);
   }
}

Whenever you call this method, the caller doesn't have need a dependency on the Circle class:

Shape s = ShapeFactory().getCircle(10);
s.draw();

In this way, only the ShapeFactory depends on the Circle. So when you change or replace the Circle class, only the ShapeFactory has to change.

To create make the shape program OCP compliant, we could replace the ShapeFactory with a dependency injection framework. The below code is pseudo-code that shows how this could work

// define the classes
class Circle {}
class Square {}

// for every class, provide a factory method. These do not have to exist inside a factory class.
public Circle createCircle() {
    return new Circle(10)
}

public Circle createSquare() {
    return new Square(42, 5)
}


public class Painter {
    //when a class requires an instance of the Circle class, the dependency injection framework will find `createCircle`, call it and add the result as an argument here.
    public Painter(Circle circle) {
       circle.draw();
    }
}


//when you need a painter object, we don't create it yourself but let the dependency framework do the heavy lifting
Painter p = dependencyframework.getInstanceOf(Painter.class)

There are many Java Dependency Injection frameworks but they all work something like this.

These frameworks do the exact same thing what you propose (things like newInstance and getConstructor, but a lot more of those), they just hide all the reflection complexity.

like image 106
Sijmen Avatar answered Oct 31 '22 01:10

Sijmen