デザインパターン

Builderパターン

今日はBuilderパターンです

さて、GOF本 を読むと、

  • 複合オブジェクトについて、その制作過程を表現形式に依存しないものにすることにより、同じ制作過程で異なる表現形式のオブジェクトを生成できるようにする。

と、ある。 複合オブジェクト ってなに?

とりあえずGOF本はまだ難しいので「Java言語で学ぶデザインパターン 」を読んでみたが、

あれー?これ、Template Methodパターンと何が違うのん?なるほど、わからん。

f:id:boxer_programmer:20100825233124p:image:left

本を読む分には、DirectorとBuilder,TextBuilder,HTMLBuilderの関係を強調しているけど

そうじゃないでしょ。その関係はTemplate MethodのキモであってBuilderのキモじゃないでしょ!

じゃあBuilderのキモってなんなのさってのがJava言語デザパタ本では不明確なので、もう一度GOF本を読んでみる。

多分この 複合オブジェクト というのがキモではないかと、俺のカンは言っている。

GOF本にケチをつけるのは畏れ多いけど、ショボプログラマの自覚を持ちつつもP105,P106の図

にケチをつけたくなりました。

P105のクラス図にBuilderからそれぞれ派生したクラスのメソッドに

GetASCIIText(),GetTeXText(),GetTextWidget()があります。派生クラスにそれぞれ

定義されているのです。いや…これ、僕的にはどう考えてもTextConverterにGetText()メソッドを追加すべきでしょ…

と思うんですが、不遜な考えでしょうか?

読み間違えているのかな、とより抽象的なP106の図を見てみる

f:id:boxer_programmer:20100825233125p:image:left

うーん。やっぱりConcreateBuilderクラスにGetResult()メソッドがあって、Builderインターフェイスにはない。

…いや、やっぱりこれはBuilderインターフェイスに持ってくるべきでしょう!!

僕は本を鵜呑みにするのはあまり意味がないと思うのだ。確かに独善的に読むのは良くないが、

経典や聖書じゃないんだから…数学の本も暗記したところで意味ないでしょ?

後から恥をかいたっていいのだ!!俺は声を大にして言う「GetResult()メソッドはBuilderに純粋仮想関数として定義すべき」だと!!

さて、自己主張は置いておいて、「適用可能性」を見てみよう。

とある。とあるので、GetResult()はインターフェイスにあるべきだと思うのです。

結論

結局のところ、Builderパターンは、それの実装によらず、クライアント側が呼び出すメソッドを組み合わせて最終的に「何か」を生成するパターンですね。

なので、「何か」をクライアントに渡すメソッドはBuilder側に必要である(この場合はGetResult)

要点:

  • メソッドを組み合わせる責任はクライアントが負う
  • Builder側は組み合わせ通りになんかしら生成する。(その生成したものをクライアントに渡されることもある)
  • 複数のBuilderがある場合にはTemplate Methodパターンも使用される

さてこれも使いどころはあるのか…と。まぁ、頭に入れておけばなんらかのヒントになるかな…。

正直あまり面白いパターンじゃないなぁ…。

| | コメント (0) | トラックバック (0)

Decoratorパターン

Decoratorパターン
はい、今日は先生がDecoratorパターンを解説するぞー!
…使ったことないし、仕事でも使ってるコードないけどな。ゲームプログラムでは使いでがないのかな。

まぁ、知識として知っておくとか、多分、ツールプログラミングでは使用することも多いかモナー

とりあえずGOF本では、このパターンの役割はこうあります

☆オブジェクトに責任を動的に追加する☆

おお、言葉の意味はよくわからんが、とにかくすごく…役立ちそうです。
GOF本の残念なところは、僕ら凡人には「言葉の意味はよくわからん」状態にされてしまうとこですな。

責任ってなんやねん…。実は僕もよくわからん。たぶん分かる人に聞いたら、「責任は責任や!」
と返されると思います。分からんことがわからん…平行線です。そんなんじゃドウシヨウモナイので
誤解があるかも知れんが、僕なりの解釈でガンガン行きます。

責任=機能

じゃないかな、と思うわけです。機能ってのはなんかしらの関数によって実装されます。
それが世界に働きかける働きかけを指します。う、うう、うまく説明できないのだ。
例をあげると、文字列を出力したり、計算したり、音を出したり。

で、GOF本では、「文字列表示」「スクロールバー機能」「枠を描画する」とかが例に挙げられています。

さて、実装の方法を簡単に言うとですね、

キモ1.デコレータと被デコレータを同じインターフェイスから継承させる

