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*///
-------------------
てなことになるわけで、いろいろな組み合わせが楽しめるわけです。
実際に業務で使う機会はあるか知りませんが、なんらかのヒントになるのではないかと…
最近のコメント