Статьи

Шаблон дизайна мастера

Мы все любим волшебников … (Я имею в виду мастера программного обеспечения). Мы всегда рады прыгать на этих кнопках «Далее», как будто мы танцевали прикольную курицу на нашей… ну, вы поняли. Итак, сегодня мы представляем вам вашего любимого волшебника в вашем опыте кодирования. Давайте сразу перейдем к примеру.

Скажем, вы хотите создать класс ConservativePerson .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.List;
class ConservativePerson{
   
    private boolean isVirgin;
    private boolean isMarried;
    private List<string> children;
   
    ConservativePerson(boolean virgin, boolean married, List<string> children) {
        this.isVirgin = virgin;
        this.isMarried = married;
        this.children = children;
    }
   
    public boolean isVirgin() {
        return isVirgin;
    }
    public boolean isMarried() {
        return isMarried;
    }
   
    public List<string> getChildren() {
        return children;
    }
}

Как таковой он имеет некоторые ограничения.

  • Он должен быть женат, прежде чем он сможет … ну, не девственница.
  • Он не может быть девственником, прежде чем он сможет иметь детей (насколько мы знаем).

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

Сегодня мы сделаем это с помощью Wizard Design Pattern . Мы используем свободный стиль интерфейса и используем возможности современной IDE для создания волшебного ощущения при создании объекта ConservativePerson . Мы знаем, мы знаем, перестанем говорить и покажем нам код … но прежде чем мы представим код мастера, мы покажем вам его использование, чтобы вы поняли, о чем мы говорим …

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public class Main {
public static void main(String[] args) {
    ConservativePersonWizardBuilder wizard = new ConservativePersonWizardBuilder();
    ConservativePerson singlePerson = wizard.
            createConservativePerson().
            whichIsSingle().
            getObject();
    ConservativePerson familyManPerson = wizard.
            createConservativePerson().
            whichIsMarried().
            andNotVirgin().
            andHasChildNamed("Noa").
            anotherChildNamed("Guy").
            lastChildName("Alon").
            getObject();
  }
   
}

Теперь он может выглядеть просто как обычно гибкий интерфейс, но классная вещь здесь в том, что метод доступен для вызова, только если текущее состояние объекта это позволяет. Это означает, что вы не сможете вызывать метод andNotVirgin, если вы не вызывали метод whichIsMarried .
Смотрите следующий набор снимков экрана:

и после того, как мы заявим, что он женат, мы можем:

Вот код мастера. Я призываю вас скопировать / вставить его в вашу среду IDE и попробовать, создав объект с его помощью.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import java.util.ArrayList;
  import java.util.List;
    
   public class ConservativePersonWizardBuilder {
     private boolean isVirgin;
     private boolean isMarried;
     private List<String> children = new ArrayList<String>();
        
     public SetMarriedStep createConservativePerson(){
         return new SetMarriedStep();
     }
    
     class SetMarriedStep {
        public SetVirginStep whichIsMarried(){
             isMarried = true;
             return new SetVirginStep();
         }
     
         public FinalStep whichIsSingle(){
             isMarried = false;
             return new FinalStep();
         }
     }
   
     class SetVirginStep {
         public AddChildrenStep andNotVirgin(){
             isVirgin = false;
             return new AddChildrenStep();
         }
         public FinalStep butStillAVirgin(){
             isVirgin = true;
             return new FinalStep();
         }
     }
   
     class FinalStep {
         public ConservativePerson getObject(){
             return new ConservativePerson(isVirgin, isMarried, children);
         }
     }
   
     class AddChildrenStep {
         public AddChildrenStep andHasChildNamed(String childName) {
             children.add(childName);
             return new AddChildrenStep();
         }
         public AddChildrenStep anotherChildNamed(String childName) {
             children.add(childName);
             return new AddChildrenStep();
         }
         public FinalStep lastChildName(String childName){
             children.add(childName);
             return new FinalStep();
         }
     }
 }

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

Этот шаблон фактически используется в нашем производственном коде. Один пример, который приходит на ум, это класс MediaJob . Этот класс описывает манипуляции с некоторыми медиа-файлами. Чтобы отправить работу в систему, нужно создать объект MediaJob . Проблема в том, что у этого объекта есть много параметров, которым можно присвоить противоречивые значения, которые создают недопустимое состояние объекта. Используя шаблон Wizard, можно легко построить легальную работу без необходимости знать весь (и сложный…) набор ограничений.

Это все. Надеюсь, вы попробуете… Мы планируем написать более формальное описание (стиль GOF) в ближайшем будущем.

Ссылки: шаблон дизайна мастера от нашего партнера JCG Надава Азарии и Роя Гамлиеля в DeveloperLife