目次へ

6. Prototype パターン

  • 2012/04/26 一部修正しました

6.1 Prototype パターンとは

第6章では Prototype パターンを学びます。prototype という単語は、日本語で「試作品」「原型」といった意味を持ちます。Prototype パターンは、あらかじめ用意しておいた「原型」からインスタンスを生成するようにするためのパターンです。

例えば、「直線を描く」機能しか持たない図形エディターを想像してみましょう。この図形エディターで星型を書きたいときには、直線を組み合わせることで、星の形を作成していくことになります。では、星型がいくつも欲しいときはどうすればよいでしょう?直線を組み合わせて星型を描くという作業を何度も繰り返す必要が出てきます。こんなとき、最初に作成した星型(直線の集まり)を「プロトタイプ」として登録しておき、これをコピーすることで星型が作成できれば、作業がとても楽になります。

Prototype パターンは、このように「プロトタイプからインスタンスを生成する」ことができるようにするためのパターンです。

6.2 サンプルケース

クリスマスが近づいてきました。教師であるあなたは、クラスの子供たちの喜ぶ顔が見たくて、教室を飾りつけることにしました。飾り付けるのは雪の結晶の形にきれいに切り抜かれた紙を 100 枚としましょう。あなたは早速制作に取り掛かりましたが、なかなか思うように進みません。雪の結晶の形に切り抜かれた紙を作成する方法は以下のとおりです。

  1. 雪の結晶の形を紙にきれいに描く
  2. 書いた線にあわせて紙を切り抜く

たったこれだけの作業なのですが、実際にきれいに雪の結晶を描くには時間がかかり、さらに何枚も用意するとなると、いくら時間があっても足りません。

この状態をソースコードとして表現してみると、以下のようになるでしょうか。Paper クラスは紙を表すクラスとします。

public class Teacher{
	public Paper[] createManyCrystals(){
		Paper[] papers = new Paper[100];
		for(int n=0; n<papers.length ; n++){
			drawCrystal(papers[n]);  // 時間がかかる
			cutAccordanceWithLine(papers[n]); // 時間がかかる
		}
		return papers;
	}
	private void drawCrystal(Paper paper){
		// きれいに描くため時間がかかる
	}
	private void cutAccordanceWithLine(Paper paper){
		// 描かれた線に沿ってきれいに切るには時間がかかる
	}
}

困りました・・・。drawCrystal という時間のかかるメソッドと、cutAccordanceWithLine という時間のかかるメソッドを繰り返し呼ぶ必要があり、とても時間がかかってしまいます。時間をかけずに同じものをたくさん作りたい。このような時あなたならどうするでしょう?「最初に作成したものと同じものを作ろう」と思いますよね。雪の結晶の紙と新しい紙を重ねて、雪の結晶の形に合わせて切り抜くことを考えるわけです。これは「紙を重ねて切り抜くことにより複製することができる」という性質を利用したものです。プログラム的には、「Paper クラスは Cloneable インタフェースを実装している」と表現できるでしょう。ここで Cloneable インタフェースで定義されている のは、返り値が Cloneable となる createClone メソッドであるとします。Paper クラスでは、この createClone メソッド新しい紙から同じものを作成して返しています。

public interface Cloneable{
	public Cloneable createClone();
}
public class Paper implements Cloneable{
	private String name;
	public Paper(){}
	public Paper(String name){
		this.name = name;
	}
	public Cloneable createClone(){
		Paper newPaper = new Paper();
		newPaper.name = this.name;
		// newPaper と this を重ねて、this の形に切り抜く
		return newPaper;
	}
}

このように、紙が Cloneable であるという特徴を生かして、同じ形の紙を時間をあまりかけずに何枚も複製することが非常に楽に行えます。

Teacher クラスも、以下のように書き直すことができるでしょう。

public class Teacher{
	public Paper[] createManyCrystals(){
		Paper prototype = new Paper("雪の結晶");
		drawCrystal(prototype);
		cutAccordanceWithLine(prototype);
		
		Paper[] papers = new Paper[100];

		for(int n=0; n<papers.length ; n++){
			papers[n] = (Paper)prototype.createClone();
		}
		return papers;
	}
	private void drawCrystal(Paper paper){
		// 描いたり消したりしながら、きれいに描くため時間がかかる
	}
	private void cutAccordanceWithLine(Paper paper){
		// 描かれた線に沿ってきれいに切るには時間がかかる
	}
}

時間のかかる作業は prototype を作成する一度だけとなり、格段に時間が節約されそうです。 クラス図にして見てみましょう。

クラス図1

Prototype パターンとは、複製を作成するためのメソッドを用意する。といういたって単純なものと考えてよいでしょう。この特徴をうまく利用すれば、大きなメリットを得ることができます。例えば、以下のような PrototypeKeeperクラスというものを作成する例をあげてみます。

import java.util.*;

public class PrototypeKeeper{ 
    private Map<String,Cloneable> map; 
    public PrototypeKeeper(){ 
        this.map = new HashMap<String,Cloneable> (); 
    } 
    public void addCloneable(String key,Cloneable prototype){ 
        map.put( key , prototype ); 
    } 
    public Cloneable getClone(String key){ 
        Cloneable prototype = map.get(key); 
        return prototype.createClone(); 
    } 
}

このような Keeper クラスを作成することで、名前を指定してインスタンスを生成することができるようになります。例えば、先ほど作成した、雪の結晶型の紙を「雪の結晶」と言う名前で登録しておけば、

getClone("雪の結晶");

とすることで、雪の結晶の形をした Paper インスタンスを得ることができるのです。同様に、例えば「星」、「もみの木」などの名前で、それぞれの型に切り抜かれた紙を登録しておくことができ、これらに対応する新しいクラスを作成する必要から開放されるのです。例えば、Paper クラスを拡張して、CrystalPaper 、 StarPaper 、TreePaper などのクラスを作成することも可能ですが、同じ Paper でできていて、そのフィールドの値が違うだけのクラスをたくさん作成していては、埒が明きません。

クラス図2

Prototype パターンを利用することで、少しずつ値の違うたくさんのクラスを作成する必要がなくなります。

「Prototype パターンとは」でも触れたように、図形エディタなどでは作成された図形をコピーできるようになっています。この機能を持たなければ、作成した図形と同じ形のものが欲しいときには、再度同じ作成過程をたどる必要があります。作成した図形をプロトタイプとして、作成した過程を繰り返さずとも、同じ図形を得ることができるようにするため Prototype パターンが利用されています。また、あらかじめ「矩形」「円」などが用意されており、「矩形」と指定することで、矩形の図形を簡単に得ることができるようにもなっています。

さらに、PrototypeKeeper に登録できるインスタンスを、CloneAndCuttable インタフェースを実装するものなどとしておくことで、PrototypeKeeper から取り出せるものはすべて CloneAndCuttable なものとして扱うことができるようになります。この特徴を利用することで、もとのクラスを知ることなく、CloneAndCuttable なインスタンスとして扱うことができるようになります。この特徴は FactoryMethod パターンと同様に柔軟な結果を与えることでしょう。

6.3 Prototype パターンまとめ

Prototype パターンの一般的なクラス図は以下のようになります。

Prototype パターンのクラス図
[引用] 『Java言語で学ぶ デザインパターン入門』(結城浩 ソフトバンクパブリッシング株式会社出版 2001年)

↑このページの先頭へ

こちらもチェック!

PR
  • XMLDB.jp