Skip to content.

Sections
Personal tools
You are here: Home » コミュニティ » masarl memorial » homepage3.nifty.com » masarl » article » nifty-logs » Niftyの過去ログ集 - Attachment パターン

Niftyの過去ログ集 - Attachment パターン

Document Actions

Attachment パターン

Attachment パターンというのは,Macintosh のフレームワークである MacApp と PowerPlant で利用されているパターンです.初めて聞く人がほとんどだと思うのですが,それは僕が名付け親なので当然です(笑)(といっても PowerPlant のクラス名からパクったのですが).一言で言えば,Decorator パターンの機能をStrategyパターンで実装している,というものです.

00942/00942 BYI20012  まさーる        RE:3つの再利用の方法
( 9)   96/08/29 22:23  00933へのコメント

Junichi Suzuki さん、こんにちは。

>・再利用性にはいくつかの方法がある(pp.117).
>
>(1)継承を用いる方法
>(2)既存のクラスのインスタンスを生成する方法
>  クラスライブラリやフレームワークを利用する方法
>(3)既存のクラスのインスタンス同士を,自身で新たなクラスを
>作成することなく結合する方法
>
>この(3)の方法を勉強するのは初めてだったので,非常に興味深かった
>です.これに比べると,dependency(Observable/Observer)もローレベル
>のものに見えてしまいます;-).どこかに良いサンプルないかなぁ・・・.

確かに、OOで強調されるのは(1)の継承を用いる再利用で、(2)や(3)の再利用は
あまり強調されていなかったような気がします。オブジェクトの機能を拡張する
のに(1)を使うより(3)を使う方がフレキシブルでかなり強力ですよね(その分プ
ログラムが複雑になったり、効率落ちたり、小さなオブジェクトが増えるなどの
デメリットもありますが)。

(3)の方法で典型的なのはDecoratorのパターンではないかと思います。継承を使
わずにインスタンスの組み合わせで動的に機能を拡張できますから。

Decoratorと同様の機能を満たすものとしてはStrategyパターンの応用例(僕は
勝手にAttachmentのパターンと呼んでます)がありますね。僕としてはこっちの
方が使いやすいと思っています。

MacintoshのフレームワークであるMacAppでは、Viewの見てくれを修飾するのに
このAttachmentのパターンを使っています。ViewはAdornersというAdornerクラ
スのインスタンスのリストを保持していて、実際に描画するメソッド
(HandleDraw)で自分自身を描画するほかに各Adornerインスタンスに描画する
チャンスを与えるよう実装されています。こうすると、Adornerインスタンスを
ViewからくっつけたりはずしたりしてViewの修飾を動的に変えることができま
す。

Adornerクラスのほかに、Behaviorクラスというのもあってこれは修飾に限ら
ず、ユーザからのマウス入力やアイドリング処理などのとき自分自身で処理する
前にBehaviorインスタンスのリストに各イベント処理のチャンスを与えること
で、簡単にViewの振る舞いを拡張・変更できるよう工夫されています。

#と、ここまで書いてデザインパターン本をみたらMacAppについてまったく同じ
ようなことが載ってますね^^;。ちょっと前までMacAppプログラマだったもの
で。

                                     96/08/29(木) まさーる(BYI20012)

3番目の再利用の例として Attachment パターンを挙げました.その後,以前話題にのぼったボンドカー(流星号?)のプログラムをOzさんが Decorator パターンを使って書いておられたので,今度は僕が Attachment パターンを使って書いてみたのが次のログです.

00993/00993 BYI20012  まさーる        RE^2:ボンドカー再び(これも長文)
( 9)   96/09/10 22:45  00990へのコメント

Oz さん、こんにちは。

OzさんのマネをしてDecoratorでなくAttachment(Strategy)で書いてみました。
destractorとかdelete処理は省略してます。

比較すると面白いかもしれません。

////////////////////////////////////
#include <iostream.h>

class VehicleAttachment
{
    friend class Vehicle;
public:
    virtual void DoAccelerate() = 0;
protected:
    VehicleAttachment() : mNextAttachment(0) {}
    VehicleAttachment* mNextAttachment;
};

