Что лучший способ, использование является шаблоном разработки “состояния”, для изменения состояний?

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

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

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

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

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

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

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

8
задан Vukašin Manojlović 30 December 2018 в 22:21
поделиться

2 ответа

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

Рассмотрим этот пример:

// 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 обладает какими-либо знаниями состояния переходов. Это означает уменьшение количества обязанностей, а также обеспечить более гибкий дизайн.

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

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

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 , и он выбрал бы следующий цвет. Или ваши государственные объекты могли выбрать следующее состояние, в этом случае вы должны иметь RedrawoClourState , который знал следующее государство OrangerainbobleColourstate и так далее. Но проблема с обоими этими примерами состоит в том, что вы должны войти и изменять (по расширению) как контекстом, так и объекты состояния для достижения другого набора переходов. Однако, если не знают переходов, и это ответственность за вызовов класса, это может быть сделано без изменения состояния или объекта контекста. Т.е.

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);
    }  

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


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

2
ответ дан 5 December 2019 в 23:15
поделиться

Я предпочитаю, чтобы метод состояния возвращал новый объект состояния (он уменьшает связь и более соответствует принципам 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) {
        //..
    }
}
3
ответ дан 5 December 2019 в 23:15
поделиться