2010年5月23日日曜日

Bridgeパターン

Bridgeパターンとは、実装クラス層と機能クラス層を橋渡しするためのパターンです。

実装クラス層と言うのは、abstractなクラスやinterfaceとそれらを継承、実装したクラス群のことをいいます。機能クラス層は1つのベースクラスと、メソッド等を追加するためにベースクラスを継承したクラスが複数個含まれるクラス群のことです。

この2つの層を結ぶ橋を用意することで、クラスの拡張を容易にすることができます。

(そろそろクラス図がないと説明しづらくなってきましたが、お付き合いください)

今回の例は単純に、引数でもらった文字列を加工して表示するだけです(毎度おなじみですが)
まずは、機能クラス層のベースクラスDisplaySampleです。
public class DisplaySample {
    private DisplayImpl impl;

    public DisplaySample(DisplayImpl impl) {
        this.impl = impl;
    }

    public void print() {
        impl.print();
    }
}

フィールドとして実装クラス層のオブジェクトを持ちます。これが「橋」となります。メソッドとしてはprint()のみです。これを呼ぶと、実装クラス層で定義されているprint()を呼び出します。

このベースクラスを継承したクラス(機能追加したクラス)は以下のとおりです。単純に、printTwice()を追加しただけです。これを呼ぶと、実装クラス層のprint()を2回呼びます。
public class ExtendedDisplaySample extends DisplaySample {
    public ExtendedDisplaySample(DisplayImpl impl) {
        super(impl);
    }

    public void printTwice() {
        System.out.print("1回め:");
        print();
        System.out.print("2回め:");
        print();
    }
}

次に、実装クラス層です。実装クラス層では、インターフェースとその実装クラスがあるわけですが、インターフェースとしては以下の通りです。
public interface DisplayImpl {
    public void print();
}

実装クラスは今回は2つ用意しました。1つは以下のように引数の文字列にアスタリスクをつけて返すだけのprint()を実装したクラスです。
public class AsteriskDisplayImpl implements DisplayImpl {
    private String value;

    public AsteriskDisplayImpl(String value) {
        this.value = value;
    }

    public void print() {
        System.out.println("*** " + value + " ***");
    }
}

もう一つは文字列の大文字小文字を変換して表示するprint()を実装したクラスです。
public class UpperLowerDisplayImpl implements DisplayImpl {
    private String value;

    public UpperLowerDisplayImpl(String value) {
        this.value = value;
    }

    public void print() {
        // valueをchar配列に変換
        char[] valueChars = value.toCharArray();

        // 大文字小文字を変換した後の文字を格納するビルダー
        StringBuilder sb = new StringBuilder();

        // 1文字ずつ、大文字か小文字かを判別し、ビルダーに変換後の文字を
        // 格納していく
        for (char ch : valueChars) {
            if (Character.isLowerCase(ch)) {
                sb.append(Character.toUpperCase(ch));
            } else {
                sb.append(Character.toLowerCase(ch));
            }
         }

         // toString()を呼び出して文字列として表示
         System.out.println(sb.toString());
     }
}
よくよく考えると、わざわざchar配列を用意しなくても、ループ中にvalue.charAt()を使えばいいよねorz

Mainは以下のとおりです。
public class Main {
    public static void main(String[] args) {
        DisplaySample dis1 = new DisplaySample(new AsteriskDisplayImpl("asterisk"));
        DisplaySample dis2 = new DisplaySample(new UpperLowerDisplayImpl("upperLOWER"));
        ExtendedDisplaySample ext1 = new ExtendedDisplaySample(new AsteriskDisplayImpl("twice / asterisk"));
        ExtendedDisplaySample ext2 = new ExtendedDisplaySample(new UpperLowerDisplayImpl("twice / uPpErLoWeR"));

        dis1.print();
        dis2.print();
        ext1.printTwice();
        ext2.printTwice();
    }
}
Mainでは、全部で4パターン用意してます。DisplaySampleとAsterisk、DisplaySampleとUpperLower、ExtendedとAsaterisk、ExtendedとupperLowerです。実行結果は以下のとおりです。








