2010年6月20日日曜日

Chain of Responsibilityパターン

Chain Of Responsibilityパターンは(本では)責任のたらい回しと書かれていて、なんだか悪いイメージがしますが、このパターンは「自分のところで処理できなければ、別の人に処理をお願いする」というパターンです。


今回もコードは単純に書きます(´・ω・`)
概要を先に示すと、
・クライアントはトラブルを対処する人にトラブル情報を渡す。
・トラブルを対処する人はトラブル情報を確認し、
・自分で対処できる→対処する
・自分で対処できない→他の人に対処してもらう
という感じです。トラブル情報は単純にトラブル番号のみを持つとします(本と同じです)


トラブルを対処するSupportクラスは以下のような感じです。
public abstract class Support {
    ・・・
    private Support next;
    ・・・
    public Support(String name) {
         ・・・
    }
    publilc Support setNext(Support next) {
        this.next = next;
        return next;
    }
    public final void support(Trouble trouble) {
        if (resolve(trouble)) {
            // 解決できた時の処理を記述
        } else if (next != null) {
            // 次の人にお願いする。
            next.support(trouble);
        } else {
            // 次の人がいない(誰ひとりとして対処できなかった)時の処理を記述
        }
    }
    protected abstract boolean resolve(Trouble trouble);
}
自分で対処できない場合に次の人にお願いするので、Supportクラスのインスタンスをnextフィールドで持つようにします。

対処する時にはsupportメソッドが呼ばれます。中ではまず、サブクラスで定義されるresolveメソッドを呼びます。resolveメソッドは解決できた時にtrue、解決できなかった時にfalseが返すとします。trueならば、解決できたのでその時の処理を実行し、そうでない場合は次の人(nextに入っているSupportクラスのインスタンス)にお願いします。そして、次の人でsupportメソッドが実行され、解決できた時はその時の処理、解決できなかったときは次の人へ・・・と処理が続きます。もし、解決できないまま、次の人がいなくなってしまった場合はそのトラブルは誰ひとりとして対処できなかったということで、その時の処理を実行します。

Supportクラスのサブクラスはresolveメソッドを実装します。Troubleクラスのインスタンスの中身を参照して、そのメソッドにあった処理を実装します。

Mainクラスでは対処順序を決めておき、出てきたトラブルを対処してもらいましょう(ちなみに、出てきたトラブルをSupportにお願いするのがクライアントの役目です)
・・・
Support support1 = new Support1(・・・);
Support support2 = new Support2(・・・);
Support support3 = new Support3(・・・);
support1.setNext(support2).setNext(support3);// 本当はJavaでメソッドチェーンを書くのはあまりおすすめしない・・・
・・・
support1.support(new Trouble(・・・));
このパターンの良いところは、クライアントはトラブル対処処理を知る必要がなく、1人目にお願いするだけで良いというところです。クライアントとサポートのつながりがゆるいということです。もし、クライアントがトラブル対処処理を知るとなると、密なつながりを持つことになってしまいます。

サポートだけを見ても、無理ならば次の人にお願いするだけなので、サポートメンバーを全員知っておく必要もありません。しかし、たらい回しなのでパフォーマンスが悪くなる可能性があるのがこのパターンの欠点です。もし、パフォーマンスを上げたければ、各サポートメンバーを知っているマネージャーがいて、トラブルの中身によって適切なサポートメンバーにお願いするという形になると思います。「このトラブルについてはこの人に任せよう」と直接お願いするということです。

関連パターン
Compositeパターン」サポートがCompositeパターンで定義される場合あり
「Commandパターン」(勉強中)

Visitorパターン

Visitorとは訪問者のことで、Visitorパターンとはデータ構造の中を訪問して処理を行うパターンです。

プログラムの説明を全部すると莫大な量の文章を書かなくてはならないので今回は割愛サーセン(´・ω・`)


Visitorは、各データを訪問するvisitorメソッドを用意します。以前勉強したCompositeパターンで言うと、Fileクラスを訪ねるvisitorメソッドとDirectoryクラスを訪ねるvisitorメソッドを用意することになります。

public void visit(File file) {
    ・・・
}

public void visit(Directori dir) {
    ・・・
}
ちなみに、訪問時の処理が複数考えられるならば、処理ごとにクラスを切り出します。上記のコードをabstractなVistorクラスとして書いておいて、そのクラスを継承したクラスを作ればいいです。

