Factory method - Фабричный метод

109
27 февраля 2022, 08:40

Зачем нужен фабричный метод?

Одно из приемуществ Фабричного метода, как описано по ссылке выше (смотреть первый ответ, отмеченый галочкой) - это создание множества понятных конструкторов.

Но вот я всё-же не очень понял где эти конструкторы можно создавать

Допустим у меня есть такой код:

    public class Test {
        public static void main(String[] args) {
            Test test = new Test();
            DeveloperFactory developerFactory = test.choice("cpp");
            Developer developer = developerFactory.create();
            developer.write();
        }
        public DeveloperFactory choice(String s){
            switch(s){
                case "java":
                    return new JavaDeveloperFactory();
                case "cpp":
                    return new CppDeveloperFactory();
                default:
                    return null;
            }
        }
    }
public interface DeveloperFactory {
    Developer create();
}
public class JavaDeveloperFactory implements DeveloperFactory {
    @Override
    public Developer create() {
        return new JavaDeveloper();
    }
}
public interface Developer{
    void write();
}
public class JavaDeveloper implements Developer{
    @Override
    public void write() {
        System.out.println("Java developer writes java code");
    }
}

Как я понял, автор предлагает создавать статические методы в классах на уровне JavaDeveloperFactory?

Какие ещё есть приемущества у данного метода?

Answer 1

Создание множества понятных конструкторов - бред. Смысл совсем не в этом. Для начала давайте мы немного переделаем ваш пример, потому как в реальной жизни так, как описано у вас, уже никто не делает. Этот паттерн выродился (его упростили), хотя в какноническом виде он выглядит именно так, как вы описали.

public interface Developer {
    void write();
    static Developer of(String type){
        switch(type){
            case "JAVA" : return new JavaDeveloper();
            case "RUBY" : return new RubyDeveloper();
            default: throw new UnsupportedOperationException("Not supported for " + type);
        }
    }
}
public class JavaDeveloper implements Developer {
    @Override
    public void write() {
        System.out.println("Java developer writes java code");
    }
}
public class RubyDeveloper implements Developer {
    @Override
    public void write() {
        System.out.println("Ruby developer writes ruby code");
    }
}
import java.util.ArrayList;
import java.util.List;
public class Test {
    public static void main(String[] args) {        
        List<Developer> developers = new ArrayList<>();        
        developers.add(Developer.of("JAVA"));
        developers.add(Developer.of("RUBY"));        
        developers.forEach(developer->developer.write());        
    }    
}

Одна из основных проблем ООП - создание экземпляра класса. Рассмотрим это на данном примере. У нас есть интерфейс Developer и две его имплементации : JavaDeveloper, RubyDeveloper. Наша задача создать экземпляр конкретного класса и привести его к переменной типа интерфейса и это достаточно просто на первый взгляд :

Developer  developer  = new JavaDeveloper();

И тут возникает две проблемы : 1) в каком именно участке кода создавать этот экземпляр класса, 2) имплементации у нас в данный момент две, следовательно, выбор конкретной реализации интерфейса Developer зависит от каких-то условий, следовательно, надо где-то написать условный оператор. В описанном примере конкретный экземпляр класса создается в зависимости от переменной типа String, который передается в параметрах. Так может быть, если вы создаете конкретный экземпляр класса в зависимости от какого-то параметра, который передан в запросе на ваш API. Однако по какому условию создавать объект абсолютно не важно, это использовано просто для примера (паттерном это не специфицировано).

Теперь рассмотрим класс Test, назовем его клиентским кодом, потому как именно в этом классе мы хотим использовать объекты наших классов JavaDeveloper и RubyDeveloper. Зачастую указаное условие и создание экземпляра класса пишут именно в клиентском коде (в нашем случае это класс Test). И это самая фатальная ошибка. Теперь осталось понять почему. На самом деле все просто. Представьте себе, что объекты JavaDeveloper и RubyDeveloper необходимо использовать не в одном классе Test, а в 50 классах. Это значит,что у вас огромное количество дублирующего кода, ведь в каждом из них вы написали условные операторы. Мало того, теперь класс Test наряду с остальными 50 классами имеют ссылки на реальные классы JavaDeveloper и RubyDeveloper, ведь вы везде написали условие и Developer developer = new JavaDeveloper(). Это нарушает Low Coupling, а значит, что изменения либо удаление какой-либо из реализаций (JavaDeveloper либо RubyDeveloper) повлечет переписывание класса Test и 50 других классов. А теперь представьте, что изменились условия создания экземпляров этих классов. Вам снова надо переписать класс Test и 50 других классов. А теперь ваше приложение расширилось и вам необходимо добавить JavaScript разработчиков. Что, опять менять Test и 50 других классов?

В случае с примером, который я привел, класс Test ничего не знает о классах JavaDeveloper и RubyDeveloper, у него есть ссылка лишь на интерфейс Developer, на который и возложена обязанность по созданию экземпляров классов. Теперь весь клиентский код будет иметь ссылки только на интерфейс Developer, а значит, что для добавления нового разработчика, ровно как и изменения условия создания экземпляров классов, либо удаления какой-либо из реализаций необходимо менять лишь один метод интерфейса Developer. На клиентский код это никак не влияет.

READ ALSO
добавлять значения в firebase database сверху

добавлять значения в firebase database сверху

Весь цикл записи и чтения работает на ура, но новые значения появляются снизу БД:

204
Вращение фигуры во круг точки

Вращение фигуры во круг точки

Такая проблема: в поставленной задаче требуется сделать вращение фигуры вокруг своего центра

93
Как в Spring WebFlux использовать Gson вместо Jackson?

Как в Spring WebFlux использовать Gson вместо Jackson?

Не думал что придется тратить на это время,но уже изрядно потрепался в поисках ответаИспользование spring

93
Как задать количество в веденных цифр в ANTLR4

Как задать количество в веденных цифр в ANTLR4

Для ограничения ввода четырех цифр в лексическом анализаторе Lex используется следующая конструкция: [0-9]{4}Как ограничить ввод, так что бы ввести...

79