« やる夫がデザインパターンをやるようです 第2回 | メイン | ExtJSのフォームウィジェットとサブミットの方法 »



やる夫がデザインパターンをやるようです 第3回

やる夫がデザインパターンをやるようです 第3回をはてなブックマークに追加 やる夫がデザインパターンをやるようです 第3回をdel.icio.usに追加  Yahoo!ブックマークに登録 やる夫がデザインパターンをやるようです 第3回をGoogle Bookmarksに追加 やる夫がデザインパターンをやるようです 第3回をtwitterにポスト
≪第2回第4回≫

昨日のやる夫で学ぶデザインパターン第2回では、「やる夫はアプリケーションの変更について知る必要がある」とまとめました。今日は、この問題をデザインパターンによる解決へと向かいたいと思います。
<アーキテクト>
punch()メソッドやkick()メソッド、fly()メソッドを共通の振舞いとして処理するヒーロークラスもあるし、固有の振舞いとして処理するヒーロークラスもある。
ところが、walk()や、jump()、crouch()(しゃがむ)というメソッドはどうだ。変更がかかる可能性はほとんど無い。全てのヒーローは共通して歩くしジャンプもすればしゃがむことが出来る。処理も変わらないだろう。
変更があるメソッドと、変更がないであろうメソッドを分けることができるな?

   / ̄ ̄\
 /   _ノ  \
 |    ( ●)(●)
. |     (__人__)
  |     ` ⌒´ノ
.  |         }
.  ヽ        }
   ヽ     ノ
   /    く
   |     \
    |    |ヽ、二⌒)


<やる夫>
       ____
     /⌒  ⌒\ ホジホジ
   /( ●)  (●)\
  /::::::⌒(__人__)⌒::::: \  まぁ、そうすね
  |    mj |ー'´      |
  \  〈__ノ       /
    ノ  ノ 

<アーキテクト>
態度は気に食わんが理解したようだな。
では変更がかかる固有の処理とヒーロークラスでマトリクスにしてみろ。
   / ̄ ̄\
 /   _ノ  \
 |    ( ●)(●)
. |     (__人__)
  |     ` ⌒´ノ
.  |         }
.  ヽ        }
   ヽ     ノ
   /    く
   |     \
    |    |ヽ、二⌒)
やる夫は固有の動きをするpunch() / kick() / fly()処理をヒーローごとにまとめてみました。すると、punch()処理は数種類の処理としてまとめることが出来ました。
punch() : 振る舞いリスト
    "何もしない(パンチ出来ない)"
    "普通のパンチ(ヒーロー共通パンチ)"
    "デビルパンチ"
    "ゴムゴムのパンチ"
    "じゃんけんグー"
    "アンパンチ"
    ・・・
同じようにkick()やfly()をまとめていく内にやる夫はあることに気がつきます。
<やる夫>
これらの処理をインターフェイスの実装クラスとして、ヒーロークラスの外部に持ち出せばいいお。
この発想は到底シロウトにはできないお。天才確定したお
          ____
       / \  /\  キリッ
