Skeleton of GOF's Design Pattern


STATEの骸骨

STATE

その名の通り、状態遷移のためのパターンです。

Stateパターンは、状態の変化を、コンポジションしているオブジェクトを変えることによって実現します。このパターンは、各状態の処理を状態ごとに定義したクラスに反映できるため、状態遷移マップをそのまま設計に置き換えることができ、とても便利なパターンです。

『STATEの骸骨』では、次のような、STATEマップを実装におきかえてみました。

Event SLEEPING WORKING EATING
requestWakeup Goodmorning/WORKING - -
requestHello - SayHello/- SayHello/-
requestEat - Eat/EATING -
requestFinish - - Finish/WOKING
requestGotoBed - Goodnight/SLEEPING Finish,Goodnight/SLEEPING
※表中の(sayGoodmorning/WORKING)は、アクション/次へ遷移する状態 を表しています。


■ 『STATEの骸骨』の特徴的な構造


■ 『STATEの骸骨』の特徴的な協調関係


■ 『STATEの骸骨』 プログラム・コード

□ Java2版
// Copyright(C) 2000-2003 Yoshinori Oota All rights reserved.

public class StateSample {
    static public void main(String[] args) {
        Context man = new Context();  // 初期状態では、寝てる

        man.reqWakeup();   //おきろ!
        man.reqHello();    //こんにちは、
        man.reqLetsEat();  //ご飯をたべよう。

        // おかしなリクエストをしてみる。
        man.reqWakeup();   //おきろ! 無視されました。

        man.reqFinish();   // 食事はおしまい。
        man.reqHello();    //こんにちは。
        man.reqLetsEat();  //ご飯をたべよう。
        man.reqGotoBed();  //そろそろ寝よう。 両方挨拶できました。

        // あいさつしてみる。
        man.reqHello();    //無反応ですね。熟睡?
        man.reqWakeup();   //おきろーーーまた仕事だーーー
    }
}

class Context {
    static final State SLEEPING = new Sleeping();
    static final State WORKING  = new Working();
    static final State EATING   = new Eating();
    private State  _currentState = SLEEPING;

    public void Set(State nextState) {
         _currentState = nextState;
    }
    public void reqWakeup() {
        _currentState.wakeup(this);
    }
    public void reqLetsEat() {
        _currentState.eat(this);
    }
    public void reqHello() {
        _currentState.sayHello(this);
    }
    public void reqFinish() {
        _currentState.finish(this);
    }
    public void reqGotoBed() {
        _currentState.gotoBed(this);
    }
}

abstract class State {
    abstract public void wakeup(Context context);
    abstract public void sayHello(Context context);
    abstract public void eat(Context context);
    abstract public void finish(Context context);
    abstract public void gotoBed(Context context);
}

class Sleeping extends State {
    public void wakeup(Context context) {
        System.out.println("おはよう!");
        context.Set(Context.WORKING);
    }
    public void sayHello(Context context) {}
    public void eat(Context context) {}
    public void finish(Context context) {}
    public void gotoBed(Context context) {}
}

class Working extends State {
    public void wakeup(Context context) {}
    public void sayHello(Context context) {
        System.out.println("こんにちは!");
    }
    public void eat(Context context) {
        System.out.println("いただきまーす");
        context.Set(Context.EATING);
    }
    public void finish(Context context) {}
    public void gotoBed(Context context) {
        System.out.println("おやすみなさい");
        context.Set(Context.SLEEPING);
    }
}

class Eating extends State {
    public void wakeup(Context context) {}
    public void sayHello(Context context) {
        System.out.println("こんにちは!");
    }
    public void eat(Context context) {}
    public void finish(Context context) {
        System.out.println("ごちそうさま!");
        context.Set(Context.WORKING);
    }
    public void gotoBed(Context context) {
        System.out.println("ごちそうさま!");
        System.out.println("おやすみなさい");
        context.Set(Context.SLEEPING);
    }
} 

○ C++版
// Copyright(C) 2003 Yoshinori Oota All rights reserved.

#include <iostream>
using namespace std;

class State;

class Context {
public:
    Context();
    ~Context();
    void Set(State* next);
    void reqWakeup();
    void reqLetsEat();
    void reqHello();
    void reqFinish();
    void reqGotoBed();
protected:
    friend class SleepingState;
    friend class WorkingState;
    friend class EatingState;
    static State* SLEEPING;
    static State* WORKING;
    static State* EATING;
private:
    State* currentState_;
};

class State {
public:
    State();
    virtual ~State();
protected:
    friend class Context;
    virtual void wakeup(Context* context) = 0;
    virtual void sayHello(Context* context) = 0;
    virtual void eat(Context* context) = 0;
    virtual void finish(Context* context) = 0;
    virtual void gotoBed(Context* context) = 0;
};