このときのインターフェイスは基本的に純粋仮想関数だと僕は思うわけです。

これによって、デコレータは被デコレータを知らなくてもいいわけです。自分と同じ関数を持っていることは
保証されます。

キモ2.デコレータ側は、被デコレータへの参照を持っている
キモ3.デコレータ側の実装では、被デコレータのメソッドをコールする

で、キモ3でコールするときに、デコレータ側で表現したい機能を実行します。

たとえば、こういう基底クラスを用意します。機能は「何かを表示する」「表示するものの大きさを返す」
だけです。

//基底クラス
//デコレータする側、される側は双方ともにこっから継承する
//これによりデコレータ側がコンポーネントのインターフェイスを知っていることになる
class Component
{
    public:
        //このメソッドがデコレータ、非デコレータ双方で実装されているとする
        //別にどんなメソッドでもかまわない。キモはデコレータと被デコレータで同じ
        //メソッドが定義されることにある
        virtual void Visualize()=0;
        virtual size_t Length()=0;
};

っすね、これを被デコレータ側で実装すると

//被デコレータ基底
class Concrete : public Component
{
    public:
        Concrete(const string str) : m_str(str){
        }
        void Visualize(){
            cout << m_str;
        }
        size_t Length(){
            return m_str.length();
        }
    private:
        const string m_str;
};

コンストラクト時に設定された文字列を表示するだけですね。

Concrete c("uho iiotoko");
c.Visualize();

とかやると、uho iiotokoが表示されます
じゃあ、これの左右に飾り文字をつけたいとします。

//デコレータ基底
//デコレータは、被デコレータへの参照を持っている必要がある
class Decorator : public Component
{
    public:
        Decorator(Component& component) : m_component(component){
        }
        virtual void Visualize(){
            cout << "*";
            m_component.Visualize();
            cout << "*";
        }
        virtual size_t Length(){
            return sizeof('*') + m_component.Length() + sizeof('*');
        }
    protected:
        Component& m_component;
};

こうですね、でこれを使用するには

Decorator d(c);
d.Visualize();

ですから、結果は*uho iiotoko*になるわけ。

さらに別の種類のボーダーを作ってみます。

//境界線デコレータ
class BorderDecorator : public Decorator
{
    public:
        BorderDecorator(Component& component) : Decorator(component){}
        void Visualize(){
            cout << "///";
            m_component.Visualize();
            cout << "///";
        }
        size_t Length(){
            return sizeof("///")-1 + m_component.Length() + sizeof("///")-1;
        }
};

こう使用すると

BorderDecorator b(c);
b.Visualize();
cout << endl;

当然結果は

///uho iiotoko///

です。不思議でもなんでもないです。何が嬉しいの?ばかなの?

うれしくなってくるのはこれを組み合わせて使用できるからです。
俺の師匠言うところの組み合わせの妙ってやつです。
DecoratorはComponentとみなすこともできるわけですから…

Concrete c("uho iiotoko");
Decorator d(c);
BorderDecorator bd(d);

とやるとどうでしょう。

///*uho iiotoko*///

になります。うん、あんまりうれしくないね。

さてさてこういうのを作ってみる


//境界線デコレータ
class VerticalBorderDecorator : public Decorator
{
    public:
        VerticalBorderDecorator(Component& component) : Decorator(component){}
        void Visualize(){
            RepeatWrite(m_component.Length(),'-');
            cout << endl;
            m_component.Visualize();
            cout << endl;
            RepeatWrite(m_component.Length(),'-');
        }
        size_t Length(){
            m_component.Length();
        }
    private:
        void RepeatWrite(int n,const char c){
            for(int i=0;i<n;++i){
                cout << c;
            }
        }
};

ってのを作って、

    Concrete c("uho iiotoko");
    c.Visualize();
    cout << endl;
    Decorator d(c);
    d.Visualize();
    cout << endl;
    BorderDecorator b(c);
    b.Visualize();
    cout << endl;
    BorderDecorator bd(d);
    bd.Visualize();
    cout << endl;
    VerticalBorderDecorator v(c);
    v.Visualize();
    cout << endl;
    VerticalBorderDecorator vb(b);
    vb.Visualize();
    cout << endl;
    VerticalBorderDecorator vbd(bd);
    vbd.Visualize();
    cout << endl;

こう使ってみると、結果は

uho iiotoko
*uho iiotoko*
///uho iiotoko///
///*uho iiotoko*///
-----------
uho iiotoko
-----------
-----------------
///uho iiotoko///
-----------------
-------------------
///*uho iiotoko*///
-------------------

