状態ありはプロトタイプパターンで, 状態なしはファクトリメソッドで実装 (Java)

はじめに

だんだん, タイトルが毎回同じになってきた.

今回はプロトタイプパターンの実装を Java で実施してみた.

Prototype パターン

生成するオブジェクトの原型をコピーして新しいオブジェクトを生成する.

Abstract Factory と似ている.

  • new でオブジェクトを生成すれば Abstract Factory./ Factory Method.
  • clone をつかう場合の Prototype.

複製を作成するためのメソッドを用意する. といういたって単純なもの.

プロトタイプ が複製を担当し, それ以外の生成における操作をクライアントが 担っている.

Map にテンプレートを登録しておいて, 利用するときに複製する. バイナリデータをマップにいれておいて, キーとなる名前をつけて管理する,など.

(実際に仕事では, バイナリのパケットをテンプレートから生成する処理につかった)

メリット

インスタンスのコンストラクタ引数で差分を渡すことで, クラスの数をかなり減らすことができる.

Java には, Clonable インタフェースがある.

利用シーン

  • Abstract Factory パターンでなされるように,

クライアント・アプリケーションにおいて オブジェクトの生成者をサブクラスにすることを回避する

  • 標準的な方法 (例えば’new’) で新しいオブジェクトを作ることによる

固有のコストが所与のアプリケーションにとって高すぎる時にそれを回避する.

サンプルコード

import java.util.HashMap;
import java.util.Map;

public class PrototypeSample {
    public static void main (String args[]) {
        printerFacotry factory = new printerFacotry ();
        Printer printer;

        printer = factory.create ("type a");
        printer.printMessage ();
        printer = factory.create ("type b");
        printer.printMessage ();
        printer = factory.create ("type c");
        printer.printMessage ();
    }
}

class Printer implements Cloneable {
    String str;

    public Printer (String str) {
        this.str = str;
    }

    public void printMessage () {
        System.out.println (str);
    }

    @Override
    public Printer clone () {
        Printer cloned = null;
        try {
            cloned = (Printer) super.clone ();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace ();
        }
        return cloned;
    }
}

class printerFacotry {
    Map<String, Printer> protoMap; 

    public printerFacotry (){
        protoMap = new HashMap<String, Printer>();
        protoMap.put ("type a", new Printer ("a"));
        protoMap.put ("type b", new Printer ("b"));
        protoMap.put ("type c", new Printer ("c"));
    }

    public Printer create (String type) {
        return protoMap.get (type).clone ();
    }
}

状態をもたないならば, プロトタイプは不要

無名クラスをクローンできるのかと思ったが, できなかった. そもそも, 無名クラスは状態を持たないので, クローンする必要がなかった.

この比較から以下のことが分かる.

  • 状態をもつオブジェクトをコピーするのはプロトタイプパターンが有用.
  • 状態をもたないオブジェクトは new で生成する ファクトリメソッドパターンが有用.

クロージャをわたす

Map のなかにクロージャを入れて, 好きな時に取り出すようにした.

これはけっこういいパターンかもしれない. 個人的に気に入った.

import java.util.HashMap;
import java.util.Map;

public class PrototypeSample {
    public static void main (String args[]) {
        printerFacotry factory = new printerFacotry ();
        Printer printer;

        printer = factory.create ("type a");
        printer.printMessage ();
        printer = factory.create ("type b");
        printer.printMessage ();
        printer = factory.create ("type c");
        printer.printMessage ();
    }
}

interface Printer {
    public void printMessage ();
}

class printerFacotry {
    Map<String, Printer> protoMap; 

    public printerFacotry (){
        protoMap = new HashMap<String, Printer>();
        protoMap.put ("type a", new Printer (){
            public void printMessage () { System.out.println ("a"); }
        });
        protoMap.put ("type b", new Printer (){
            public void printMessage () { System.out.println ("b"); }
        });
        protoMap.put ("type c", new Printer (){
            public void printMessage () { System.out.println ("c"); }
        });
    }

    public Printer create (String type) {
        return protoMap.get (type);
    }
}

クロージャ + 引数

さらに改良.

クロージャを Map に保存しておいて, 呼び出し時に外部から引数を与えるようにした.

これで, さらにメソッドが柔軟になった.

interface Printer {
    public void printMessage (String str);

}

class printerFacotry {
    Map<String, Printer> protoMap; 
    public printerFacotry (){
        protoMap = new HashMap<String, Printer>();
        protoMap.put ("type a", new Printer (){
            public void printMessage (String str) { System.out.println ("**"+str+"**"); }
        });
        protoMap.put ("type b", new Printer (){
            public void printMessage (String str) { System.out.println ("++"+str+"++"); }
        });
        protoMap.put ("type c", new Printer (){
            public void printMessage (String str) { System.out.println ("=="+str+"=="); }
        });
    }

    public Printer create (String type) {
        return protoMap.get (type);
    }
}

もはや, プロトタイプパターンの記事ではなくなっているが…