このパターンを使うと、機能拡張をしたい場合はExtendedDisplaySample(場合によってはDisplaySampleを再び継承?)を継承したり、DisplayImplの実装クラスを増やせば良いだけですね。

関連パターン
Abstract Factoryパターン」interface実装クラスにこのパターンを用いることがある
Template Methodパターン」実装クラス層で利用
Adapterパターン」異なるクラス同士を結びつけるためのパターン。これも橋渡し役と言えそう

Abstract Factoryパターン

前に紹介したパターンでは部品を抽象化して、マネージャーとかファクトリーに当たるところはせいぜいインターフェースで定義したAPIのみを使うという形でしたが、今度はファクトリーも抽象化します。ちなみに、ファクトリーが抽象的なら、そのファクトリーが扱うパーツも抽象的です。

※部品というより製品って言った方がいいかもな・・・

まず、抽象的なPartは以下のような感じです。
public abstract class Part {
    protected String name;

    public Part(String name) {
        this.name = name;
    }

    public abstract void doSomething();
}

nameフィールドがprotectedになっていますが、これは子クラスでもnameフィールドにアクセス出来るようにするためです。
そのPartを扱うFactoryは以下のようにします。
public abstract class Factory {
    public abstract Part createPart(String name);
}

もし、種類の違うPartを用意したければ、Partクラスとは違うクラスを作成し、Factoryクラスの中にもう一つcreateメソッドを用意すればいいです(*1)

抽象的なクラスがあれば、具体的なクラスもあります(ようは、抽象的なクラスを継承したクラスを作るのです)。具体的なクラスは例として2種類用意しました。

まず、1つ目のABCPartは以下の感じです。doSomething()はとりあえずはnameを表示するだけにしました。
import factory.Part;

public class ABCPart extends Part {
    public ABCPart(String name) {
        super(name);
    }

    public void doSomething() {
        System.out.println("ABCPart - name: " + name);
    }
}
ABCPartを扱うABCFactoryは以下の通りです。
import factory.Factory;
import factory.Part;

public class ABCFactory extends Factory {
    public Part createPart (String name) {
        return new ABCPart(name);
    }
}
2つ目はDEFPartです。ソースは以下の通りです。こちらのdoSomething()もnameを表示するだけですが、表示形式を変えています。
import factory.Part;

public class DEFPart extends Part {
    public DEFPart(String name) {
        super(name);
    }

    public void doSomething() {
        System.out.println("DEFPartです。名前は " + name + " です。");
    }
}
DEFPartを扱うDEFFactoryは以下の通りです。
import factory.Factory;
import factory.Part;

public class DEFFactory extends Factory {
    public Part createPart (String name) {
        return new DEFPart(name);
    }
}
Factoryを扱うMainは以下の通りです。
import factory.Factory;
import factory.Part;
import abc_factory.ABCFactory;
import def_factory.DEFFactory;

public class Main {
    private static final String ABC_FACTORY = "ABCFactory";
    private static final String DEF_FACTORY = "DEFFactory";

    public static void main(String[] args) {
 // TODO argsの長さが0だったり、2以上の場合があるので、その時はSystem.exit(0)を呼んで終了させる。

        Factory factory = createFactory(args[0]);

        Part part = factory.createPart("sample");
        part.doSomething();
    }

    private static Factory createFactory(String factoryName) {
        if (ABC_FACTORY.equals(factoryName)) {
            return new ABCFactory();
        } else if (DEF_FACTORY.equals(factoryName)) {
            return new DEFFactory();
        } else {
            return null;// TODO こうすると呼び出し側でnullチェックをしないといけないので望ましくない。
        }
    }
}
ABCFactoryとDEFFactoryの2種類があるので、引数で選択します。ですが、実際にFactoryやPartに処理をお願いするときにはどちらのFactory、Partを使っているのか気にする必要はありません。前回やったパターンと同じで、共通のAPIを利用するだけです。

ということで、今回はFactoryも抽象化してみました。前回同様、具体的なことは知らなくても処理をすることができます。

