Шаблон фабричного проектирования и нарушение OCP (принцип Open-Closed) - PullRequest
5 голосов
/ 12 апреля 2020

Фабрика в этого урока явно нарушает OCP. Каждый раз, когда фигура добавляется в систему, нам нужно добавить ее на заводе, чтобы поддержать ее. Я думаю о другой реализации, и я хотел бы знать, есть ли какие-либо недостатки.

public class ShapeFactory {

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

Эта реализация выглядит так, что она не нарушает OCP и не является сложной. Есть ли какая-то причина, по которой я не могу найти учебник по фабричному дизайну, в котором упоминается об этом?

Ответы [ 2 ]

3 голосов
/ 12 апреля 2020

У этого метода есть несколько недостатков.

Во-первых, когда класс, переданный в getShape, требует аргумента конструктора, .newInstance завершится ошибкой. Например:

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

Вы можете получить отражение, используя getConstructor и выяснить, какие аргументы передать, но это сложно и подвержено ошибкам. И вы теряете безопасность типов во время компиляции. И как класс фабрики узнает, какие значения передать диаметру?

Одним из преимуществ шаблона проектирования фабрики является то, что вызывающая сторона не должна знать, какой класс реализации используется при вызове. Возьмите следующий пример:

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

Когда вы вызываете этот метод, вызывающая программа не нуждается в зависимости от класса Circle:

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

Таким образом, только ShapeFactory зависит от Circle. Поэтому, когда вы изменяете или заменяете класс Circle, нужно изменить только ShapeFactory.

Чтобы создать совместимую с OCP программу для фигур, мы могли бы заменить ShapeFactory средой для внедрения зависимостей. Приведенный ниже код является псевдокодом, который показывает, как это может работать

// 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)

Существует много Java структур внедрения зависимостей, но все они работают примерно так.

Эти платформы выполняют точные действия То же самое, что вы предлагаете (такие вещи, как newInstance и getConstructor, но их гораздо больше), они просто скрывают всю сложность отражения.

2 голосов
/ 12 апреля 2020

Я думаю, что ответ @hfontanez на вопрос «Заводской паттерн нарушает принцип Открытого / Закрытого?» охватывает. Если вы добавляете новые подклассы Shape, вы также должны каким-то образом добавить способ их создания. Предположим, вы не можете изменить исходный ShapeFactory, потому что он является частью сторонней библиотеки, но вы можете создать подкласс или украсить исходную фабрику, чтобы добавить поддержку новых фигур. Расширение примера будет выглядеть следующим образом:

public class AdvancedShapeFactory {
  private final ShapeFactory factory = new ShapeFactory();

  public Shape getShape(String shapeType) {
    if (shapeType.equalsIgnoreCase("PENTAGON")) {
      return new Pentagon();
    } else {
      return factory.getShape(shapeType);
    }
  }    
}

Если разработчик оригинальной вымышленной библиотеки «Фигуры» хотел упростить создание новых фигур по типу фигуры, он мог бы реализовать реестр:

public class ShapeRegistry {
  private static final Map<String, Class<Shape>> shapeTypes = new HashMap<>();

  public void registerShape(String shapeType, Class<Shape> shapeClass) {
    shapeTypes.put(shapeType, shapeClass);
  }

  public Shape getShape(String shapeType) throws InstantiationException, IllegalAccessException {
    if (shapeTypes.containsKey(shapeType)) {
      return shapeTypes.get(shapeType).newInstance();
    }
    return null;
  }
}

Стоит прочитать о внедрении зависимостей и Guice в качестве хорошего примера.

...