observer pattern

GoF design pattern のアレ。 C++ でのわかりやすいまともな実装例が見つからなかったので自分で書いてみた。とりあえず wikipedia:Observer パターンhttp://www.objectclub.jp/technicaldoc/pattern/observer を参考に名前はそのまま ( CamelCase な識別子は C++ 風に小文字 + under_bar に変えてある ) でできるだけ simple に。いわゆる状態を表わす型は任意に指定できたほうがいいよなと思って template で指定できるようにしてある。

でまぁここで定義されてる class を継承していわゆる concreate_subject と concrete_observer を定義して使うわけだ。というわけで長いけど以下が使用例。実質的なところは Language class ( concreate_subject にあたる ) と Fruit class ( concrete_observer にあたる ) の定義ね。 VC++ 2008 Express Edition ( cl.exe /EHsc /W4 main.cpp ) と andLinux g++ 4.3.3 ( g++ -Wall --pedantic main.cpp ) で確認。

// main.cpp

#include <iostream>
#include <vector>
#include "observer.hpp"

enum language_t {
    CHINESE,
    ENGLISH,
    GERMAN,
    ITALIAN,
    JAPANESE,
    RUSSIAN,

    NUMOF_LANG
};

const char* language_name(const language_t lang) {
    switch (lang) {
        case CHINESE:   return "Chinese";
        case ENGLISH:   return "English";
        case GERMAN:    return "German";
        case ITALIAN:   return "Italian";
        case JAPANESE:  return "Japanese";
        case RUSSIAN:   return "Russian";
        default:        return NULL;
    }
}

class Language : public pattern::observer::basic_subject<language_t> {
    private:
        language_t lang;

    protected:
        // implementation of virtual function
        // tell what object represents a state to basic_subject
        const state_t& state(void) const { return lang; }

    public:
        Language(language_t lang = ENGLISH) : lang(lang) { notify(); }
        void set(language_t lang) { this->lang = lang; notify(); }
        const char* what(void) const { return language_name(lang); }
};

std::ostream& operator<< (std::ostream& out, const Language& l) {
    return out << l.what();
}

class Fruit : public pattern::observer::basic_observer<language_t> {
    protected:
        language_t lang;

    public:
        Fruit() : lang(ENGLISH) {}
        virtual ~Fruit() {}

        void language(const language_t lang) { this->lang = lang; }
        const char* language(void) const { return language_name(lang); }
        virtual const char* name(void) const = 0;

        // implementation of virtual function
        void update(const state_t& s) { lang = s; }
};

std::ostream& operator<< (std::ostream& out, const Fruit& f) {
    return out << f.name() << "(" << f.language() << ")";
}

class Apple : public Fruit {
    public:
        const char* name(void) const {
            switch (lang) {
                case CHINESE:   return "\u82f9\u679c";
                case ENGLISH:   return "apple";
                case GERMAN:    return "apfel";
                case ITALIAN:   return "mela";
                case JAPANESE:  return "\u308a\u3093\u3054";
                case RUSSIAN:   return "\u044f\u0431\u043b\u043e\u043a\u043e";
                default:        return NULL;
            }
        }
};

class Orange : public Fruit {
    public:
        const char* name(void) const {
            switch (lang) {
                case CHINESE:   return "\u6a58\u5b50";
                case ENGLISH:   return "orange";
                case GERMAN:    return "orange";
                case ITALIAN:   return "mandarino";
                case JAPANESE:  return "\u307f\u304b\u3093";
                case RUSSIAN:   return "\u043c\u0430\u043d\u0434\u0430\u0440\u0438\u043d";
                default:        return NULL;
            }
        }
};

class Banana : public Fruit {
    public:
        const char* name(void) const {
            switch (lang) {
                case CHINESE:   return "\u9999\u8549";
                case ENGLISH:   return "banana";
                case GERMAN:    return "banane";
                case ITALIAN:   return "banana";
                case JAPANESE:  return "\u3070\u306a\u306a";
                case RUSSIAN:   return "\u0431\u0430\u043d\u0430\u043d";
                default:        return NULL;
            }
        }
};

int main(void) {
    // create fruits
    Apple apple;
    Orange orange;
    Banana banana;

    // print default state
    std::cout
        << apple << "\n"
        << orange << "\n"
        << banana << "\n"
        << std::endl;

    // build language selector
    Language lang;
    // register fruits
    lang.attach(apple).attach(orange).attach(banana);

    // register all languages
    typedef std::vector<language_t> lang_list_t;
    typedef lang_list_t::iterator   lang_it;
    lang_list_t langs;
    langs.push_back(CHINESE);
    langs.push_back(ENGLISH);
    langs.push_back(GERMAN);
    langs.push_back(ITALIAN);
    langs.push_back(JAPANESE);
    langs.push_back(RUSSIAN);

    // print all languages
    for (lang_it it = langs.begin(); it < langs.end(); ++it) {
        lang.set(*it);
        std::cout
            << apple << "\n"
            << orange << "\n"
            << banana << "\n"
            << std::endl;
    }

    // release apple
    lang.detach(apple);

    // print in Japanese
    lang.set(JAPANESE);
    std::cout
        << apple << "\n"    // in Russian!!
        << orange << "\n"
        << banana << "\n"
        << std::endl;

    return 0;
}