また、訪問される側(つまり、データ側)は訪問を受け入れなければなりません。ということで、acceptメソッドを用意します。引数は訪問してきた人、つまり、Visitorクラスのインスタンスです。
public void accept(Visitor v) {
    v.accept(this);
}
内部では、訪ねてきた人に処理をお願いします。渡すのは自身です。ということで、(Compositeパターンの例で言うと)Fileだったり、Directoryだったりします。本には書かれていないのですが、もし何かしらの理由で受け入れできない場合はここで例外を出すのが良いと思います。

Mainでは、データを用意してVisitorに訪問してもらいましょう。
・・・
File file = new File("sample", 100);
file.accept(new SampleVisitor());
・・・

このパターンを使う理由としてはデータと処理の分離をするためです。
処理の追加はVisitorクラスを継承したクラスを作ればよいので容易です。しかし、データを拡張した場合は、Visitorクラスにそのデータに訪問するvisitメソッドを(Visitorクラスのサブクラス全て)書かないといけないので、大変です。

関連パターン
Iteratorパターン」データ構造を辿って処理するパターンのもうひとつの例
Compositeパターン」Visitorパターンを適用するデータ構造はCompositeパターンになることがある。
「Interpreterパターン」(勉強中)

2010年6月13日日曜日

Decoratorパターン

Decoratorパターンとは、あるものを土台にしてDecorateする(飾り付ける)ためのパターンです。普通は飾り付けるといいますか、土台に対して何かを付加したい場合はその土台クラスを継承したクラスを実装することが考えられます。しかし、これですと臨機応変に付加したいタイプを変えたり、複数のタイプを付加したい場合に実現できません。「ベースはこれで、これとこれを付加したい、飾りつけたい」と言った場合に威力を発揮するのがこのパターンです。


今回は文字列を#または<>、あるいはその両方をつけてコンソールに表示するサンプルにします。

まず、文字列表示用のベースクラスです。
public abstract class SampleBase {
    public abstract String getValue();
    public final void print() {
        System.out.println(getValue());
    }
}
print()が呼ばれると子クラスで持っている文字列を表示します。もちろん飾りはついていれば飾りも合わせて表示されます。

文字列を持っているクラスは以下の感じです。
public class Sample extends SampleBase {
    private String value;
    public Sample(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}
コンストラクタを介して文字列が渡されます。SampleBaseを継承しているので、getValue()を実装しています。ただ単にこのクラスが管理している文字列を返すだけです。

次に、飾り付け用のベースクラスです。
public abstract class Decor extends SampleBase {
    protected SampleBase sampleBase;
    public Decor(SampleBase sampleBase) {
        this.sampleBase = sampleBase;
    }
}
飾り付けも表示されるのでSampleBaseを継承しているのが特徴です。そして、自身は何に対して飾り付けるのかを知っておく必要がありますので、sampleBaseを持っています。

具体的な飾り付けクラスの一つは以下のとおりです。
public class SharpDecor extends Decor {
    public SharpDecor(SampleBase sampleBase) {
        super(sampleBase);
    }

    public String getValue() {
        return "#" + sampleBase.getValue() + "#";
    }
}
コンストラクタを介して、飾り付けの対象を受け取ります。Sampleが渡されることがあるのは分かると思いますが、Decorを継承したオブジェクトが渡されることもあります。そして、そのDecor継承クラスは飾り付けの対象を知っていて・・・という風に構造が再帰的になります。これがDecoratorパターンの特徴です。ちなみに、SampleBaseもDecorもabstractなので、getValueメソッドはここで実装します。ここでは、飾り付けの対象からgetvalueメソッドで値を取り出し、#で「飾り付け」をして返しています。

他の飾り付けクラスは以下のとおりです。
public class BracketDecor extends Decor {
    public BracketDecor(SampleBase sampleBase) {
        super(sampleBase);
    }