class Sleeping : public State {
public:
    Sleeping();
    virtual ~Sleeping();
protected:
    virtual void wakeup(Context* context);
    virtual void sayHello(Context* context);
    virtual void eat(Context* context);
    virtual void finish(Context* context);
    virtual void gotoBed(Context* context);
};

class Working : public State {
public:
    Working();
    virtual ~Working();
protected:
    virtual void wakeup(Context* context);
    virtual void sayHello(Context* context);
    virtual void eat(Context* context);
    virtual void finish(Context* context);
    virtual void gotoBed(Context* context);
};

class Eating : public State {
public:
    Eating();
    virtual ~Eating();
protected:
    virtual void wakeup(Context* context);
    virtual void sayHello(Context* context);
    virtual void eat(Context* context);
    virtual void finish(Context* context);
    virtual void gotoBed(Context* context);
};


State* Context::SLEEPING = new Sleeping();
State* Context::WORKING  = new Working();
State* Context::EATING   = new Eating();

Context::Context() : currentState_(SLEEPING) { }
Context::~Context() {
    delete SLEEPING;
    delete WORKING;
    delete EATING;
}
void Context::Set(State* next) {
    currentState_ = next;
}
void Context::reqWakeup() {
    currentState_->wakeup(this);
}
void Context::reqLetsEat() { 
    currentState_->eat(this);
}
void Context::reqHello() {
    currentState_->sayHello(this);
}
void Context::reqFinish() {
    currentState_->finish(this);
}
void Context::reqGotoBed() {
    currentState_->gotoBed(this);
}


State::State() { }
State::~State() { }

Sleeping::Sleeping() { }
Sleeping::~Sleeping() { }
void Sleeping::wakeup(Context* context) {
    cout << "おはよう!" << endl;
    context->Set(Context::WORKING);
}
void Sleeping::sayHello(Context* context) { }
void Sleeping::eat(Context* context) { }
void Sleeping::finish(Context* context) { }
void Sleeping::gotoBed(Context* context) { }

Working::Working() { }
Working::~Working() { }
void Working::wakeup(Context* context) { }
void Working::sayHello(Context* context) {
    cout << "こんにちは" << endl;
}
void Working::eat(Context* context) {
    out << "いただきまーす" << endl;
    context->Set(Context::EATING);
}
void Working::finish(Context* context) { }
void Working::gotoBed(Context* context) {
    cout << "おやすみなさい" << endl;
    context->Set(Context::SLEEPING);
}

Eating::Eating() { }
Eating::~Eating() { }
void Eating::wakeup(Context* context) { }
void Eating::sayHello(Context* context) {
    cout << "こんにちは" << endl;
}
void Eating::eat(Context* context) { }
void Eating::finish(Context* context) {
    cout << "ごちそうさま" << endl;
    context->Set(Context::WORKING);
}
void Eating::gotoBed(Context* context) {
    cout << "ごちそうさま" << endl;
    cout << "おやすみなさい" << endl;
    context->Set(Context::SLEEPING);
}


int main() {
    Context* man = new Context();  // 初期状態では、寝てる

    man->reqWakeup();   //おきろ!
    man->reqHello();    //こんにちは、
    man->reqLetsEat();  //ご飯をたべよう。

    // おかしなリクエストをしてみる。
    man->reqWakeup();   //おきろ! 無視されました。

    man->reqFinish();   // 食事はおしまい。
    man->reqHello();    //こんにちは。
    man->reqLetsEat();  //ご飯をたべよう。
    man->reqGotoBed();  //そろそろ寝よう。 両方挨拶できました。

    // あいさつしてみる。
    man->reqHello();    //無反応ですね。熟睡?

    man->reqWakeup();   //おきろーーーまた仕事だーーー

    delete man;
    return 0;
}

○ C版
/* Copyright(C) 2009 Yoshinori Oota All rights reserved. */
#include <stdio.h>
#include <stdlib.h>
struct State;

enum STATE_TYPE { SLEEPING ,WORKING ,EATING };

/* State Interface */
struct State {
    void (*wakeup)();
    void (*sayHello)();
    void (*eat)();
    void (*finish)();
    void (*gotoBed)();
};
struct State* New_State(enum STATE_TYPE type);
void Delete_State(struct State* state);
/* Sleeping State */
void SleepingState_wakeup();
void SleepingState_sayHello();
void SleepingState_eat();
void SleepingState_finish();
void SleepingState_gotoBed();
/* Working State */
void WorkingState_wakeup();
void WorkingState_sayHello();
void WorkingState_eat();
void WorkingState_finish();
void WorkingState_gotoBed();
/* Eating State */
void EatingState_wakeup();
void EatingState_sayHello();
void EatingState_eat();
void EatingState_finish();
void EatingState_gotoBed();

