Статьи

Шаблоны проектирования в XXI веке: абстрактная фабричная модель

Это вторая часть моего выступления « Шаблоны проектирования в 21 веке» .

Этот шаблон используется повсеместно в коде Java, особенно в более «корпоративных» кодах. Он включает в себя интерфейс и реализацию. Интерфейс выглядит примерно так:

1
2
3
4
public interface Bakery {
    Pastry bakePastry(Topping topping);
    Cake bakeCake();
}

И реализация:

1
2
3
4
5
6
7
8
9
public class DanishBakery implements Bakery {
    @Override public Pastry bakePastry(Topping topping) {
        return new DanishPastry(topping);
    }
 
    @Override public Cake bakeCake() {
        return new Aeblekage(); // mmmm, apple cake...
    }
}

В более общем смысле шаблон абстрактной фабрики обычно реализуется в соответствии с этой структурой.

абстрактно-завод-модель-ОМЛ

В этом примере Pastry и Cake являются «абстрактными продуктами», а Bakery — «абстрактными фабриками». Их реализации являются конкретными вариантами.

Это довольно общий пример.

На самом деле, большинство фабрик имеет только один метод «создания».

1
2
3
4
@FunctionalInterface
public interface Bakery {
    Pastry bakePastry(Topping topping);
}

О, смотри, это функция.

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

Итак, как бы мы внедрили этого кондитера?

1
2
3
4
5
public class DanishBakery implements Bakery {
    @Override public Pastry apply(Topping topping) {
        return new DanishPastry(Topping topping);
    }
}

ОК, конечно, это было легко. Он выглядит так же, как и в предыдущей DanishBakery за исключением того, что он не может сделать торт. Вкусный яблочный пирог … какой в ​​этом смысл?

Ну, если вы помните, в Bakery есть один абстрактный метод . Это означает, что это функциональный интерфейс .

Так что же это за функциональный эквивалент?

1
Bakery danishBakery = topping -> new DanishPastry(topping);

Или даже:

1
Bakery danishBakery = DanishPastry::new;

Вуаля. Наш класс DanishBakery исчез.

Но мы можем пойти дальше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package java.util.function;
/**
 * Represents a function that
 * accepts one argument and produces a result.
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     */
    R apply(T t);
 
    ...
}

Мы можем заменить Bakery с помощью Function<Topping, Pastry> ; у них одинаковые типы.

1
Function<Topping, Pastry> danishBakery = DanishPastry::new;

В этом случае мы можем захотеть сохранить его, так как он имеет имя, относящееся к нашему бизнесу, но часто Factory объекты не преследуют никакой цели, кроме как помогают нам отделить наш код. ( UserServiceFactory , кто-нибудь?) Это замечательно, но в таких случаях нам не нужны явные классы для него — в Java 8 встроено множество интерфейсов, таких как Function , Supplier и многие другие в java.util.function пакет, который удовлетворяет нашим потребностям довольно хорошо.

Вот наша обновленная UML-диаграмма:

абстрактно-завод-модель-ОМЛ-функциональный

Aaaaaah. Намного лучше.