У построения объектов иерархии Pet есть один недостаток: каждый раз, когда в иерархию включается новый тип Pet, вы должны добавить его в LiteralPetCreator.java. В системах с регулярным добавлением новых классов это может создать проблемы. Первое, что приходит в голову, — добавить в каждый класс статический инициализатор, который добавлял бы свой класс в некий список. К сожалению, статические инициализаторы вызываются только при первой загрузке класса, поэтому возникает «порочный круг»: класс отсутствует в списке генератора, поэтому генератор не может создать объект этого класса, соответственно, класс не загрузится и не будет помещен в список. По сути, вы вынуждены создать список вручную (разве что вы напишете утилиту, которая будет анализировать исходный код, а затем создавать и компилировать список). Вероятно, лучшее, что можно сделать, — это разместить список в одном централизованном, очевидном месте. Вероятно, лучшим местом для него будет базовый класс иерархии. В этом разделе мы также внесем другое изменение: создание объекта будет передано самому классу с использованием паттерна «метод-фабрика». Метод-фабрика может вызываться полиморфно и создает объект соответствующего типа. В следующей упрощенной версии методом-фабрикой является метод create() интерфейсаFactory:
//: typeinfo/factory/Factory.java
package typeinfo.factory:
public interface Factory<T> { T create(); }
Обобщенный параметр T позволяет create() возвращать разные типы для разных реализаций Factory. Также при этом используется ковариантность возвращаемых типов. В следующем примере базовый класс Part содержит список объектов-фабрик. Фабрики типов, которые должны создаваться методом createRandom(), «регистрируются» в базовом классе включением в список partFactories:
package typeinfo;
import typeinfo.factory.*;
import java.util.*;
class Part {
@Override
public String toString() {return getClass().getSimpleName(); }
static List<Factory<? extends Part>>partFactories
= new ArrayList<Factory<? extends Part>>();
static {
// При вызове Collections addAll() выдается предупреждение
// "unchecked generic array creation for varargs parameter"
</spoiler> He все классы иерархии рассчитаны на создание экземпляров; в нашем примере классы Filter и Belt существуют исключительно в целях классификации. Экземпляры этих классов не создаются — только одного из их субклассов. Если класс должен создаваться посредством createRandom(), он содержит внутренний класс Factory. Хотя для включения всех фабрик в список можно воспользоваться вызовом Collections.addAll(), компилятор выдает предупреждение, поэтому я вернулся к вызовам add(). Метод createRandom()случайным образом выбирает объект фабрики из partFactories и вызывает его метод create() для получения нового объекта Part.