てなことになるわけで、いろいろな組み合わせが楽しめるわけです。
実際に業務で使う機会はあるか知りませんが、なんらかのヒントになるのではないかと…

| | コメント (0) | トラックバック (0)

FlyWeight

ついでにFlyweight。いやぁ、馴染み深いコトバです。なぜなら僕がフライ級ボクサーだからですが…そんなことはどーでもいい。

えーと、まぁメモリとかロード時間とかいろいろ節約するためのパターン。もう適当な実装書いたほうが早いんで書きます。
#include<map>
using namespace std;

class Loader
{
private:
  map<string,char*> m_map;
public:
  void* load(string filename)
  {
   map<string,char*>::iterator it = m_map.find(filename);
   if( it != m_map.end() ){//既にロード済
    return it->second;//ロードせずポインタを返す
   }
   else{//新規ロード
    fopenだのなんだの...
    char* buf = freadだのなんだの...
    m_map.insert(make_pair(filename,buf) );//新規データを登録
    return buf;
   }
  }
};

…まぁサンプルなんで解放とかの問題なんて気にすんな!要は上のようにして余計なメモリやファイルアクセスをしないようにするってことだね!!ただこのままだとどんどんたまっていってバッドアロック例外になるはずなんで、それは各自考えてくれ!!まぁ参照カウンタつきスマートポインタでも実装してくれたまえ。

それができる人はFlyweightくらい知ってるって?まぁ、そうかもね。この実装だとマップから削除するタイミングが意外と難しいんじゃないかな。上の例ではナマポインタだけど、IsAliveだのIsValidだのチェックできるようにして、毎フレ削除更新かけるのもありだと思うけど。

| | コメント (0) | トラックバック (0)

Abstruct Factory

はい、今日は先生がAbstruct Factoryについて解説するぞー。

簡単に言います。「アブストラクトファクトリ」は「ファクトリ」を切り替え可能にするためのパターンです。

…言っちゃった。

ファクトリはわかりますよね?わかんねぇ?例えば、C++で適当に書くなら

class IBase
{
public:
  virutal void tekitou()=0;
};

class Derive1 : public IBase
{
public:
  virutal void tekitou()
  {
   cout << "A";
  }
};

class Derive2 : public IBase
{
public:
  virutal void tekitou()
  {
   cout << "B";
  }
};

とかやって、

class Factory
{
public:
  IBase* create(int param)
  {
   if(param){
    return new Derive1();
   }
   else{
    return new Derive2();
   }
  }
};

で、

Factory f;
IBase* b = f.create(0);
b.tekitou();
IBase* c = f.create(1);
c.tekitou();

てな感じ。どっちもIBase型なのに、振舞いがかわるんだねぇ。むむっ、仕事が忙しくて少しだけボクサーにあるまじきお腹になっている!!ちょっと腹筋200回します。

…ハァハァ、プログラマとボクサーの両立は大変ですたい。

あ、まだAbstruct Factoryじゃないですよ。ここまでは通常のC++のテクニック、Factory Methodではあるけれど、まぁわざわざパターンって呼ぶまでもないのねん。で、ここからがAbstruct Factory

その名の通り、ファクトリクラスをAbstructにします。つまり

class IFactory
{
public:
  vitrual IBase* create(int param)=0;
};

class IFactory
{
public:
  vitrual IBase* create(int param)=0;
};

class Factory1 : public IFactory
{
public:
  vitrual IBase* create(int param)
  {
   if(param){
    return new Derive1();
   }
   else{
    return new Derive2();
   }
  }
};

class Factory2 : public IFactory
{
public:
  vitrual IBase* create(int param)
  {
   if(param){
    return new Derive3();
   }
   else{
    return new Derive4();
   }
  }
};

と、なります。で、これを例えばシステムに組み込むとすれば

class System
{
private:
  IFactory* m_factory;
public:
  void SetWindowFactory(IFactory* factory){
   m_factory = factory;
  }
  void createWindow(int param){
   m_factory.create(param);
  }
};

System s;
s.SetWindowFactory(new Factory2);
s.createWindow(1);

てな使い方ができる。つまりファクトリ自体の振舞いが変わるってわけ。そんなことして何の利点があるん?というとですな。まぁ、場面によって生成されるものの性質を大きく変えたいとか、マルチプラットフォーム時のためとか、アロケータだのローダーの生成方法を変えたいときに使用する。

ここでの例のように生成されるウィンドウの種別を変えてもいいけどね(GOF本の解説ではそういう用途だった)。

| | コメント (0) | トラックバック (0)