class Vehicle
{
public:
    void Accelerate()
    {
        VehicleAttachment* p = mFirstAttachment;
        while(p)
        {
            p->DoAccelerate();
            p = p->mNextAttachment;
        }
        DoAccelerate();
    }
    virtual void DoAccelerate() = 0;
    void AddAttachment(VehicleAttachment* p)
    {
        p->mNextAttachment = mFirstAttachment;
        mFirstAttachment = p;
    }
protected:
    Vehicle() : mFirstAttachment(0) {}
    VehicleAttachment* mFirstAttachment;
};

class Car : public Vehicle
{
public:
    void DoAccelerate() {cout << "\tOn the earth";}
};

class Airplane : public VehicleAttachment
{
 public:
    void DoAccelerate() { cout << "\tIn the air." << endl; }
};

class Boat : public VehicleAttachment
{
 public:
   void DoAccelerate() { cout << "\tOn the sea" << endl; }
};

class Submarine : public VehicleAttachment
{
 public:
   void DoAccelerate() { cout << "\tUnder the sea." << endl;}
};

class SpaceShuttle : public VehicleAttachment
{
 public:
   void DoAccelerate() { cout << "\tIn the space." << endl; }
};

void MachGoGo(Vehicle* p, char* n);

void main(void)
{
    Vehicle* RyuseiGo = new Car;
    RyuseiGo->AddAttachment(new SpaceShuttle);
    RyuseiGo->AddAttachment(new Airplane);
    RyuseiGo->AddAttachment(new Submarine);
    RyuseiGo->AddAttachment(new Boat);
    MachGoGo(RyuseiGo, "流星号");

    Vehicle* BondCar = new Car;
    BondCar->AddAttachment(new Submarine);
    MachGoGo(BondCar, "Bond Car");
}

void MachGoGo(Vehicle* p, char* n)
{
   cout << n << " can move " << endl;
   p->Accelerate();
   cout << endl;
}

                                     96/09/10(火) まさーる(BYI20012)

上のプログラムで Attachment パターンがどんなものかわかってもらえたかと思います.Decorator パターンに比べて優位な点も不利な点もありますが,Decorator パターンに比べこちらの方が単純で扱いやすいと言えます.

なおこのプログラムをそのまま利用しないで下さい.オブジェクトを破棄してないので^^;.

MacApp や PowerPlant では,この Attachment パターンをリソースエディタでサポートしています.つまり,既にコンパイル済みのプログラムのリソースをリソースエディタで変えられます.これがすごいところですね.

02565/02565 BYI20012  まさーる        MacAppのポトペタ
( 9)   97/04/02 23:16  02555へのコメント


小林さん、こんにちは。

>  直接は関係ないんですが(^^;、InsideWindows5月号には
>C++Builder & Optima++と、二つのポトリペタペタC++ツールの
>トライアル版が付いてますね。ついにC++erもポトペタが必須か?

(小林さんはあまり興味ないかなと思いつつ...)
MacAppでは、ポトペタをリソースエディタ(ViewEdit)を使って実現してます。

ウィンドウの見てくれをこれで作るんですけど、リソースの中にViewの位置等は
もちろんですが、クラス名の情報が入っているので実行時にMacAppがクラス名か
らそのViewインスタンスを生成して最終的なウィンドウインスタンスをつくりま
す。
この方式だとリソースエディタに自分で作ったViewの子クラス等を直接貼り付け
れるので便利です。

さらに、このリソースエディタはMacAppのAttachmentパターンに対応していま
す。つまり、リソースエディタ上で、あるViewにAdornerクラスをつける指定や
Behaviorクラスをつける指定もできます。(Adornerクラスは個々のViewの見て
くれを装飾するクラスで、BehaviorクラスはViewに機能を加えるクラス #942参
照)

面白がって、RadioButtonBehaviorとかCheckboxBehaviorなんか作って引っつけ
てました。こうすると、ソース上どこにもラジオボタンの機能を入れてないアイ
コンが勝手にラジオボタンのように振る舞ってしまいます。

欠点は、そのリソースエディタよく落ちてたまにリソースファイルを破壊するこ
とがあったとかでしょうか(^^;(今はどうなのか知らないですが)。

                                     97/04/02(水) まさーる(BYI20012)

参考資料