Динамический подход шаблона factory

Я пытаюсь понять шаблон factory. Если существует много реализаций, тогда у моего шаблона factory будет много случаев if else или switch. А также каждый раз, когда я представляю новую реализацию, я должен изменить свой код factory

Как и в приведенных ниже примерах, если позволить предположить, что утка собаки реализует интерфейс для домашних животных, например, завтра, если многие животные реализуют интерфейс для домашних животных, мой factory долгое время держится с большим количеством if else else, если код или случай переключения. Есть ли способ решить эту проблему с помощью более динамичного подхода?

package com.javapapers.sample.designpattern.factorymethod;

//Factory method pattern implementation that instantiates objects based on logic
public class PetFactory {

    public Pet getPet(String petType) {
        Pet pet = null;

        // based on logic factory instantiates an object
        if ("bark".equals(petType))
            pet = new Dog();
        else if ("quack".equals(petType))
            pet = new Duck();
        return pet;
    }

Если животные растут

if ("bark".equals(petType))
    pet = new Dog();
else if ("quack".equals(petType))
    pet = new Duck();
else if ("mno".equals(petType))
    pet = new MNO();
else if ("jkl".equals(petType))
    pet = new JKL();
else if ("ghi".equals(petType))
    pet = new GHI();
else if ("def".equals(petType))
    pet = new DEF();
......
else if ("abc".equals(petType))
    pet = new ABC();
return pet

Ответ 1

Я думаю, что существует динамический подход:

  • В factory вам понадобится Map<String, Class<? extends Pet>>
  • В статическом конструкторе каждого класса, который расширяет Pet, зарегистрируйте его с такой картой.
  • Чем создание класса будет просто map.get(pet).newInstance (вам, конечно, нужно будет проверить нули)

Ответ 2

Идея шаблона factory - позволить вам динамически создавать объекты, типы которых вы не обязательно знаете во время разработки.

Наличие большого блока if поражает эту цель.

Эффективный способ реализации этого шаблона также должен иметь factory для каждого типа, который реализует базовый интерфейс factory и имеет возможность создавать экземпляр нового объекта такого типа (кстати, в Java, встроенный Class является примером такого factory).

Затем вы регистрируете карту имен/идентификаторов/и т.д. к экземплярам этих отдельных заводов во время выполнения. Когда нужно создать экземпляр одного из типов, вы посмотрите на factory на карте по имени и используйте это для создания нового объекта этого типа.

Как вы регистрируете отдельные фабрики на карте, полностью в воздухе. Вы можете зарегистрировать некоторые явно, вы можете сканировать файл конфигурации и т.д.

По существу, вы хотите заменить блок if на карту, динамически создаваемую во время выполнения.

Вам даже не нужно использовать только предварительно зарегистрированную "карту" - иногда может быть целесообразно выяснить, как создать объект с заданным именем "на лету" или комбинацию из двух (например, Class.forName() ищет путь к классу, если он не может найти уже загруженный класс). Дело в том, что перевод имени в тип класса может происходить без базового factory, фактически зная, что такое тип класса.

Стоит отметить, что отражение Java обеспечивает очень работоспособную реализацию factory уже через Class.forName() и/или Class.newInstance(), поэтому подумайте об использовании этого вместо того, чтобы изобретать колесо, если это имеет смысл.

Ответ 3

использовать отражение

public Pet getPet(String petType)
{
     Pet _pet = (Pet)Class.forName(petType).newInstance();
     return _pet;
}

вам нужно изменить свои аргументы от "коры", "quack" до "Dog" и "Duck" и т.д.

Ответ 4

Я немного набросился на это, так как у меня была аналогичная проблема, и, наконец, я пришел с решением, основанным на Reflections Library (обратите внимание на заключительный S в Reflections!)

Это может быть применено к вашей проблеме, ЕСЛИ все ваши подклассы домашних животных имеют атрибут, который можно использовать для их различения, например

   public String petType;

Метод вашего factory может быть следующим:

        public static Pet getPet(String _petType) {
    String packageName = "your.package.with.pet.classes";

    Reflections reflections = new Reflections(packageName);

    Set<Class<? extends Pet>> allPets = reflections
            .getSubTypesOf(Pet.class);

    Iterator<Class<? extends Pet>> it = allPets.iterator();

    while (it.hasNext()) {
        try {
            Pet pet = it.next().newInstance();
            if (pet.petType.equals(_petType))
                return pet;
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    System.out.println("Pet " + _petType
            + " not yet implemented in package " + packageName);
    return null;
}

Этот метод остался бы незатронутым, если бы были определены новые Домашние животные.

Плюсы:

  • Не требуется дополнительной модификации подклассов Pet, ни какой-либо инициализации/регистрации в структуре карты, поддерживаемой Factory

  • Это более общее, чем решение на основе Java Reflection API, поскольку вы можете различать подклассы Pet по некоторым атрибутам вместо имени класса

Минусы:

  • Он создает локальные экземпляры всех подклассов Pet, чтобы найти соответствующий. С этой стороны я уверен, что этот подход можно улучшить.

Ответ 5

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

Примерно так для Фабрики:

public class DynamicSupplierTierFactory {

    public static final Map<String, Supplier<? extends Tier>> registeredSuppliers = new HashMap<>();

    static {
        registeredSuppliers.put("blue", new BlueSupplier());
        registeredSuppliers.put("silver", new SilverSupplier());
        registeredSuppliers.put("golden", new GoldenSupplier());
    }

    public static void registerSupplier(String type, Supplier<? extends Tier> supplier){
        registeredSuppliers.put(type, supplier);
    }

    public static Tier getTier(String type){
        Supplier<? extends Tier> supplier = registeredSuppliers.get(type);
        return supplier != null ? supplier.get():null;
    }
}

Поставщики будут как:

public class BlueSupplier implements Supplier<Tier> {
    @Override
    public Tier get() {
        return new Blue();
    }
}

Это можно увидеть здесь более подробно: https://medium.com/@mhd.durrah/factory-pattern-the-dynamic-way-with-java-8-3ca5ab48a9cf