ちなみに、Mainクラスは具体的なクラスをimport文を利用して知っちゃっています。Mainクラスも具体的なクラスを知らないようにする方法は以下のとおりです。
public static Factory createFactory(String factoryName) {
    Factory factory = null;
    try {
        factory = (Factory)Class.forName(factoryName).newInstance();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
        return factory;
    }
}
これはクラス名を引数にとり、ClassクラスのforName()で、指定したクラスを動的に読み込みます。読み込んだクラスのインスタンス化はnewInstance()です。こうすることで、import abc_factory.ABCFactoryと書いたり、new ABCFactory();と書く必要がなくなります。言い換えると、MainはABCFactory()などを知っておく必要はないということです。

関連パターン
Builderパターン」どちらも複雑なインスタンスを生成するパターン
Factory Methodパターン」FactoryクラスのcreateのところをFactory Methodパターンにすることがある。
Compositeパターン」ファクトリーで作られる部品をComposite(つまり、部品の中に更なる部品)になる場合がある。
Singletonパターン」ファクトリーをいくつ作っても仕方がないので、それをSingletonパターンにすることがある。


*1 なので、Factory Methodパターンを利用するのが良いかと

2010年5月16日日曜日

Builderパターン

Builderパターンとは、文字通りビルド(組み立て、建築、構築)のためのパターンです。このパターンに出てくる人物は実際に建物を作る時と同様に、建築者、監督者、依頼人です。要件によって作るものは異なりますから、実際には上記に加えて、要件に特化した建築者もいます(本では具体的建築者なんて言っていますが)


まず、建築者はインスタンスを作るためのインターフェースを決めます。Builderクラスは以下のような感じです。
public abstract class Builder {
    public abstract void makeEntrance();
    public abstract void makeRoom(String name);
    ・・・
}

上のクラスを継承したクラス(ようは要件に特化した建築者)は以下のようにします。他の要件の場合でも同様に継承して作ればよいでしょう。
public class ABCBuilder extends Builder {
    public void makeEntrance() {
        ・・・
    }

    public void makeRoom(String name) {
        ・・・
    }
}

監督者であるDirectorクラスは、Builderクラスで宣言されているメソッドを使用して、実際に建築の指示をします。
public class Director {
    private Builder builder;
    public Director (Builder builder) {
        this.builder = builder;
    }

    public void construct() {
        builder.makeEntrance();
        builder.makeRoom("staff room");
        ・・・
    }
}
コンストラクタの引数がBuilderクラスのオブジェクトになっています。ここに、特化した建築者を入れてあげます。ソースを見てのとおり、監督者はBuilderクラスのメソッドしか呼ばないため、具体的にどんな建築者がやってきたのかを監督者であるDirectorは知りません。ですが、監督者は別にそんなことを意識しなくても必要な操作、つまり、Builderクラスで定義したメソッドを呼ぶことができちゃいます。

Mainクラスでは、要件に応じてABCBuilderクラスのインスタンス又はDEFBuilderクラスのインスタンスを生成し、Directorに構築の依頼をします。
public static void main(String[] args) {
    ・・・
    Director director = null;
    if ("ABC".equals(args[0])) {
        ABCBuilder abc = new ABCBuilder();
        director = new Director(abc);
    } else {
        DEFBuilder def = new DEFBuilder();
        director = new Director(def);
    }
    director.construct();
    ・・・
}

ここでのポイントは、依頼者であるMainクラスは、ABCとDEFがどのように構築されるかは知りません。ただ単に監督者に構築をお願いしただけです。そして、先程も説明しましたが、監督者はどんな建築者がやってこようと必要な指示をするだけで済んでいます。このパターンは各クラス間の独立性が高いことと、拡張性がある(別の用件に特化した建築者を作っても容易に対応出来る)のが特徴です。ですが、(他のパターンでも言えることですが)クラスの役割はしっかり理解しましょう。DirectorがABCBuilderのメソッドを呼ぶなんて以ての外です。

