WedX - журнал о программировании и компьютерных науках

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

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

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

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
14.08.2013


Ответы:


1

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

  1. На вашем заводе вам нужен Map<String, Class<? extends Pet>>
  2. В статическом конструкторе каждого класса, расширяющего Pet, зарегистрируйте его с такой картой.
  3. Тогда создание класса будет просто map.get(pet).newInstance (конечно, вам нужно будет проверить наличие нулей)
14.08.2013
  • Одно предостережение, однако, заключается в том, что класс должен быть загружен для выполнения его статического конструктора. Это не происходит автоматически, если на класс никогда не ссылаются. Если вы когда-либо сталкивались с API, где вам нужно было сначала сделать произвольное Class.forName("") что-то, это демонстрация этого. Это не запретительно, это просто нужно отметить; что-то где-то придется подгружать классы. 14.08.2013
  • при таком подходе, когда вы добавляете новый класс реализации, вы должны добавить этот класс в свой Map. Вот так? Таким образом, вы все равно должны изменить свой заводской код. 01.05.2018

  • 2

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

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

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

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

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

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

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

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

    14.08.2013
  • Просто для добавления имени этот шаблон называется Abstract Factory Pattern. 14.08.2013

  • 3

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

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

    вам нужно изменить свои аргументы с «лая», «шарлатана» на «собака» и «утка» и т. д.

    14.08.2013
  • Это определенно то, что мне нужно. Большое спасибо. 01.05.2018

  • 4

    Я немного ломал голову над этим, так как у меня была похожая проблема, и, наконец, я нашел решение, основанное на Библиотека отражений (обратите внимание на последнюю букву S в отражениях!)

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

       public String petType;
    

    Метод вашей фабрики может быть следующим:

            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, а также какой-либо инициализации/регистрации в структуре Map, поддерживаемой Factory.

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

    Минусы:

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

    5

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

    Что-то вроде этого для Фабрики:

    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

    02.05.2019
    Новые материалы

    Как создать диаграмму градиентной кисти с помощью D3.js
    Резюме: Из этого туториала Вы узнаете, как добавить градиентную кисть к диаграмме с областями в D3.js. Мы добавим градиент к значениям SVG и применим градиент в качестве заливки к диаграмме с..

    Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
    Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

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

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..


    Для любых предложений по сайту: [email protected]