Стан (шаблон проєктування)Стан (англ. state) — шаблон проєктування (належить до шаблонів поведінки), що реалізує скінченний автомат в обʼєктно-орієнтованому програмуванні. Він реалізується шляхом створення для кожного стану скінченного автомата класу-спадкоємця інтерфейсу (або абстрактного класу) та дозволяє об'єктові варіювати свою поведінку залежно від внутрішнього стану. ПризначенняДозволяє об'єктові варіювати свою поведінку залежно від внутрішнього стану. ЗастосовністьСлід використовувати шаблон Стан у випадках, коли:
Структура
Відносини
Переваги та недолікиПереваги
Недоліки
Зв'язок з іншими патернами
РеалізаціяC++Приклад реалізації мовою С++
#include <iostream>
#include <string>
using namespace std;
class Creature
{
private:
struct State
{
virtual string response() = 0;
};
struct Frog : public State
{
string response() { return " Ribbet!"; }
};
struct Prince : public State
{
string response() { return " Darling!"; }
};
State* state;
public:
Creature() : state(new Frog()) {}
void greet()
{
cout << state->response() << endl;
}
void kiss()
{
delete state;
state = new Prince;
}
};
void main()
{
Creature creature;
creature.greet();
creature.kiss();
creature.greet();
}
C#Приклад реалізації мовою C#
namespace StatePattern
{
class Mario
{
abstract class HeroStateBase
{
protected readonly Mario _mario;
public HeroStateBase(Mario mario)
{
_mario = mario;
}
public abstract void HandleInput(string input);
public abstract string Show();
}
#region WalkingState
abstract class WalkingState : HeroStateBase
{
protected WalkingState(Mario mario)
: base(mario) { }
}
class StandingState : WalkingState
{
public StandingState(Mario mario)
: base(mario) { }
public override void HandleInput(string input)
{
if (input == "up")
{
_mario.ChangeState(new JumpingState(_mario));
}
if (input == "down")
{
_mario.ChangeState(new DuckingState(_mario));
}
}
public override string Show()
{
return "Standing";
}
}
class JumpingState : WalkingState
{
private int _time = 3;
public JumpingState(Mario mario)
: base(mario) { }
public override void HandleInput(string input)
{
_time--;
if (_time == 0)
{
_mario.ChangeState(new StandingState(_mario));
}
}
public override string Show()
{
return "Jumping";
}
}
class DuckingState : WalkingState
{
public DuckingState(Mario mario)
: base(mario) { }
public override void HandleInput(string input)
{
if (input != "down")
{
_mario.ChangeState(new StandingState(_mario));
}
}
public override string Show()
{
return "Ducking";
}
}
#endregion
#region AttackingState
abstract class AttackingState : HeroStateBase
{
protected AttackingState(Mario mario)
: base(mario) { }
}
class DisarmedState : AttackingState
{
public DisarmedState(Mario mario)
: base(mario) { }
public override void HandleInput(string input)
{
if (input == "mushroom")
{
_mario.ChangeState(new FireState(_mario));
}
}
public override string Show()
{
return "Disarmed";
}
}
class FireState : AttackingState
{
private int _time = 5;
public FireState(Mario mario)
: base(mario) { }
public override void HandleInput(string input)
{
_time--;
if (input == "attack")
{
System.Console.WriteLine("Throw fire ball");
}
if (_time == 0)
{
_mario.ChangeState(new DisarmedState(_mario));
}
}
public override string Show()
{
return "Fire";
}
}
#endregion
private WalkingState _walkingState;
private AttackingState _equipment;
public Mario()
{
_walkingState = new StandingState(this);
_equipment = new DisarmedState(this);
}
public void HandleInput(string input)
{
// для того щоб не "засмічувати" логіку
// багатьма умовними операторами та змінними
// винисемо її в окремі стани
_walkingState.HandleInput(input);
_equipment.HandleInput(input);
}
public string Show()
{
return _walkingState.Show() + " " + _equipment.Show();
}
private void ChangeState(WalkingState newState)
{
_walkingState = newState;
}
private void ChangeState(AttackingState newState)
{
_equipment = newState;
}
}
class Program
{
static void Main(string[] args)
{
var mario = new Mario();
while (true)
{
var input = System.Console.ReadLine();
mario.HandleInput(input);
var state = mario.Show();
System.Console.WriteLine(state);
}
}
}
}
JavaПриклад реалізації мовою Java
class Car {
private CarState carState;
public Car() {
off();
}
public void on() {
carState = new CarOn();
System.out.println("The car is on!");
}
public void off() {
carState = new CarOff();
System.out.println("The car is off!");
}
public void start() {
carState = new CarMoving();
System.out.println("The car is moving!");
}
public void openWindow() {
carState.openWindow();
}
public void openDoor() {
carState.openDoor();
}
}
abstract class CarState {
abstract public void openWindow();
abstract public void openDoor();
}
class CarOff extends CarState {
public void openWindow() {
System.out.println("Can't open the window! Switch the car on!");
}
public void openDoor() {
System.out.println("The door is being opened ...");
}
}
class CarOn extends CarState {
public void openWindow() {
System.out.println("The window is being opened ...");
}
public void openDoor() {
System.out.println("The door is being opened ...");
}
}
class CarMoving extends CarState {
public void openWindow() {
System.out.println("The window is being opened ...");
}
public void openDoor() {
System.out.println("Can't open the door while moving!");
}
}
class Main {
public static void main(String[] args) {
Car car = new Car();
car.openWindow();
car.openDoor();
car.on();
car.openWindow();
car.openDoor();
car.start();
car.openWindow();
car.openDoor();
}
}
PythonПриклад реалізації мовою Python
class BaseState(object):
def open_window(self):
raise NotImplementedError()
def open_door(self):
raise NotImplementedError()
class CarOff(BaseState):
def open_door(self):
print("Can't open the window. Switch the car on")
def open_window(self):
print("The door is being opened...")
class CarOn(BaseState):
def open_window(self):
print("The window is being opened...")
def open_door(self):
print("The door is being opened...")
class CarMoving(BaseState):
def open_window(self):
print("The window is being opened")
def open_door(self):
print("Can't open the door while moving...")
class Car(object):
_state = None
def __init__(self):
self._state = CarOff()
def off(self):
self._state = CarOff()
print("Car is off")
def on(self):
self._state = CarOn()
print("Car is on")
def start(self):
self._state = CarMoving()
print("Car is moving...")
def open_window(self):
self._state.open_window()
def open_door(self):
self._state.open_door()
if __name__ == '__main__':
car = Car()
car.open_window()
car.open_door()
car.on()
car.open_window()
car.open_door()
car.start()
car.open_door()
car.open_window()
TypeScriptПриклад реалізації мовою TypeScript
class StateMachine<TState, TCommand>
{
private currentState: TState;
private states: Map<TState, State<TState, TCommand>>;
constructor(currentState: TState)
{
this.currentState = currentState;
this.states = new Map<TState, State<TState, TCommand>>();
}
public addState(state: TState): State<TState, TCommand>
{
const newState = new State<TState, TCommand>(state);
this.states.set(state, newState);
return this.states.get(state) as State<TState, TCommand>;
}
public handle(command: TCommand): void
{
const state = this.states.get(this.currentState) as State<TState, TCommand>;
const newState = state.handle(command);
this.currentState = newState;
}
public getCurrentState(): TState
{
return this.currentState;
}
}
class State<TState, TCommand>
{
private state: TState;
private transitionMap: Map<TCommand, TState>;
constructor(state: TState)
{
this.state = state;
this.transitionMap = new Map<TCommand, TState>();
}
public configureTransition(command: TCommand, newState: TState): State<TState, TCommand>
{
this.transitionMap.set(command, newState);
return this;
}
public handle(command: TCommand): TState
{
return this.transitionMap.get(command) as TState;
}
}
enum TvState
{
On = "TvOn",
Off = "TvOff",
}
enum TvCommand
{
TurnOn = "TurnOn Command",
TurnOff= "TurnOff Command",
}
// використання
const tvState = new StateMachine<TvState, TvCommand>(TvState.Off);
tvState
.addState(TvState.Off)
.configureTransition(TvCommand.TurnOn, TvState.On);
tvState
.addState(TvState.On)
.configureTransition(TvCommand.TurnOff, TvState.Off);
console.log(`Current state = ${tvState.getCurrentState()}`);
tvState.handle(TvCommand.TurnOn);
console.log(`Current state = ${tvState.getCurrentState()}`);
tvState.handle(TvCommand.TurnOff);
console.log(`Current state = ${tvState.getCurrentState()}`);
ДжерелаЛітератураАлан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М. : «Вильямс», 2002. — 288 с. — ISBN 0-201-71594-5.
|