Читайте также:
|
|
Q табличная альтернатива. Том Каргилл (Tom Cargill) в книге C++Programming Style [Car92] описывает другой способ структурирования кода, управляемого сменой состояний. Он использует таблицу для отображения входных данных на переходы между состояниями. С ее помощью можно определить, в какое состояние нужно перейти при поступлении некоторых входных данных. По существу, тем самым мы заменяем условный код (или виртуальные функции, если речь идет о паттерне состояние) поиском в таблице. Основное преимущество таблиц - в их регулярности: для изменения критериев перехода достаточно модифицировать только данные, а не код. Но есть и недостатки:
- поиск в таблице часто менее эффективен, чем вызов функции (виртуальной);
- представление логики переходов в однородном табличном формате делает
критерии менее явными и, стало быть, более сложными для понимания;
- обычно трудно добавить действия, которыми сопровождаются переходы
между состояниями. Табличный метод учитывает состояния и переходы
между ними, но его необходимо дополнить, чтобы при каждом измене
нии состоянии можно было выполнять произвольные вычисления.
Главное различие между конечными автоматами на базе таблиц и паттерном состояние можно сформулировать так: паттерн состояние моделирует поведение, зависящее от состояния, а табличный метод акцентирует внимание на определении переходов между состояниями;
Q создание и уничтожение объектов состояния. В процессе разработки обычно приходится выбирать между:
- созданием объектов состояния, когда в них возникает необходимость,
и уничтожением сразу после использования;
- созданием их заранее и навсегда.
Первый вариант предпочтителен, когда заранее неизвестно, в какие состояния будет попадать система, и контекст изменяет состояние сравнительно редко. При этом мы не создаем объектов, которые никогда не будут использованы, что существенно, если в объектах состояния хранится много информации. Когда изменения состояния происходят часто, поэтому не хотелось бы уничтожать представляющие их объекты (ибо они могут очень скоро понадобиться вновь), следует воспользоваться вторым подходом. Время на создание объектов затрачивается только один раз, в самом начале, а на уничтожение - не затрачивается вовсе. Правда, этот подход может оказаться неудобным, так как в контексте должны храниться ссылки на все состояния, в которые система теоретически может попасть;
Q использование динамического наследования. Варьировать поведение по запросу можно, меняя класс объекта во время выполнения, но в большинстве объектно-ориентированных языков это не поддерживается. Исключение составляет Self [US87] и другие основанные на делегировании языки, которые предоставляют такой механизм и, следовательно, поддерживают паттерн состояние напрямую. Объекты в языке Self могут делегировать операции
Паттерны поведения
другим объектам, обеспечивая тем самым некую форму динамического наследования. С изменением целевого объекта делегирования во время выполнения, по существу, изменяется и структура графа наследования. Такой механизм позволяет объектам варьировать поведение путем изменения своего класса.
Пример кода
В следующем примере приведен код на языке C++ с TCP-соединением из раздела «Мотивация». Это упрощенный вариант протокола TCP, в нем, конечно же, представлен не весь протокол и даже не все состояния TCP-соединений.1
Прежде всего определим класс TCPConnection, который предоставляет интерфейс для передачи данных и обрабатывает запросы на изменение состояния:
class TCPOctetStream; class TCPState;
class TCPConnection { public:
TCPConnection ();
void ActiveOpen (); void PassiveOpenO; void Close ();
void Send();
void Acknowledge ();
void Synchronize ();
void ProcessOctet (TCPOctetStream*); private:
friend class TCPState;
void ChangeState (TCPState*); private:
TCPState* _state;
};
В переменной-члене _state класса TCPConnection хранится экземпляр класса TCPState. Этот класс дублирует интерфейс изменения состояния, определенный в классе TCPConnect ion. Каждая операция TCPState принимает экземпляр TCPConnection как параметр, тем самым позволяя объекту TCPState получить доступ к данным объекта TCPConnection и изменить состояние соединения:
class TCPState { public:
virtual void Transmit(TCPConnection*, TCPOctetStream*);
virtual void ActiveOpen(TCPConnection*);
virtual void PassiveOpen(TCPConnection*);
virtual void Close(TCPConnection*);
1 Пример основан на описании протокола установления TCP-соединений, приведенном в книге Линча и Роуза [LR93]. %
Паттерн State
virtual void Synchronize (TCPConnection*);
virtual void Acknowledge (TCPConnection*); virtual void Send (TCPConnect ion*); protected:
void ChangeState (TCPConnection*, TCPState*);
TCPConnection делегирует все зависящие от состояния запросы хранимому в _state экземпляру TCPState. Кроме того, в классе TCPConnection существует операция, с помощью которой в эту переменную можно записать указатель на другой объект TCPState. Конструктор класса TCPConnection инициализирует _state указателем на состояние TCPClosed (мы определим его ниже):
TCPConnection:: TCPConnection () {
_state = TCPClosed:: Instance ();
void TCPConnection::ChangeState (TCPState* s) { _state = s;
void TCPConnection::ActiveOpen () { _state->ActiveOpen(this);
void TCPConnection::PassiveOpen () { _state->PassiveOpen(this);
oid TCPConnection::Close () { _state->Close(this);
void TCPConnection:Acknowledge () { _state->Acknowledge (this);
void TCPConnection::Synchronize () {
_state->Synchronize(this); }
В классе TCPState реализовано поведение по умолчанию для всех делегированных ему запросов. Он может также изменить состояние объекта TCPConnection посредством операции ChangeState. TCPState объявляется другом класса TCPConnection, что дает ему привилегированный доступ к этой операции:
void TCPState::Transmit (TCPConnection*, TCPOctetStream*) { } void TCPState::ActiveOpen (TCPConnection*) { } void TCPState::PassiveOpen (TCPConnection*) { }
Паттерны поведения
void TCPState:: Close (TCPConnection*) { }
void TCPState:: Synchronize (TCPConnection*) { }
void TCPState::ChangeState (TCPConnection* t, TCPState* s) { t->ChangeState(s);
}
В подклассах TCPState реализовано поведение, зависящее от состояния. Соединение TCP может находиться во многих состояниях: Established (установлено), Listening (прослушивание), Closed (закрыто) и т.д., и для каждого из них есть свой подкласс TCPState. Мы подробно рассмотрим три подкласса -TCPEstablished, TCPListen и TCPClosed:
class TCPEstablished: public TCPState { public:
static TCPState* Instanced;
virtual void Transmit (TCPConnection*, TCPOctetStream*); virtual void Close (TCPConnection*);
) /
class TCPListen: public TCPState { public:
static TCPState* Instance();
virtual void Send(TCPConnection*);
class TCPClosed: public TCPState { public:
static TCPState* Instanced;
virtual void ActiveOpen(TCPConnection*); virtual void PassiveOpen(TCPConnection*);
В подклассах TCPState нет никакого локального состояния, поэтому их можно разделять, так что потребуется только по одному экземпляру каждого класса. Уникальный экземпляр подкласса TCPState создается обращением к статической операции Instance.1
В подклассах TCPState реализовано зависящее от состояния поведение для тех запросов, которые допустимы в этом состоянии:
void TCPClosed::ActiveOpen (TCPConnection* t) { // послать SYN, получить SYN, ACK и т.д.
ChangeState(t, TCPEstablished::Instanced);
Дата добавления: 2015-09-11; просмотров: 76 | Поможем написать вашу работу | Нарушение авторских прав |