関連パターン
TemplateMethodパターン」今回のパターンでは、「DirectorがBuilderをコントロールする」パターンだったが、Template Methodパターンでは、「スーパークラスがサブクラスをコントロールする」パターン
Compositeパターン」作るものがCompositeになる場合がある。
Abstract Factoryパターン」どちらも複雑なインスタンスを生成するためのパターン
Facadeパターン」Facadeパターンは複数の処理をまとめて一つのメソッド(窓口)として実装し、外部からそのメソッドを呼ぶだけでよいパターン。BuilderパターンもBuilderが行う複数の処理をDirectorがまとめているという点で似ている。

Prototypeパターン

Prototypeパターンとは、インスタンスをnewで生成するのではなく、既にあるインスタンスからコピーをして新しいインスタンスを作るパターンです。このパターンを使う場面としては、似たようなクラスを大量に作りたくない場合や、インスタンス生成が難しい場合(*1)、F/W(*2)とインスタンス生成を分けたい場合などです。

コード例としては、複数のパーツを用意してそれを利用するという形にしています。パーツをコピーできるようにするためのPartインターフェースと、それを利用するManagerクラスをF/Wで用意します。パーツの雛形(まさしく、プロトタイプ)はPartインターフェースを実装すればよいです。実際にサンプルコードを動かしたいときは、PartインターフェースとManagerクラスのソースをframeworkフォルダに入れ、それ以外はframeworkフォルダの外においてください。

Partインターフェースは単純に以下のような感じです。Partはコピーできるので、copy()を用意しています。copy()を実際に実装するときはjava.lang.Objectのclone()を呼ぶので、cloneableインターフェースを継承しています。

package framework;
import java.lang.Cloneable;

public interface Part extends Cloneable {
    void doSomething();
    Part copy();
}

ManagerクラスはPartインターフェースを介してインスタンスのコピーを行うクラスです。register()はPartの登録、copyPart()はPartをコピーして、それを取得します。

package framework;
import java.util.Map;
import java.util.HashMap;

public class Manager {
    private Map<String, Part> showcase = new HashMap<String, Part>();
    public void register(String name, Part part) {
        showcase.put(name, part);
    }
    public Part copyPart(String name) {
        Part p = (Part)showcase.get(name);
        return p.copy();
    }
}


Partインターフェースを実装するクラスでは、doSomething()はそのパーツにあった処理(パーツの名前を決めたり、読み込むファイルを指定したり、フラグを立てたり・・・なんでも構わない)を定義すればよく、copy()はただ単純にjava.lang.Objectのclone()を呼べばいいです。(*3) 

Main(PartとManagerを利用するクラス)では以下のような感じです。最初はもちろんPartインスタンスを生成して登録しておく必要がありますが、その後はManagerさんにcopyPart()をお願いして、コピーを頂くだけです。
import framework.Part;
import framework.Manager;

public class Main {
    public static void main(String[] args) {
        // 準備
        Manager manager = new Manager();
        SamplePart part1 = new SamplePart("part1");
        SamplePart2 part2 = new SamplePart2("part2");
        manager.register("abc", part1);
        manager.register("def", part2);

        // 生成
        Part p1 = manager.copyPart("abc");
        p1.doSomething();
        Part p2 = manager.copyPart("def");
        p2.doSomething();
    }
}


register()に入れる名前は分かりやすいものがいいでしょう。コピーをお願いするときはその名前を指定します。例が今ひとつ分かりづらいですが、雛形としてのPartはManagerに登録されているので、わざわざパーツを一つずつ自分で作って(コーディングして)、そのクラスをnewする必要がなくなります。単純に言えば再利用性が高いということです。

また、ManagerクラスはPartを扱うクラスではあるものの、実際のPart(例で言うと、SamplePartやSamplePart2)については知らなくて構わないので、パーツとマネージャの依存関係が低いと言えます。

関連パターン
「Flyweightパターン」(勉強中)
「Mementoパターン」(勉強中)
Compositeパターン」Compositeで複雑な構造のインスタンスを作る場合にはprototypeを利用すると良いかもしれない(と本には書かれている)
Decoratorパターン」Decoratorで複雑な構造のインスタンスを作る場合にはprototypeを利用すると良いかもしれない(と本には書かれている)
「Commandパターン」(勉強中)

