プラダの鞄の話の続き。
■プラダの鞄を共有する
いままで、プラダの鞄をポインタとして話してきましたが、クラスとして扱うとちょっと違った形になります。
Prada *prada = new Prada();
Alice.Present( prada );
な形で、Present メソッドを使って、アリスにすることを考えてみると、実はプラダの鞄は、ロリータにもプレゼントできることが分かります。
Prada *prada = new Prada();
Alice.Present( prada );
Lolita.Present( prada );
ってな訳ですね。
これは、プラダの鞄をアリスにもロリータにもプレゼントができる、っていう意味でデータを共有します。
一見、これで十分なような気もしますが、実は微妙なところがあるのです。
■ロリータは中古のプラダなのよ
そうなんです。
Prada *prada = new Prada();
Alice.Present( prada );
Lolita.Present( prada );
こうしたときには、ロリータに渡るプラダは「中古」なんですね。一度、アリスが使っている(というかプレゼントされている)ので、ロリータが貰う prada とアリスが貰う prada とは微妙に違います。
メモリなので変わらないだろう?という話があるでしょうが、分かり易いように Prada クラスに Used フラグを追加してみましょう。
C++の場合は、こんな感じ。
class Prada {
public:
bool Used ;
};
C#やJavaの場合は、こんな感じ。
class Prada {
public bool Used ;
}
さて、この状態でアリスの Present メソッドを次のように定義すると。
class Alice {
public:
bool Present( Prada *prada ) {
if ( prada->Used == true ) {
return false;
}
prada->Used = true;
return true;
}
};
ほら、プラダの鞄が「中古(Used)」になりますね。
このあたりが、オブジェクト指向のポリフォリズム(隠蔽化)の利用です。
そんな訳で、ロリータに渡されるプラダの鞄は、中古である可能性がある、ということですねw
そうすると、
Prada *prada = new Prada();
if ( Alice.Present( prada ) == false ) {
// 激怒ッ!!!
}
if ( Lolita.Present( prada ) == false ) {
// 激怒ッ!!!
}
という話も作れます。アリスが「激怒ッ!!!」するときは、いきなり中古を私たときです。
例えば、ファクトリークラス「大黒屋」を使うと、
Prada *prada = Daikokuya.Buy();
if ( Alice.Present( prada ) == false ) {
// 激怒ッ!!!
}
ええ、アリスは激怒するかもしれません。
■ Used フラグを隠蔽化してプロパティっぽく
ここで、使用済みフラグ(Used)は、public にして直接変更できるようにしましたが、ここの仕組みを「フィールド」とか「プロパティ」とか「属性」とか言います。
使用済みフラグだとイメージがしづらいので、メソッドに変えると、いわゆるC#のプロパティの例になります。
class Prada {
private:
bool used ;
public:
void Used() {
used = true;
}
};
ってな感じで、Used メソッドってのを作ります。
先の例だと、used = false って設定すると、「中古」から「新品」に勝手に変えることができるから、こんな風に「新品」→「中古」の一方通行の制限ができるといいですよね。
いわゆる、書き込み専用のプロパティ(あるいはメソッド)ってことです。
この場合も同じで、
class Alice {
public:
bool Present( Prada *prada ) {
prada->Used();
…
return true;
}
};
となって、
Prada *prada = new Prada();
if ( Alice.Present( prada ) == false ) {
// 激怒ッ!!!
}
if ( Lolita.Present( prada ) == false ) {
// 激怒ッ!!!
}
こうすると、ロリータが「激怒ッ!!!」するようにしたい。
bool used は非公開(private)だから、使いづらいのでは?と思いますが、さにあらず。例外なんかを使うと結構シンプルに書けます。
class Prada {
private:
bool used ;
public:
Prada() {
used = false;
}
void Used() throw Gekido {
if ( used == true ) {
throw new Gekido();
}
}
};
こんな風に、Used メソッドを使うときは最初の1回だけにしておきます。
このパターンは結構多くて、データベースを Close した後、ファイルハンドルを閉じた後に呼び出されないようにガードする時に便利ですね。
例外をとらえるために、Present メソッドはこうなります。
class Alice {
public:
bool Present( Prada *prada ) {
try {
prada->Used();
} catch {
return false;
}
return true;
}
};
used フラグ、Used メソッドの使い方が異なりますね。
「激怒ッ!!!」を例外でも良いのであれば、シンプルに
class Alice {
public:
void Present( Prada *prada ) throw Gekido {
prada->Used();
}
};
と書いてしまって、
Prada *prada = new Prada();
try {
Alice.Present( prada );
Lolita.Present( prada );
} catch {
// 激怒ッ!!!
}
な風にしても良いです。
この違いは、例外すると、アリスとロリータのどちらが激怒したか分かりません。
■「激怒ッ!!!」例外の位置を変えるのがベターかなと。
ちと、書いてしまってから思ったのですが、この例外の仕方は状況にそぐわないのです。というのも、例外を発生させているのが Prada クラスの Used メソッド内ってのが変ですよね。
プラダの鞄が「激怒ッ!!!」するのは変です(苦笑)。
というのも、
・アリスは中古のプラダを貰ったら、激怒する。
・ロリータは中古のプラダでも我慢する。
っていうパターンができません。
なので、利用フラグのsetter/getterを用意して(C#ならばプロパティ)にしておいて、アリスやロリータがそれぞれ getter を使って中古かどうかをチェックする(あるいはチェックしない)ってのがベターです。
class Prada {
private:
bool used ;
public:
Prada() {
used = false;
}
void setUsed() {
// 利用設定のみ
used = true;
}
bool getUsed() {
return used ;
}
};
という形にしておいて、
class Alice {
public:
bool Present( Prada *prada ) throw Gekido {
if ( prada->getUsed() == true ) {
throw new Gekido();
}
return true;
}
};
こんな風に、getUsed メソッドでチェックします。
Prada *prada = new Prada();
try {
Alice.Present( prada );
Lolita.Present( prada );
} catch {
// 激怒ッ!!!
}
そうすると、例外を catch できますね。