Как лучше всего использовать шаблон проектирования State для изменения состояний? - PullRequest
8 голосов
/ 21 января 2010

Мое текущее понимание шаблона проектирования State в основном таково:

Инкапсулирует все поведение объекта в определенном состоянии в объекте. Делегирование запросов к объекту «Текущее».

Мой вопрос: каков наилучший способ обработки состояний? В моем случае вполне вероятно, что «Текущее» состояние объекта будет тем, кто решит, в какое другое состояние нам нужно перейти. Я подумал о 2 способах реализации этого:

  1. Методы объектов состояния могут возвращать какое-то конкретное значение, которое означает «Я запрашиваю переход состояния». Затем главный объект может запросить текущее состояние, в какое новое состояние мы должны перейти, вызвать ChangeState(), а затем направить исходный запрос в новое состояние.

  2. Сам объект состояния может вызвать ChangeState() для родителя, а затем сам передать запрос, вызвавший изменение состояния, новому объекту.

Сценарий 2 обладает тем преимуществом, что главному объекту нужно только когда-либо делегировать запросы в «текущее» состояние (он внутренне будет обрабатывать любые необходимые переходы состояний). Это также возможно немного менее очевидно.

Я надеюсь, что известны более эффективные способы обработки этого сценария. Что ты думаешь?

Ответы [ 2 ]

3 голосов
/ 21 января 2010

Я предпочитаю, чтобы метод состояния возвращал новый объект состояния (он уменьшает связь и более соответствует принципам S.O.L.I.D.).

Вот пример (эта идея используется в реальном проекте):

class ExternalContext {
    //...
}

class Entity
{
    public Entity(ExternalContext context)
    {
        //Creating current state with factory method
        state = EntityState.Create(context);
    }

    public void ChangeEntity(ExternalContext context)
    {
        state = state.Change(context);
    }

    private EntityState state;
}

abstract class EntityState
{
    public abstract EntityState Change(ExternalContext externalContext);
    public static EntityState Create(ExternalContext externalContext);
}
class EntityState1 : EntityState {
    public override EntityState Change(ExternalContext externalContext) {
        //..
    }
}
2 голосов
/ 21 января 2010

Я думаю, что вы, возможно, ограничиваетесь мышлением только с точки зрения объектов, реализующих шаблон состояния (объект Context и объекты State). Дело обстоит не так, и есть другие вовлеченные объекты (Клиенты). Возможно, что клиент, который содержит ссылку на объект контекста, должен нести ответственность за переходное состояние.

Рассмотрим этот выдуманный пример:

// Paintbrush is the context object
class Paintbrush {
    // The State object, ColourState would be the abstraction
    private ColourState colourState; 

    // ... other class stuff

    public paint() {
        // Delegation to the state object
        this.colourState.paintInYourSpecificColour(); 
    }

    public void setColourState(ColourState newState) {
        this.colourState = newState;
    }
}

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

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

Я пытаюсь убедиться, что не использую аргумент Strawman, но я продолжу работать с примером. Скажем, в разных точках вы хотели рисовать разные узоры, а используемые цвета должны быть в определенном порядке, ваш клиент сам решит, когда менять состояние, например:

public void paintRainbow() {
    paintbrush.setColourState(new RedColourState());
    // do painting...
    // Change state to next colour
    paintbrush.setColourState(new OrangeColourState());
    // Chane state again, and so on...
}

Вы могли бы иметь порядок цветов, заданный состоянием или объектом контекста, т.е. иметь подкласс Paintbrush с именем RainbowPaintbrush, и он выберет следующий цвет. Или ваши объекты состояния могут выбрать следующее состояние, в этом случае вам нужно будет иметь RedRainbowColourState, который знал бы, что следующим состоянием было OrangeRainbowColourState и так далее. Но проблема обоих этих примеров заключается в том, что вы должны войти и изменить (путем расширения) и контекст, и объекты состояния, чтобы получить другой набор переходов. Однако, если никто не знает о переходах, и это является обязанностью вызывающего класса, это можно сделать без изменения объекта состояния или контекста. Т.е..

public void paintChessboard() {
    paintbrush.setColourState(blackColourState);
    // do painting...
    // change state
    paintbrush.setColourState(whiteColourState);
    // etc...
}

Это упрощенный пример, но, как правило, имеет место.

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

public class RedRainbowColourState implements ColourState {
    public void doPaint(Paintbrush paintbrush) {
        // do painting
        ColourState nextStateInRainbow = new OrangePaintbrushColourState();
        paintbrush.setColourState(nextStateInRainbow);
    }  

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


Подводя итог, вы можете позволить отдельным состояниям выполнять переход или даже контекстный объект. Другой вариант - позволить клиенту обрабатывать переходы состояний.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...