/* Context Interface */
void Context_Initialise();
void Context_Set(struct State* next);
void Context_reqWakeup();
void Context_reqLetsEat();
void Context_reqHello();
void Context_reqFinish();
void Context_reqGotoBed();
void Context_Finalise();


/* State Class Implementation */
static struct State* gSLEEPING = NULL;
static struct State* gWORKING = NULL;
static struct State* gEATING = NULL;
struct State* gCurrentState = NULL;

struct State* New_State(enum STATE_TYPE type) {
    struct State* state = NULL;
    state =(struct State*)malloc(sizeof(struct State));
    switch (type) {
    case SLEEPING:
        state->wakeup = &SleepingState_wakeup;
        state->sayHello = &SleepingState_sayHello;
        state->eat = &SleepingState_eat;
        state->finish = &SleepingState_finish;
        state->gotoBed = &SleepingState_gotoBed;
        break;
    case WORKING:
        state->wakeup = &WorkingState_wakeup;
        state->sayHello = &WorkingState_sayHello;
        state->eat = &WorkingState_eat;
        state->finish = &WorkingState_finish;
        state->gotoBed = &WorkingState_gotoBed;
        break;
    case EATING:
        state->wakeup = &EatingState_wakeup;
        state->sayHello = &EatingState_sayHello;
        state->eat = &EatingState_eat;
        state->finish = &EatingState_finish;
        state->gotoBed = &EatingState_gotoBed;
        break;
    }
    return state;
}

void Delete_State(struct State* state) {
    if (state == NULL) {
        return;
    }
    free(state);
    state = NULL;
    return;
}

/* Sleeping */
void SleepingState_wakeup() {
    printf("おはよう!\n");
    Context_Set(gWORKING);
    return;
}
void SleepingState_sayHello() { }
void SleepingState_eat() { }
void SleepingState_finish() { }
void SleepingState_gotoBed() { } 
/* Working */
void WorkingState_wakeup() { }
void WorkingState_sayHello() {
    printf("こんにちは\n");
    return;
}
void WorkingState_eat() {
    printf("いただきまーす\n");
    Context_Set(gEATING);
    return;
}
void WorkingState_finish() { }
void WorkingState_gotoBed() {
    printf("おやすみなさい\n");
    Context_Set(gSLEEPING);
    return;
}
/* Eating */
void EatingState_wakeup() { }
void EatingState_sayHello() {
    printf("こんにちは\n");
    return;
}
void EatingState_eat() { }
void EatingState_finish() {
    printf("ごちそうさま\n");
    Context_Set(gWORKING);
    return;
}
void EatingState_gotoBed() {
    printf("ごちそうさま\n");
    printf("おやすみなさい\n");
    Context_Set(gSLEEPING);
    return;
}


/* Context Implementation */
void Context_Initialise() {
    gSLEEPING = New_State(SLEEPING);
    gWORKING  = New_State(WORKING);
    gEATING   = New_State(EATING);
    gCurrentState = gSLEEPING;
    return;
}

void Context_Clean() {
    Delete_State(gSLEEPING);
    Delete_State(gWORKING);
    Delete_State(gEATING);
    gCurrentState = NULL;
    return;
}

void Context_Set(struct State* next) {
    gCurrentState = next;
    return;
}
void Context_reqWakeup() {
    gCurrentState->wakeup();
    return;
}
void Context_reqLetsEat() { 
    gCurrentState->eat();
    return;
}
void Context_reqHello() {
    gCurrentState->sayHello();
    return;
}
void Context_reqFinish() {
    gCurrentState->finish();
    return;
}
void Context_reqGotoBed() {
    gCurrentState->gotoBed();
    return;
}


int main() {
    Context_Initialise();

    Context_reqWakeup();   /* おきろ! */
    Context_reqHello();    /* こんにちは、*/
    Context_reqLetsEat();  /* ご飯をたべよう */

    /* おかしなリクエストをしてみる */
    Context_reqWakeup();   /* おきろ! 無視されました */

    Context_reqFinish();   /* 食事はおしまい */
    Context_reqHello();    /* こんにちは */
    Context_reqLetsEat();  /* ご飯をたべよう */
    Context_reqGotoBed();  /* そろそろ寝よう。 両方挨拶できました */

    /* あいさつしてみる */
    Context_reqHello();    /* 無反応ですね。熟睡? */

    Context_reqWakeup();   /* おきろーーーまた仕事だーーー */

    Context_Clean();

    return 0;
}


改訂履歴

2009.2.28 C言語版を追加
     
     

ご意見、ご感想はこちらまで。

Copyright(C) 2000-2009 Yoshinori Oota All rights reserved.

本ホームページのリンクは自由です。複製、転載される場合は、必ず著者までご連絡をください。