アリスはプラダの中古がお嫌い

プラダの鞄の話の続き。

■プラダの鞄を共有する

いままで、プラダの鞄をポインタとして話してきましたが、クラスとして扱うとちょっと違った形になります。

 Prada *prada = new Prada();
 Alice.Present( prada );

な形で、Present メソッドを使って、アリスにすることを考えてみると、実はプラダの鞄は、ロリータにもプレゼントできることが分かります。

 Prada *prada = new Prada();
 Alice.Present( prada );
 Lolita.Present( prada );

ってな訳ですね。
これは、プラダの鞄をアリスにもロリータにもプレゼントができる、っていう意味でデータを共有します。

20100921_01.jpg

一見、これで十分なような気もしますが、実は微妙なところがあるのです。

■ロリータは中古のプラダなのよ

そうなんです。

 Prada *prada = new Prada();
 Alice.Present( prada );
 Lolita.Present( prada );

こうしたときには、ロリータに渡るプラダは「中古」なんですね。一度、アリスが使っている(というかプレゼントされている)ので、ロリータが貰う prada とアリスが貰う prada とは微妙に違います。

20100921_02.jpg

メモリなので変わらないだろう?という話があるでしょうが、分かり易いように 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

20100922_01.jpg

そうすると、

 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 できますね。

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