.     / (ー)  (ー)\
    /   ⌒(__人__)⌒ \
    |      |r┬-|    |
     \     `ー'´   /
    ノ            \
  /´               ヽ
 |    l              \
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒))


<アーキテクト>
        / ̄ ̄\
      /       \
      |::::::        |  天才を除いてはそのとおりだ。
     . |:::::::::::     | 
       |::::::::::::::    |          ....,:::´, .
     .  |::::::::::::::    }          ....:::,,  ..
     .  ヽ::::::::::::::    }         ,):::::::ノ .
        ヽ::::::::::  ノ        (:::::ソ: .
        /:::::::::::: く         ,ふ´..
-―――――|:::::::::::::::: \ -―,――ノ::ノ――
         |:::::::::::::::|ヽ、二⌒)━~~'´ 
やる夫が思いついたのはこういうことです。
  • punch() → interface PunchBehavior
  • kick() → interface KickBehavior
  • fly() → interface FlyBehavior
これらのメソッドをインターフェイスとして定義します。ここまでは第2回で思いついたままですが、インプリメントはヒーロークラスではなく、ヒーロークラスと分離した外部のクラスで行うことにたどり着いたのです。
コードで表現すると以下のようになります。
interface PunchBehavior {
    public abstract void punch();
}
class NormalPunch implements PunchBehavior {
    public void punch(){ System.out.println( "パンチ!" ); }
}
// 他、パンチビヘイビアをインプリメントして振る舞いを作る。
// キックビヘイビア、フライビヘイビアも同様
そして、ヒーロークラスがこのインターフェイスを保持し、パンチの振る舞いや、キックの振る舞いを委譲すればよいわけです。
class Hero {
    // 振舞いを保持する
    protected PunchBehavior mPunchBhv = null;
    
    // ビヘイビアクラスに処理を委譲する
    public void performPunch(){ mPunchBhv.punch(); }
    ・・・・
これで解決できたのは、パンチという処理に変更が入った場合、以前のやる夫の発想では全てのヒーロークラスを見て回って修正しなければなりませんが、今回のこの方法を取るとビヘイビアクラスの修正により各々のヒーローに適用されていきます。
<アーキテクト>
解決できたことは他にもある。setPunch( PunchBehavior punchBhv )というプロパティセッターをヒーロークラスに用意することで、サザエさんでもゴムゴムのパンチが打てるようになる。
たとえば、「"ゴムゴムパンチの薬"というアイテムが登場させるから、サザエさんでもゴムゴムのパンチを打てるようにしろ。」という仕様が企画から降りてきても柔軟に対応できるわけだ。
   / ̄ ̄\
 /   _ノ  \
 |    ( ●)(●)
. |     (__人__)
  |     ` ⌒´ノ
.  |         }
.  ヽ        }
   ヽ     ノ
   /    く
   |     \
    |    |ヽ、二⌒)


<やる夫>
     ____
   /      \ ( ;;;;( (なるほど。よく理解できました)
  /  _ノ  ヽ__\) ;;;;) ウチのアーキテクトの話は長いお。。。
