Статьи

Государственный шаблон проектирования в Java

В этом уроке мы рассмотрим еще один популярный шаблон поведенческого дизайна — State Design Pattern.

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

С этим, давайте начнем!

Государственный шаблон проектирования:

Мы можем представить шаблон проектирования состояния с помощью следующей диаграммы UML:

Куда,

  • Состояние — представляет абстрактный класс или интерфейс для объявления минимальной ожидаемой функциональности для каждого конкретного объекта состояния.
  • ConcreteState — это классы реализации для государства
  • Контекст — это класс, который мы выставляем миру, и обычно он отвечает за поддержание текущего состояния экземпляра. Он также делегирует ответственность конкретным государственным классам для выполнения задачи.

Пример реализации:

Допустим, у нас есть автоматический дозатор мыла. В идеале он может существовать в этих состояниях — Ideal , Dispensing или OutOfStock . Давайте визуализируем это с помощью диаграммы состояний:

Определение состояний классов:

Сначала мы определим наш интерфейс DispenserState :

1
2
3
4
5
public DispenserState {
  
    void sensesHand(AutoDispenser autoDispenser);
    void stopsSensingHand(AutoDispenser autoDispenser);
}

Каждый из наших классов состояний должен реализовывать DispenserState и определять поведение для этого состояния. Наш класс IdleState будет выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
public class IdleState implements DispenserState {
  
    public void sensesHand(AutoDispenser autoDispenser) {
        System.out.println("Hand sensed");
        autoDispenser.startDispensing();
        autoDispenser.setDispenserState(autoDispenser.getDispensingState());
    }
  
    public void stopsSensingHand(AutoDispenser autoDispenser) {
        System.out.println("Sensor inactive already!");
    }
}

Точно так же мы можем определить два других наших класса, представляющих состояние:

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
public class DispensingState implements DispenserState {
  
    public void sensesHand(AutoDispenser autoDispenser) {
        System.out.println("Already dispensing");
    }
  
    public void stopsSensingHand(AutoDispenser autoDispenser) {
        System.out.println("Stopped sensing");
        autoDispenser.stopDispensing();
        if(autoDispenser.getQuantity() > 1) {
          autoDispenser.setDispenserState(autoDispenser.getIdleState());
        } else {
          autoDispenser.setDispenserState(autoDispenser.getOutOfStockState());
        }
    }
}
  
public class OutOfStockState implements DispenserState {
  
    public void sensesHand(AutoDispenser autoDispenser) {
        System.out.println("nothing to dispense");
    }
  
    public void stopsSensingHand(AutoDispenser soapDispenser) {
        System.out.println("dispenser is already inactive");
    }
}

Определение контекста:

Наконец, давайте определим наш контекстный класс — AutoDispenser , который будет взаимодействовать с клиентским кодом:

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
public class AutoDispenser {
  
    private IdleState idleState;
    private DispensingState dispensingState;
    private OutOfStockState outOfStockState;
  
    private DispenserState currentDispenserState;
  
    public AutoDispenser() {
        this.idleState = new IdleState();
        this.dispensingState = new DispensingState();
        this.outOfStockState = new OutOfStockState();
        if(getQuantity() > 0) {
            this.currentDispenserState = idleState;
        } else {
             this.currentDispenserState = outOfStockState;
        }
    }
  
    public int getQuantity() {
        //returns current soap quantity
        ...
    }
  
    public void startDispensing() { ... }
  
    public void stopDispensing() { ... }
  
    public void sensesHand() {
        this.currentDispenserState.sensesHand(this);
    }
  
    public void stopsSensingHand() {
        this.currentDispenserState.stopsSensingHand(this);
    }
  
}

Здесь мы поддерживаем текущее состояние нашего автоматического диспенсера и вызываем метод в конкретном классе состояний для выполнения операции.

Почему Государственный Образец?

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

  • Добавить новое состояние очень легко и просто, так как нам просто нужно определить другой конкретный класс состояний
  • Точно так же удаление состояния потребует только удаления связанного класса
  • Делает код легче читать и следовать
  • Отличная демонстрация принципа единой ответственности

Вывод:

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

В этом быстром уроке мы рассмотрели шаблон State State. Мы узнали, как и когда это реализовать.

Опубликовано на Java Code Geeks с разрешения Шубхры Шриваставы, партнера нашей программы JCG . Смотрите оригинальную статью здесь: State Design Pattern In Java

Мнения, высказанные участниками Java Code Geeks, являются их собственными.