    public String getValue() {
        return "<" + sampleBase.getValue() + ">";
    }
}
Mainは以下のとおりです。
public class Main {
    public static void main(String[] args) {
        SampleBase sample = new Sample("sample");
        sample.print(); // 何も飾りつけずに表示
        SampleBase sample2 = new SharpDecor(sample);
        sample2.print(); // #で飾り付け
        SampleBase sample3 = new BracketDecor(sample);
        sample3.print(); // <>で飾り付け
        SampleBase sample4 = new BracketDecor(sample2);
        sample4.print(); // #で飾りつけたサンプルに<>で飾り付け
    }
}
一番最初にSampleインスタンスを作り、まずは何も飾り付けずに表示します。その後に#で飾り付けた場合、<>で飾り付けた場合を表示し、最後に#で飾り付けたsample2を更に<>で飾り付けるという流れになっています。

実行結果は以下のとおりです。







このパターンを利用することで、機能追加が容易(今回の例でいう飾り付けの種類がいくらでも増やせる)であり、しかも、飾り付けられるもの(今回だとSampleクラス)を変更する必要がありません。文字列を表示すること自体も基本的にはsampleBaseへ移譲しているだけということもあり、クラス間結合(今回だとSampleとDecor、Decor同士)は緩い→動的に機能追加、差し替えができるのも特徴です。

ちなみに、java.ioパッケージはDecoratorパターンで実装されています。

関連パターン
Adapterパターン」中身のインターフェースを変えずに作るのがDecoratorパターンで、異なるインターフェースをつなぐのがAdapterパターン
Strategyパターン」飾りを変えたり重ねたりするのがDecoratorパターンで、アルゴリズムを変えるのがStrategyパターン

Compositeパターン

Compositeとは「合成する」とか「混ぜる」という意味ですが、Compositeパターンはファイルとディレクトリ構造のように容器とその中身を同じものとして扱い、再帰的な構造を作るパターンになります。

ファイルとディレクトリの例でいいますと、

Entryクラス
  • FileクラスとDirectoryクラス共通の親クラス(abstract)
  • FileとDirectory共通のメソッドはここでabstractで定義
  • Entryをaddするaddメソッドも用意するが、使用できるのはDirectoryのみなので、Entryクラス上では「何も実行しない」又は「例外を投げる」にしておく(ちなみに、addメソッドをabstractにすると、Fileクラスでaddメソッドを実装するハメになるので、abstractにしない)
Fileクラス
  • ファイルを表すクラス
  • 管理するのは名前とファイルとファイルサイズくらい?
Directoryクラス
  • ディレクトリを表すクラス
  • 名前とそのディレクトリが保持しているEntryのリストを管理
  • 必ずaddメソッドを実装
です。

ファイル(ディレクトリ)パスを表示したいときは、Entryクラスにabstractなprintメソッドを用意し、各クラスの実装においては、
  • Fileクラス:ファイル名を表示
  • Directoryクラス:自信のディレクトリ名だけでなく、リストから一つずつEntryを取り出し、それのprintメソッドを呼ぶ(Compositeが再帰的なので、このメソッドも再帰的に定義出来る)
と言う風にしておけばいいでしょう。


すみません、サンプルソースは本の丸写しになりそうなのでやめておきます(´・ω・`)
ファイル、ディレクトリ以外でCompositeになるものがあればいいのですが・・・


関連パターン
「Commandパターン」(勉強中)
「Visitorパターン」(勉強中)
Decoratorパターン」中身と容器を同じ扱いにするのがCompositeパターンで、中身と飾りを同じ扱いにするのがDecoratorパターン


Strategyパターン

すみません、仕事で立て込んでいたのでなかなか勉強する暇がありませんでした(´・ω・`)
Strategyパターンとは、様々なアルゴリズムをクラス単位で実装し、実行時に場面に応じて、そのアルゴリズムクラスを切り替えて処理して行くパターンになります。


で、クラス図としては前回のBridgeパターンとあまり変わりません。前回で言うところの実装クラス群に相当するところが今回はアルゴリズムクラス群になります。つまり、1つインターフェースがあり、その実装クラス内でアルゴリズムを実装します。そして、mainになるクラスはそのアルゴリズムクラスのインスタンスを(任意の条件、タイミングで)作り、共通のメソッドを呼べばおkです。

Strategyのインターフェースの例は以下のような感じです。
public interface Strategy {
    public void exec();
}

実装クラスの1つはこんな感じ
public interface AStrategy {
    ・・・
    public AStrategy(・・・) {
        ・・・
    }

    public void exec() {
        // AStrategy特有のアルゴリズムを実装
    }
}

他の実装クラスも(きっと)同じ感じになるでしょう

public interface BStrategy {
    ・・・
    public BStrategy(・・・) {
        ・・・
    }

    public void exec() {
        // BStrategy特有のアルゴリズムを実装
    }
}
mainクラスでは以下のように呼びます。
public static void main(String[] args) {
    Strategy aStrategy = new AStrategy(・・・);
    Strategy bStrategy = new BStrategy(・・・);
    ・・・
    aStrategy.exec();
    bStrategy.exec();
    ・・・
}
あくまでこれは例なので、ある条件の時はaStrategy、そうでないときはbStrategyにしてももちろん構いません。あるいはaStrategy実行中に何かしらの理由でbStrategyに切り替えることもあるかと思います。ここがミソで、このパターンを利用するとアルゴリズムを容易に切り替えられるということなのです。

関連パターン
「Flyweightパターン」(勉強中)
Abstract Factoryパターン」こちらのパターンは工場・部品などをごっそり切り替えるパターン
「Stateパターン」(勉強中)