/    (─)  (─ /;;/
|       (__人__) l;;,´
/      ∩ ノ)━・'/
(  \ / _ノ´.|  |
.\  "  /__|  |
  \ /___ 

<アーキテクト>
      / ̄ ̄\ 
    /ノ( _ノ  \    ( )が逆だボケが
    | ⌒(( ●)(●)
    .|     (__人__) /⌒l 
     |     ` ⌒´ノ |`'''| 
    / ⌒ヽ     }  |  |               
   /  へ  \   }__/ /             / ̄ ̄\ 
 / / |      ノ   ノ           / ●)) ((●\’, ・ 
( _ ノ    |      \´       _    (   (_人_)’∴ ),
       |       \_,, -‐ ''"   ̄ ゙̄''―---└'´ ̄`ヽ   て 
       .|                  ______ ノ    ( 
       ヽ           _,, -‐ ''"  ノ       ヽ   r'
         \       , '´        し/..     | J 
          \     (           /      | 
            \    \         し-  '^`-J 
というわけで、以下にサンプルソースを載せておきます。やる夫で学ぶデザインパターンと銘打っている以上これはデザインパターンなのですが、ストラテジーパターンと呼ばれるものです。
正式な定義で記すとなんだか腹に落ちてこない文言なのですが、
Strategyパターンは一連の「アルゴリズム(処理)」を定義します。そしてそれらをカプセル化し、交換可能にします。Strategyパターンにより、このアルゴリズムを使用するクライアントに依存することなく、アルゴリズムそのものを変更することができます
ストラテジーパターンはオブジェクト指向の「カプセル化」の概念を理解するために有効です。ヒーロークラスが知っているのは、パンチを繰り出す方法だけです。そのパンチの処理方法は知る必要がありません。パンチのことに詳しいクラスに処理を委譲するのです。

ドラゴンボールの初期に出てきたポイポイカプセルではありませんが、このカプセルをポイっと投げるだけでバイクが出てきてそれに乗ることができます。どうやってバイクを出したかは知りませんが。
乱暴な言い方をすればそんな感じです。

ということで、やる夫で学ぶデザインパターン。今回はストラテジーパターンでした。他にもたくさんあるのでそのうちポストしてみます。やる夫で学ぶデザインパターンだけ串刺しで見たい人はタグのほうからどうぞ。


<サンプルソース>
import java.util.*;

public class YaruoStrategyPtn {
    public static void main( String[] args ) {
        Hero heroInst = null;
        {
            heroInst = new Sazae();
            heroInst.performeAll();

            // (アイテムをとって)振る舞いを変更
            heroInst.setPunch( new GomugomuPunch() );
            heroInst.performeAll();
        }
    }
};
// ヒーロースーパークラス
class Hero {
    protected String name = "";
    protected PunchBehavior mPunchBhv = null;
    protected KickBehavior mKickBhv = null;
    protected FlyBehavior mFlyBhv = null;

    Hero(){ name = ""; }
    public void walk(){ System.out.println( "歩きます" ); }
    public void jump(){ System.out.println( "飛び跳ねます" ); }
    public void crouch(){ System.out.println( "しゃがみます" ); }
    public void setPunch( PunchBehavior punchBhv ){ mPunchBhv = punchBhv; }
    public void setKick( KickBehavior kickBhv ){ mKickBhv = kickBhv; }
    public void setFly( FlyBehavior flyBhv ){ mFlyBhv = flyBhv; }
    public void performPunch(){ mPunchBhv.punch(); }
    public void performKick(){ mKickBhv.kick(); }
    public void performFly(){ mFlyBhv.fly(); }
    public void display(){ System.out.println( name ); }
    public void performeAll(){
        display();
        walk();
        jump();
        crouch();
        mPunchBhv.punch();
        mKickBhv.kick();
        mFlyBhv.fly();
        System.out.println( "パフォーマンス終了\n" );
    }
}
class Sazae extends Hero {
    Sazae(){ 
        name = "サザエでございます"; 
        mPunchBhv = new NormalPunch();
        mKickBhv = new NormalKick();
        mFlyBhv = new FlyNoWay();
    }
}
class Devilman extends Hero {
    Devilman(){
        name = "デッビール!"; 
        mPunchBhv = new DevilPunch();
        mKickBhv = new DevilKick();
        mFlyBhv = new FlyWithWing();
    }
}
class Rufie extends Hero {
    Rufie(){
        name = "海賊王になるっ";
        mPunchBhv = new GomugomuPunch();
        mKickBhv = new NormalKick();
        mFlyBhv = new FlyNoWay();
    }
}
// ------------------------
// interface PunchBehavior
// ------------------------
interface PunchBehavior {
    public abstract void punch();
}
class NormalPunch implements PunchBehavior {
    public void punch(){ System.out.println( "パンチパンチシュッシュッ" ); }
}
class PunchNoWay implements PunchBehavior {
    public void punch(){ System.out.println( "パンチなんて。。。できません" ); }
}
class GomugomuPunch implements PunchBehavior {
    public void punch(){ System.out.println( "ゴムゴムのーぱぁーんち" ); }
}
class DevilPunch implements PunchBehavior {
    public void punch(){ System.out.println( "デッビールパーンチ" ); }
}
// ------------------------
// interface KickBehavior
// ------------------------
interface KickBehavior {
    public abstract void kick();
}
class NormalKick implements KickBehavior {
    public void kick(){ System.out.println( "キックキックシュッシュッ" ); }
}
class KickNoWay implements KickBehavior {
    public void kick(){ System.out.println( "蹴るなんて。。。できません" ); }
}
class DevilKick implements KickBehavior {
    public void kick(){ System.out.println( "デッビールキーック" ); }
}
// ------------------------
// interface FlyBehavior
// ------------------------
interface FlyBehavior {
    public abstract void fly();
}
class FlyWithWing implements FlyBehavior {
    public void fly(){ System.out.println( "デッビールウィーング" ); }
}
class FlyWithEngine implements FlyBehavior {
    public void fly(){ System.out.println( "エンジンで飛びますゴゴゴゴ" ); }
}
class FlyNoWay implements FlyBehavior {
    public void fly(){ System.out.println( "飛ぶなんて。。。できません" ); }
}

★このコンテンツに目的の情報はありませんでしたか?


[ 最近のエントリーとその関連エントリー ]


[ スポンサードリンク ]

トラックバック

このエントリーのトラックバックURL:
http://mojalog.com/cgi/mt/mt-tb.cgi/242

コメントを投稿

ツリータイプ・カテゴリー

open all | close all

リファラから検索


サイト内検索