誰かが使ったらアリスに知らせろ

怒涛のアリプラシリーズをもうひとつ。

鞄に対してマーキングをするのは消極的な手段でして、マーキングを上書きされてしまったり、マーキング自体を無視して Use メソッドを呼び出したりするとアリスはお手上げです。なので、もっと積極的に違法な Use メソッドが呼び出されたら、アラートメッセージがアリスに届くようにします。
そう、mvvm パターンの INotifyPropertyChanged と同じです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// for 誰かが使ったらアリスに知らせろ
 
#include <iostream>
#include <list>
using namespace std;
 
class ICatch {
public:
    virtual void OnTatch( void *bag ) = 0;
};
 
class INotify {
protected:
    ICatch *catcher;
public:   
    void setCatcher( ICatch *user ) {
        catcher = user;
    }
    virtual void OnTatch( void *bag ) {
        if ( catcher != NULL ) {
            catcher->OnTatch( bag );
        }
    }
};
 
class Bag : public INotify {
protected:
    void *_mark;
public:
    Bag() : _mark(NULL) {}
    virtual void setMark( void *mark ) {
        // 1回だけマークできる
        if ( _mark == NULL ) {
            _mark = mark;
        }
    }
    virtual void *getMark() {
        return _mark;
    }
    virtual void Used( void *user ) {
        if ( _mark != NULL && _mark != user ) {
            OnTatch( this );
        }
    }
};
 
class Prada : public Bag {};
class Tiffany : public Bag {};
 
class Alice : public ICatch
{
private:
    list<Bag*> bags;
public:
    Alice() {
    }
    void Present( Bag *bag )
    {
        if ( dynamic_cast<Prada*>(bag) != NULL &&
             bag->getMark() == NULL )
        {
            bag->setMark( this );
            bag->setCatcher( this );
            bags.push_back( bag );
        }
    }
    int CountBags()
    {
        return bags.size();
    }
    void Use( Bag *bag ) {
        bag->Used( this );
    }
    virtual void OnTatch( void *bag ) {
        cout << "who use my Prada bag!!!" << endl;
    }
};
 
class Lolita
{
private:
    list<Bag*> bags;
public:
    void Present( Bag *bag )
    {
        bag->setMark( this );
        bags.push_back( bag );
    }
    void Use( Bag *bag ) {
        bag->Used( this );
    }
};
 
int main(void)
{
    Alice alice;
    Lolita lolita;
     
    Prada *bag = new Prada();
    // アリスにプレゼント
    alice.Present( bag );
    // アリスが使う分には問題なし
    alice.Use( bag );
    // 同じ鞄をロリータにプレゼント
    lolita.Present( bag );
    // ロリータが鞄を使うと...
    lolita.Use( bag );
 
    return 0;
}

鞄(Bag)のほうには、INotiry インターフェースを仕込んでおきます。アラートメッセージをを受け取る側(Alice)のほうは、ICatch インターフェースで受け取りの関数を用意しておきます。C++ で2つのインターフェースを使っているのは C# のデリゲートの機能がないので、こうなっています。static な関数ポインタを用意してもいいのですが、どうせならば、Alice のメソッドとして用意したいところですよね。
実は、モノである鞄(Bag)に機能を仕掛けておいて、人であるアリスに通知する、ってのがいい感じかなと。

これを実行すると、

1
2
D:\work\blog\src\alice>a
who use my Prada bag!!!

誰か私のバッグを使ったッ!!!って怒ります。ロリータからは Use メソッドの中身は見れないし、そもそも Lolita は ICatch を継承してないのでアラートメッセージを受け取りません。

似たようなパターンとしては、

  • Bag クラスのデストラクタに仕込んで、なんで、私の鞄を捨てるのよッ!!!とか。
  • List を改造して、なんで、私の鞄を持っているのよッ!!!とか

ができます。鞄というオブジェクトはひとつなので、Alice::bags と Lolita::bags の両方にあるってのは妙な感じですよね。実際は、ポインタが使われているので、鞄そのものが置いてあるというよりも、鞄を示すタグ(所有権)を撮り廻しているっていう意味になりますが。鞄そのものは実体なのでひとつしかないけど、ポインタ(指し示すもの)は、いくらでもできるから list のようにポインタのコレクションの場合は、所有自体を制限させるのはできないのかなと。写真みたいなものですから。肖像権を主張できても、対象そのものを写真で写すこを制限するのは難しい、っていう意味ですね。

カテゴリー: C++ パーマリンク