*1 例としては、グラフィックエディタなどでユーザがマウス操作によって作り上げた図形を再び作りたい場合。プログラミングでこれを作るのは大変なので、ユーザ操作によって作られた図形インスタンスをあらかじめ保存しておいて、それを作りたいときにコピーする(と本には書かれている)
*2 F/W frameworkの略称
*3 本当はclone()の実装についていろいろ説明したいが、本筋から離れるので割愛。気になる人は「Effective Java」のp.54にある「項目11 cloneを注意してオーバーライドする」を参照されたし

2010年5月9日日曜日

Singletonパターン

Singletonパターンとは、システム内でとあるクラスのインスタンスを「たった1つ」しか作らない(作りたくない)ようにするためのパターンです。このパターンを実現することで、指定したクラスのインスタンスが『絶対に』1つしか無いことを保証します。

Singletonにするクラスは以下のようになります。
public class SingletonSample {
    private static SingletonSample instance = new SingletonSample();

    /**
     * デフォルトコンストラクタ
     * 外部から使用されないようにprivateにしている
     */
    private SingletonSample() {}

    public static SingletonSample getInstance() {
        return instance;
    }
}
このクラスのように、Singletonを実現する場合は
  • 自身のインスタンスを持つprivateフィールドを用意する。
  • デフォルトコンストラクタにprivateをつける。
  • インスタンスを取得するメソッドを用意する。
となります。

Mainクラス(SingletonSampleクラスを利用するクラス)では以下のようにgetInstance()を呼べば良いです。
SingletonSample obj1 = SingletonSample.getInstance();

ちなみに、以下のようにMainクラスでSingletonSampleインスタンスを作るコードを入れると、
SingletonSample obj1 = new SingletonSample();




上の通り、コンパイルエラーになります。デフォルトコンストラクタがprivateで外部から利用できないようにしているので当たり前ですね。

また、getInstance()を何度呼んでも同じインスタンスなのかをチェックしてみます。以下のように、getInstance()を2回呼んで、それぞれの変数(obj1とobj2)は同じインスタンスを指しているのかをチェックします。(オブジェクト型を==で比較した場合、それは同一のインスタンスかどうか、正確に言うと、そのインスタンスが置かれているメモリ上の番地が同じかをチェックします。番地が同じならば同じインスタンスを指しているということになり、trueと判定されます)

SingletonSample obj1 = SingletonSample.getInstance();
    SingletonSample obj2 = SingletonSample.getInstance();
    if (obj1 == obj2) {
        System.out.println("same");
    } else {
        System.out.println("different");
    }
実行結果は以下の通りです。



Singletonパターンの悪い実装例も挙げておきます。
    private static SingletonSample instance;

    /**
     * デフォルトコンストラクタ
     * 外部から使用されないようにprivateにしている
     */
    private SingletonSample() {}

    public static SingletonSample getInstance() {
        if (instance == null) {
            instance = new SingletonSample();
        }
        return instance;
    }

上記では、SingletonSampleのインスタンスをフィールドで生成するのではなく、getInstance()が呼ばれた際にnullであるかどうかを判定して、もしnullならばその時に生成する方法を取っています。がしかし、複数の異なるスレッドから「ほぼ同時に」getInstance()を呼び出した場合、SingletonSampleのインスタンスが複数生成される可能性があります。

Javaでは、クラスに初めてアクセスした際にそのクラスの初期化が行われる(当然、その初期化は最初の1回のみ)ので、フィールドでインスタンスを生成するようにしておけば、複数生成されることは無いということになります。

