Полиморфные фабрики (Polymorphic factories)


Статический метод factory( ) в предыдущем примере заставляет все операции создания сфокусировать в одном месте, так что вы должны менять код только в одном месте. Это, несомненно, резонное решение, в этом случае мы получаем как бы ящик вокруг процесса создания объектов. Однако, книга Design Patterns акцентирует, что поводом для Метода Фабрики является то, что различные типы фабрик могут быть подклассами базовой фабрики (вышеприведенный дизайн упоминается, как особый случай). Но, в книге не приводится пример, а вместо этого повторяется пример, использованный для Абстрактной Фабрики (вы увидите этот пример в следующем разделе). Здесь же приводится модифицированный пример ShapeFactory1.java, так что методы-фабрики находятся в различных классах, как виртуальные функции. Обратите внимание, что определенный класс Формы (Sahpe) загружается динамически по требованию:

//: factory:shapefact2:ShapeFactory2.java
// .
package factory.shapefact2;

import java.util.*;

import junit.framework.*;

interface Shape {
  
void draw();
  
  
void erase();
}

abstract class ShapeFactory {
  
protected abstract Shape create();
  
  
private static Map factories = new HashMap();
  
  
public static void addFactory(String id, ShapeFactory f) {
     
factories.put(id, f);
  
}
  
  
// :
  
public static final Shape createShape(String id) {
     
if (!factories.containsKey(id)) {
        
try {
           
//
           
Class.forName("factory.shapefact2." + id);
        
}
        
catch (ClassNotFoundException e) {
           
throw new RuntimeException("Bad shape creation: " + id);
        
}
        
// :
        
if (!factories.containsKey(id))
           
throw new RuntimeException("Bad shape creation: " + id);
     
}
     
return ((ShapeFactory) factories.get(id)).create();
  
}
}

class Circle implements Shape {
  
private Circle() {
   }
  
  
public void draw() {
     
System.out.println("Circle.draw");
  
}
  
  
public void erase() {
     
System.out.println("Circle.erase");
  
}
  
  
private static class Factory extends ShapeFactory {
     
protected Shape create() {
        
return new Circle();
     
}
   }
  
  
static {
     
ShapeFactory.addFactory("Circle", new Factory());
  
}
}

class Square implements Shape {
  
private Square() {
   }
  
  
public void draw() {
     
System.out.println("Square.draw");
  
}
  
  
public void erase() {
     
System.out.println("Square.erase");
  
}
  
  
private static class Factory extends ShapeFactory {
     
protected Shape create() {
        
return new Square();
     
}
   }
  
  
static {
     
ShapeFactory.addFactory("Square", new Factory());
  
}
}

public class ShapeFactory2 extends TestCase {
  
String shlist[] = { "Circle", "Square", "Square", "Circle", "Circle",
        
"Square" };
   List shapes =
new ArrayList();
  
  
public void test() {
     
// ,
      // .
     
Iterator it = Arrays.asList(shlist).iterator();
     
while (it.hasNext())
        
shapes.add(ShapeFactory.createShape((String) it.next()));
      it = shapes.iterator
();
     
while (it.hasNext()) {
        
Shape s = (Shape) it.next();
         s.draw
();
         s.erase
();
     
}
   }
  
  
public static void main(String args[]) {
     
junit.textui.TestRunner.run(ShapeFactory2.class);
  
}
}
// /:~

Теперь метод фабрики появился в своем собственном классе ShapeFactory, как метод create( ). Это метод с уровнем доступа protected, что означает, что он не может быть вызван напрямую, но он может быть перегружен. Каждый из производных классов от Формы (Shape) должен создать свой собственный подкласс ShapeFactory и перегрузить метод create( ), который создает объект своего собственного типа. Реальное создание формы выполняется путем вызова метода ShapeFactory.createShape( ), который является статическим методом, использующим класс Map в классе ShapeFactory для нахождения походящего фабричного объекта, основываясь на переданном вами идентификаторе. Фабрика немедленно используется для создания объекта формы, но вы должны представлять себе более сложный процесс, при котором возвращается соответствующий производящий объект, а затем он используется вызывателем для создания объекта более искусным способом. Однако, это выглядит так, что большую часть времени вам не нужно путаться в полиморфных методах фабрик, а можно использовать единый статический метод базового класса (как показано в ShapeFactory1.java).

Обратите внимание, что ShapeFactory должен быть проинициализирован при загрузке в Map объектом-фабрикой, что имеет место в статическом блоке каждой реализации Формы (Shape). Так что при добавлении нового типа при таком дизайне вы должны наследовать тип, создать фабрику и добавить блок статической инициализации при загрузке в Map. Эта дополнительная сложность снова возвращает нас к статическому методу фабрики, если вам не нужно создавать индивидуальных объектов фабрик.