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