注意:悪い実装例で、getInstance()にsynchronizedをつける方法も考えられるが、同期化コストが高いので推奨しません(と、wikipediaに書かれている)。スレッドのことをよく知らないので、そのコストがどれだけ高いのか分かりません(´・ω・`)

関連パターン
以下のパターンはインスタンスが1つである場合が多い

2010年5月2日日曜日

Factory Methodパターン

Factory Methodパターンとは、インスタンス生成を前回のTemplate Methodパターンで構成したパターンになります。インスタンス生成を行う(まさしく工場と言える)Factoryクラスと生成したものを表すProductクラスをフレームワークとして用意し、具体的な処理はそれぞれを継承して定義します。

例えば、工場では商品を作り、ある処理を行うというフローがある時には
public abstract class Factory {
    /**
     * 商品を作る.
     * @param name 商品名
     */
    public final Product create(String name) {
        Product p = createProduct(name);
        doSomething(p);
        return p;
    }
    
    protected abstract Product createProduct(String name);
    protected abstract void doSomething(Product p);
}

のようにしておきます。具体的な処理はこのクラスを継承して、
public class SampleFactory extends Factory {
    protected Product createProduct(name){
        return new SampleProduct(name);
    }
    protected void doSomething(Product p) {
        ・・・
    }
}

とします。Mainとなるクラス(Factoryを呼ぶクラス)は以下のような処理があれば良いです。
Factory factory = new SampleFactory();
Product product = factory.create("sample");

SampleProductクラスはProductクラスを継承したクラス(具体的な商品を表すクラス)です。

こうすることで、どんな商品が登場しても(Factory、Productクラスを継承したクラスがどんなに増えても)「商品を作って、ある処理を行う」というフローが変わりません。また、フレームワーク側は上記のようなSampleFactoryクラスやSampleProductクラスに依存しません。言い換えると、新商品が登場してもフレームワークを修正する必要がないということです。

関連パターン
「Template Methodパターン」Factory MethodパターンはTemplate Methodパターンの応用例
Singletonパターン」FactoryクラスはSingletonの場合が多い。
Compositeパターン」製品がcomposite(製品の中にある製品が含まれる)になる場合がある。
「Iteratorパターン」Iteratorインスタンス作成にはFactory Methodパターンが使われることがある。

Template Methodパターン

Template Methodパターンとは、文字通り、テンプレートとなるメソッドを用意して利用するパターンです。具体的には、内部に抽象メソッド(Javaの場合、abstractをつけたメソッド)をいくつか呼び出しているテンプレートメソッドを用意し、、抽象メソッドの具体的な処理をサブクラスで決めます。もちろん、サブクラスによって抽象メソッドの処理内容は違う可能性があるわけですが、どのように実装しても大きな流れはテンプレートメソッドで組み立てた通りになります。

例えば、テンプレートのあるクラスは
public abstract class AbstractSample {
    protected abstract void start();
    protected abstract void finish();
    public final void execute() {
        start();
        System.out.println("execute");
        finish();
    }
}

になります。execute()がテンプレートメソッドです。start()とfinishi()はサブクラスで以下のように定義します。
public class Sample extends AbstractSample {
    protected void start() {
        System.out.println("--- Start ---");
    }

    protected void finish() {
        System.out.println("--- Stop ---");
    }
}

別のサブクラスでは以下のようにしてみます。
public class AnotherSample extends AbstractSample {
    protected void start() {
        System.out.println("*** スタート ***");
    }

    protected void finish() {
        System.out.println("*** 終了 ***");
    }
}

Mainクラスは以下のようにしてみました。
public class Main {
    public static void main(String[] args) {
        AbstractSample sample = new Sample();
        sample.execute();
        AbstractSample anotherSample = new AnotherSample();
        anotherSample.execute();
    }
}

実行結果はこんな感じです。








このようにすることで、

  • サブクラスをいくつ作っても、大きな流れはテンプレートメソッドの通り(ロジック共通化)
  • サブクラスをいくつ作っても、呼び出すメソッドは同じ名前のメソッド

といった点が挙げられます。具体的な処理はサブクラスに決めてもらわないといけないとは言え、抽象クラスで処理を形作れることが、抽象クラスの存在意義と言えるでしょう。

注意:本に付属のサンプルコードで、Linux向けのものは文字コードがEUC_JPになっているため、コンパイル時に場合によっては警告が出るかもしれません。その時は、コンパイル時にencodingオプションを入れて

%> javac -encoding EUC_JP *.java

とすること。

関連パターン
「Factory Methodパターン」 Template Methodパターンをインスタンス生成に応用した例
Strategyパターン」Template Methodパターンでは継承を利用してプログラム動作を変更するのに対し、Strategyパターンでは移譲を利用してプログラム動作を変更する。