Factory Methodパターンとは

factoryは工場のことです。
インスタンスを次々作り出す工場のイメージでしょうか。

以前にTemplate Methodパターンについて説明しましたが、それのコンストラクタ版といったところです。
chiopino.hatenablog.com
メインプログラムではインスタンスを直接呼び出さないので、型に柔軟なプログラムを書くことができます。

パターンの例

新入社員に社員証を発行し、出勤、退勤を管理するシステムを作ります。
例ではフレームワークとなる2つの抽象クラス、それを実装した2クラス、テスト用の1クラスを作ります。

Productインタフェース

public interface Product {

	void on();

	void off();
}

社員証の元となるインタフェースです。
二つのメソッドが宣言されていますが、これらは「何らかの機能を実装する」ためのメソッドです。
社員証なら「出勤」「退勤」を実装できますし、
pcなら「電源オン」「電源オフ」を実装できます。

このon,offメソッドはFactoryMethodパターンからすると必要のないものです。
極端に言ってしまえば製品を意味するProductインタフェースさえあれば、メソッドは何もなくてもFactoryMethodを名乗れます。
ただし実用性があるかは別の話です。


Factoryクラス

public abstract class Factory {

	public abstract Product createProduct(String name) ;
}

createProductメソッドはProduct(製品)を生産するための抽象メソッドです。


実装例

これら2つのクラス、インタフェースを実装して、実際の社員証と管理システムを作っていきます。


EmployeelIDcardクラス

public class EmployeelIDcard implements Product {

	private String name ;

	public EmployeelIDcard(String name) {
		this.name = name;
		System.out.println(name + "が入社しました");
	}

	public void on() {
		System.out.println(name + "が出勤しました");
	}

	public void off() {
		System.out.println(name + "が退勤しました");
	}
}

コンストラクタでは持ち主の名前を社員証に登録しています。
登録が完了したら新入社員の名前と、入社したことをアナウンスします。

onメソッドはProductインタフェースで宣言したメソッドです。
ここでは出勤という意味を持たせて実装しました。
本来であればAdapterパターンでメソッド名をarrival(出社)のように変えたいところですが、FactoryMethodパターンの説明には不要であるためそのままonメソッドとします。

offメソッドはonメソッドと同様の実装となっています。


EmployeelIDSystemクラス

public class EmployeelIDSystem extends Factory {

	@Override
	public Product createProduct(String name) {
		return new EmployeelIDcard(name);
	}
}

社員管理システムです。
管理システムとは言ってもここでは社員証の発行しかしていません。

createProductメソッドはFactoryクラスで宣言されていた抽象メソッドでした。
EmployeelIDcardクラスのインスタンスを生成し、それを返すだけのメソッドです。


テスト用クラス

public class Main {

	public Main() {
		Factory factory = new EmployeelIDSystem();
		Product a_san = factory.createProduct("安藤");
		Product b_san = factory.createProduct("馬場");
		Product c_san = factory.createProduct("千代田");
		Product d_san = factory.createProduct("大和");

		System.out.println("----------------");
		a_san.on();
		b_san.on();
		c_san.on();
		d_san.on();
		System.out.println("----------------");
		a_san.off();
		b_san.off();
		c_san.off();
		d_san.off();
	}

	public static void main(String[] args) {
		new Main();
	}
}

4人の新入社員にそれぞれの社員証を発行しています。
createProductメソッドは、渡された名前でProductクラスのスーパークラスインスタンスを生成して返すものでした。
変数a_san,b_san,c_san,d_sanにはそれぞれの社員証が保持されました。

それぞれの社員証をon,offメソッドを呼び出しています。
onメソッドは出勤、offメソッドは退勤でした。

実行してみます。

安藤が入社しました
馬場が入社しました
千代田が入社しました
大和が入社しました
----------------
安藤が出勤しました
馬場が出勤しました
千代田が出勤しました
大和が出勤しました
----------------
安藤が退勤しました
馬場が退勤しました
千代田が退勤しました
大和が退勤しました

使用する利点

createProductの実装で、社員証を全てコレクションに追加することで全社員を管理することができたりします。
コンストラクタ呼び出しとセットにしたい処理がある場合に力を発揮するパターンです。

また、createProductで呼び出しているコンストラクタをGuestIDcardなどに差し替えることで、簡単に来客用IDカード用のシステムに変更したりもできます。

まとめ

FactoryクラスやProductインタフェースがFactoryMethodパターンの本体であり、EmployeelIDcardクラスやEmployeelIDcardSystemクラスはあくまで実装例に過ぎません。

FactoryクラスやProductインタフェースだけ見ると、社員証システムのプログラムには到底見えません。
なぜなら社員証システム以外でも十分使用できるように設計しているからです。
テレビやスマホ、車などでも実装次第です。

デザインパターンはプログラムの部品化、そのやり方であることを忘れないようにしましょう。

デザインパターン Chain of Responsibilityについて勉強した

使用言語はJavaです。

Chain of Responsibilityパターンとは

役割の似た複数のクラスがあってどれかに処理を割り振りたいが、どれに割り振ればいいのかわからない、というような時に使用します。
あるクラスAに処理を渡し、そのクラスAが適していなければクラスAはクラスBに処理を渡します。
クラスBも適していなければクラスCに処理を渡します。
そうやって次へ次へと処理を渡してやがて適したクラスにたどり着き、処理が実行されます。
この次へ次へと処理を渡していくのは日本語ではたらい回しと言えば適切かもしれません。

パターンの例

たくさんの業種の人が集まっている場面を例にします。
色々な仕事が依頼されてきますが、誰に依頼すればいいのかわかりません。
そこで、とりあえずある一人に依頼をし、その人ができない仕事なら別の人に伝えてもらうようにします。

Workerインタフェース

public abstract class Worker {

	public static final String PROGRAMMING = "programming";
	public static final String DRIVING = "driving";
	public static final String ILLSUTRATION = "illustration";
	public static final String TEACHER = "teacher";

	private String name ;
	private Worker next ;

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

	public Worker setNext(Worker worker) {
		this.next = worker;
		return worker;
	}

	public boolean support(String work) {
		if(canSupport(work)) {
			done(work);
			return true;
		}else if(next!=null) {
			return next.support(work);
		}else {
			impossible(work);
			return false;
		}
	}

	public abstract boolean canSupport(String work);

	public void done(String work) {
		System.out.println(work + " を "+name + " が対応しました");
	}

	public void impossible(String work) {
		System.out.println(work + " を対応できる人がいません");
	}
}

Workerクラスは全業種をひっくるめた「ここで働いている人」の抽象クラスです。

クラスフィールド4つは仮にここで受けている仕事内容だとしています。
プログラマはPROGRAMMINGの仕事ができ、ドライバーはDRIVINGの仕事ができると言った具合です。
Chain of Responsibilityパターンに必要な定数ではありません。

nameフィールドはそれぞれの名前です。
実行結果で、誰が処理したかをわかりやすくするためのものなので、これもパターンに必要なフィールドではありません。

nextフィールドは、このWorkerができない仕事を受け取った場合にたらい回しする先の人です。

コードの順番が前後しますが、先にcanSupportメソッドの説明をします。
canSupportメソッドは、受け取った仕事ができる場合はtrueを返すようにサブクラスで実装するための抽象メソッドです。
例えばプログラマはPROGRAMMINGの仕事を受け取った場合はtrueを、DRIVINGなどを受け取った場合はfalseを返します。

doneメソッドは受け取った仕事ができる場合に実行されるメソッドです。
このコードでは誰が何の仕事をしたのかがわかるようにコンソール出力をしています。
プログラマならプログラミングの仕事をするというように業種ごとに処理は違うので、実際は抽象メソッドにしてサブクラスに実装させるのが良いですが、今回はコンソール出力という共通の処理であるため、Workerクラス内で実装しています。

impossibleメソッドはこの仕事が誰もできなかった場合に呼び出されるメソッドです。
誰もできなかったことがコンソール出力されます。

supportメソッドは仕事を受け取るためのメソッドです。
渡されたWorkerのcanSupportメソッドがtrueであれば仕事をし、falseであり次のたらい回し先がいればそちらに渡します。
たらい回し先がいない、つまり自分が最後である場合はimpossibleメソッドを呼んでいます。

実装例

プログラマ

public class Programmer extends Worker {

	public Programmer(String name) {
		super(name);
	}

	@Override
	public boolean canSupport(String work) {
		return work==PROGRAMMING;
	}

}

実装はcanSupportメソッドのみです。
プログラマはPROGRAMMINGの仕事が渡されたらtrueを返すようになっています。


ドライバー

public class Driver extends Worker {

	public Driver(String name) {
		super(name);
	}

	@Override
	public boolean canSupport(String work) {
		return work==DRIVING;
	}

}

ドライバーはDRIVINGの仕事が渡されたらtrueを返すようになっています。


イラストレータ

public class Illustrator extends Worker{

	public Illustrator(String name) {
		super(name);
	}

	@Override
	public boolean canSupport(String work) {
		return work==ILLSUTRATION;
	}

}

イラストレータはILLSUTRATIONの仕事が渡されたらtrueを返すようになっています。


テスト用クラス

public class Main {

	public Main() {
		Worker driver = new Driver("ドライバー");
		Worker illustrator = new Illustrator("イラストレータ");
		Worker programmer = new Programmer("プログラマ");
		
		driver.setNext(illustrator).setNext(programmer);
		
		driver.support(Worker.DRIVING);
		driver.support(Worker.PROGRAMMING);
		driver.support(Worker.ILLSUTRATION);
		driver.support(Worker.TEACHER);
	}
	
	public static void main(String[] args) {
		new Main();
	}
}

初めにドライバー、イラストレータプログラマの各インスタンスを生成しています。

続いて各職種で自分以降のたらい回し先を設定しています。
ドライバーはイラストレータへ、イラストレータプログラマへとたらい回しをします。

最後にたらい回しの先頭であるドライバーに色々な仕事を渡しています。

実行してみましょう。

driving を ドライバー が対応しました
programming を プログラマ が対応しました
illustration を イラストレータ が対応しました
teacher を対応できる人がいません

それぞれがそれぞれのできる仕事をしているのがわかります。
ドライバーがプログラムを書いたり、イラストレータが運転などはしていません。
唯一teacherの仕事は「対応できる人がいません」と言われています。
canSupportメソッドでteacherにtrueを返す人がいなかったためです。

パターンを使用する利点

supportメソッドやdoneメソッドをみるとわかるように、Workerたちは誰がどの仕事をするかは意識していません。
またたらい回しした場合も、誰が仕事をしたのか、または誰もできなかったのかも知りません。
ただ自分ができる仕事かどうかだけを気にしています。
このように自分の仕事だけに集中できるのがChain of Responsibilityパターンの特徴です。

まとめ

このWorkerがダメなら次、ダメならその次、その次というように連続的に伝わっていくところからchainという名前になっているようです。

最初からMainプログラムなどでこの仕事はどのWorkerに渡せばいいかを考えて割り振ることもできますが、できると思って渡した仕事が実はできなかったなんてことも考えられますし、Workerが増えれば増えるほどその条件は複雑になります。
であれば初めから誰かに渡して、たらい回ししたほうが割り振り条件を作る必要もなくなりシンプルになります。

ただし、割り振り条件を作るのに比べると、たらい回しは低速になります。
速度をとるか、シンプルをとるかです。

デザインパターン Prototypeパターンについて勉強した

使用言語はjavaです。

Prototypeパターンとは

コンストラクタ呼び出しによるインスタンス生成は一般的ですが、中にはコンストラクタ呼び出しでは求めているインスタンスが生成できない場合もあります。
例えば、すでに情報が入力されたインスタンスとそっくりな、しかし参照は別のインスタンスを作りたい場合です。
このような時は雛形となるインスタンスコピー機にかけるように複製できると便利です。
Prototypeパターンはそのようなインスタンスの複製を作る時のパターンになります。

Prototypeパターンの例

タイトル、著者、記事からなる書類を例に考えてみます。

public class Document implements Cloneable {

	private String title ;
	private String author ;
	private String article ;

	public void setTitle(String title) {
		this.title = title;
	}

	public String getTitle() {
		return title;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getAuthor() {
		return author;
	}

	public void setArticle(String article) {
		this.article = article;
	}

	public String getArticle() {
		return article;
	}

	public Document clone() {
		try {
			return (Document) super.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println("複製に失敗しました");
			e.printStackTrace();
		}
		return null;
	}

	public String toString() {
		return String.join(
				"\n",
				"タイトル\t:"+getTitle(),
				"著者名\t:"+getAuthor(),
				"記事内容\t:"+getArticle());
	}
}

フィールドから見ていきます。
titleフィールドはタイトル文字列を保持する変数。
authorフィールドは著者名を保持する変数。
articleフィールドは記事内容を保持する変数です。

その下に並んでいるのは各フィールドのセッター、ゲッターです。
単純なので、個別の詳細は省きます。

cloneメソッドは、今回の中心となるメソッドです。
tryブロックの中ではスーパークラスのcloneメソッドをキャストして返しているだけです。

ここでcatchしようとしているCloneNotSupportedExceptionは、Cloneableインタフェースを実装していない場合に発生する例外です。
クラス宣言を見ると、確かにCloneableインタフェースが実装されています。
このインタフェースが実装されてさえいれば発生しない例外なので、例外処理も特別なことはしなくて大丈夫です。

実はcloneメソッドはオーバライドしなくても問題はありません。
問題はありませんが、Objectクラスのcloneメソッドのデフォルトの戻り値がObject型であるため、オーバライドしないと呼び出し元で毎回キャストしなければいけない手間が発生します。
あちこちでキャストするよりもcloneメソッドをオーバライドする方がコード全体の見通しが良くなります。

ちなみに、Cloneableインタフェースには宣言しているメソッドはありません。
マーカーインターフェースと言って、この例で言えば「このクラスではcloneメソッドによる複製を許可しますよ」という意味があります。

toStringメソッドはこのクラスの情報を文字列に変換するものです。
printlnメソッドに渡すと内部的にtoStringメソッドを呼び出して文字列表示してくれるので、デバッグ時に重宝します。
Prototypeパターンとしては必要ありません。

実装例

テスト用クラス

import java.util.Scanner;

public class Main {

	public Main() {
		Scanner scanner = new Scanner(System.in);
		Document prototype = new Document();
		prototype.setTitle("デフォルトタイトル");
		prototype.setAuthor("名無し");
		prototype.setArticle("記事内容");

		Document document1 = prototype.clone();
		Document document2 = prototype.clone();
		Document document3 = document2;

		System.out.print("記事1のタイトルを入力:");
		document1.setTitle(scanner.nextLine());
		System.out.println("----------------");
		System.out.println(document1);
		System.out.println();
		System.out.println(document2);
		System.out.println();
		System.out.println(document3);

		System.out.print("記事2の著者を入力:");
		document2.setAuthor(scanner.nextLine());
		System.out.println("----------------");
		System.out.println(document1);
		System.out.println();
		System.out.println(document2);
		System.out.println();
		System.out.println(document3);

		System.out.print("記事3の記事内容を入力:");
		document3.setArticle(scanner.nextLine());
		System.out.println("----------------");
		System.out.println(document1);
		System.out.println();
		System.out.println(document2);
		System.out.println();
		System.out.println(document3);

		scanner.close();
	}

	public static void main(String[] args) {
		new Main();
	}
}

Scannerクラスはコンソールからの入力を受け付けるクラスです。

次の4行ではDocumentクラスをインスタンス化し、タイトル、著者名、記事のデフォルトを入力しています。
これが実際のアプリケーションだと全て手入力されている場合やファイルから読み込んでいる場合などが考えられます。

次の3行ではdocument変数3つにprototype変数のインスタンスをコピーしています。
document1変数とdocument2変数はcloneメソッドでコピーしていますが、
document3だけはdocument2から参照渡しでコピーされていることを覚えておいてください。

次の8行ではdocument1に対してタイトルを変更し、3記事を全て表示して比較しています。
変更内容はscanner.nextLineメソッドから取得します。
コンソールに文字を入力する必要があります。

次の8行ではdocument2に対して著者を変更しています。

次の8行ではdocument3に対して記事の内容を変更しています。

実行してみます。
まず最初に記事1にタイトルを入力します。

----------------
記事1のタイトルを入力:デザインパターン
タイトル	:デザインパターン
著者名	:名無し
記事内容	:記事内容

タイトル	:デフォルトタイトル
著者名	:名無し
記事内容	:記事内容

タイトル	:デフォルトタイトル
著者名	:名無し
記事内容	:記事内容

記事1のタイトルは入力した通り、「デザインパターン」となっていますが、
それ以外はコピー元の値を引き継いでいます。
別々のインスタンスに複製されていますね。

次に記事2に著者名を入力します。

----------------
記事2の著者を入力:菊池
タイトル	:デザインパターン
著者名	:名無し
記事内容	:記事内容

タイトル	:デフォルトタイトル
著者名	:菊池
記事内容	:記事内容

タイトル	:デフォルトタイトル
著者名	:菊池
記事内容	:記事内容

記事2の著者名は「菊池」となりました。
記事1と比較すると、こちらは著者名はデフォルトとなっています。
しかし記事3を見てみると、著者名は「菊池」になってしまっています。
記事3(document3変数)の作成方法を思い出してください。
これはdocument2から参照渡しでコピーしていました。
ということはdocument2とdocument3は同じオブジェクトとなっています。

最後に記事3に記事内容を入力します。

----------------
記事3の記事内容を入力:prototypeパターンについて
タイトル	:デザインパターン
著者名	:名無し
記事内容	:記事内容

タイトル	:デフォルトタイトル
著者名	:菊池
記事内容	:prototypeパターンについて

タイトル	:デフォルトタイトル
著者名	:菊池
記事内容	:prototypeパターンについて

ここまでくると想像通り、記事3への入力は記事2へも反映されており、記事1はインスタンスが別なので反映されていません。

使用する利点

インスタンスのデフォルト状態を外部からのデータで決定したい、さらにそのインスタンスが複数必要である場合に有効なパターンです。

まとめ

cloneメソッドを呼び出すと、元のインスタンスで保持しているフィールドを自動で複製してくれます。
Documentクラスでも、titleフィールドやauthorフィールドをコピーするような文はどこにもないのに新しいインスタンスには当然のように保持されています。
これが100個、1000個のフィールドを持ったクラスであっても同じように自動でコピーしてくれます。

ただし、フィールド個々のコピーには参照渡し(シャローコピー)が行われています。
例えばフィールドに配列が含まれていた場合、インスタンス1の配列の要素を上書きしたらインスタンス2の配列の要素まで変更されてしまいます。
配列の要素まで別のインスタンスとしたいのであれば、cloneメソッド内で配列を一つずつコピーしていくことになります。

デザインパターン Proxyについて勉強した

使用言語は相変わらずJavaです。

Proxyパターンとは

Proxyとは代理という意味です。
代理というか、僕としては窓口と言った方があっているのではないかと感じています。(窓口のイメージを持ったFacadeパターンもあるので、Proxyを窓口と呼ぶのもまた微妙な気はしますが)

例えばマネージャとプログラマの関係で考えてみます。
マネージャはお客さんと挨拶をしたり、打ち合わせをしたりします。そして品物を要求されたら品物を作ってお客さんに提出します。
しかし、この品物を作る実際の作業(コーディング)はマネージャはできません。覚えればできるかもしれませんが、あっちこっちで仕事をすることになり、忙しくなってしまいます。
そこで、品物を要求されたらプログラマを呼び出してその仕事を委譲します。

このように簡単な仕事はProxyとなるマネージャが行い、実際の専門的であったり時間のかかるような仕事は委譲先であるプログラマに任せてしまうのがProxyパターンです。

Proxyパターンの例

ここでは2つのクラスと1つのインタフェースで設計します。

Workerインタフェース

public interface Worker {

	void greeting();

	void meeting();

	void work();
}

それぞれが今回の仕事に必要な作業です。
greeting(挨拶)メソッドは、お客さんとの挨拶をします。
meeting(打ち合わせ)メソッドは、これから行う仕事の打ち合わせをします。
work(仕事)メソッドは、実際の仕事をします。ここではコーディングをする予定です。


Managerクラス

public class Manager implements Worker{

	Worker worker ;
	
	public Manager() {
		System.out.println("+--------");
		System.out.println("|マネージャです。初めまして");
		System.out.println("+--------");
	}

	@Override
	public void greeting() {
		System.out.println("マネージャです。おはようございます");
	}

	@Override
	public void meeting() {
		System.out.println("マネージャです。打ち合わせします");
	}

	@Override
	public void work() {
		if(worker==null) {
			worker = new Programmer();
		}
		worker.work();
	}
}

このクラスはWorkerインタフェースを実装しているので、実実装のメソッドを実装する必要があります。
コンストラクタでは、コンストラクタを呼び出したタイミングがわかるように「初めまして」とテキストを出力しています。

greetingメソッドでは、マネージャであることを名乗り、挨拶をしています。

meetingメソッドでは、マネージャであることを名乗り、打ち合わせをしています。

workメソッドだけは他と少し違います。
マネージャはコーディングができないのでプログラマに任せたいと思っていますが、
初めて仕事を委譲するときは、workerフィールドはnullです。(プログラマはプロジェクトに参加していません)
ですので、workerフィールドがnullである場合のみProgrammerのインスタンスを生成しています。
インスタンスを生成したら、workerフィールドに格納されているプログラマに仕事を委譲しています。


Programmerクラス

public class Programmer implements Worker {

	public Programmer() {
		System.out.println("+--------");
		System.out.println("|プログラマです。初めまして");
		System.out.println("|(1分後)");
		System.out.println("+--------");
	}

	@Override
	public void greeting() {
		System.out.println("プログラマです。おはようございます");
	}

	@Override
	public void meeting() {
		System.out.println("プログラマです。打ち合わせします");
	}

	@Override
	public void work() {
		System.out.println("プログラマです。コーディングします");
	}
}

このクラスもWorkerを実装しています。
コンストラクタ、greetingメソッド、meetingメソッドはManagerクラスとほぼ同じです。
違うのは、マネージャではなくプログラマと名乗っているところだけです。

workメソッドもマネージャと違っています。
プログラマはコーディングができるので、誰かに委譲せず素直に仕事をしています。

実装例

Mainクラス

public class Main {

	public Main() {
		Worker worker = new Manager();
		worker.greeting();
		worker.meeting();
		worker.work();
	}

	public static void main(String[] args) {
		new Main();
	}
}

Managerクラスをインスタンス化して各仕事をさせています。

実行してみましょう。

+--------
|マネージャです。初めまして
+--------
マネージャです。おはようございます
マネージャです。打ち合わせします
+--------
|プログラマです。初めまして
|(1分後)
+--------
プログラマです。コーディングします

挨拶と打ち合わせはマネージャがしていますが、コーディングをするところでプログラマが出てきました。
マネージャができる仕事は自分でしていますが、自分ができないコーディングはプログラマに任せていることが確認できます。

使用する利点

コーディングの準備に数秒かかるなどの場合、使うかわからないような機能のためにアプリケーション立ち上げ時にまとめて準備していると、ユーザとしては無駄に待たされているようでストレスになります。
これがDesignerクラスなどのようにいくつもあるなら尚更です。
であれば、実際に使う直前に準備を開始することで待ち時間を分散させることができるようになります。

Proxyパターンのキモは委譲しているところではなく、初めて委譲する直前に対象のインスタンスを生成しているところにあります。

まとめ

Proxyパターンの考え方としては実はWorkerインタフェースは不要です。
上でも説明したように、「初めて委譲する直前に対象のインスタンスを生成」することがProxyパターンの特徴だからです。
ではなぜWorkerインタフェースを作成したかというと、これもクラスの再利用性を考えたものになります。
例えばMainクラスのManagerコンストラクタ呼び出しをProgrammerコンストラクタ呼び出しに変更しても、このプログラムは難無く動作します。

public class Main {

	public Main() {
		Worker worker = new Programmer();
		worker.greeting();
		worker.meeting();
		worker.work();
	}

	public static void main(String[] args) {
		new Main();
	}
}
+--------
|プログラマです。初めまして
|(1分後)
+--------
プログラマです。おはようございます
プログラマです。打ち合わせします
プログラマです。コーディングします

全て自分でこなせるたくましいプログラマになってしまいましたが、、
もし打ち合わせまでしてコーディングしない場合があると、最初に待っていた1分間は完全に無駄なものになってしまいます。

繰り返しになりますが、Proxyパターンは長い待ち時間を分散させ、不要になるかもしれない待ち時間を無くすための考え方となります。

デザインパターン TemplateMethodについて勉強した

使用言語はJavaです。

Template Methodパターンとは

処理の流れは決まっているけど、具体的な処理の内容は決まっていないような場合に使用されるパターンです。

Template Methodパターンの例

働く人をイメージしてみます。
業種はいろいろありますが、それらをまとめて抽象的なものとしてWorker(働く人)クラスとして定義します。

public abstract class Worker {

	public void work() {
		System.out.println("出勤");
		am();
		System.out.println("昼ごはん");
		pm();
		System.out.println("退勤");
	}

	public abstract void am();

	public abstract void pm();
}

amメソッドはam(午前)の仕事内容を定義するための抽象メソッドとします。
なぜ抽象かというと、仕事内容は業種によって異なるからです。
プログラマならデスクワークでしょうし、トラックドライバーなら運転をします。
それらはWorkerクラスを継承した各サブクラスに実装させて、このクラスではメソッドが存在することだけわかればいいことにします。

pmメソッドはpm(午後)の仕事内容を定義するための抽象メソッドです。
役割はamメソッドと同じです。

workメソッドは抽象メソッドではありません。
このメソッドを呼び出すことで、1日の仕事があるべき順に実行されていくようになっています。
amメソッド、pmメソッドが組み込まれていますが、これらはサブクラスで処理を実装されることで、各業種として機能するようになります。


Programmerクラス

public class Programmer extends Worker {

	@Override
	public void am() {
		System.out.println("打ち合わせ");
	}

	@Override
	public void pm() {
		System.out.println("コーディング");
	}
}

Workerクラスが継承されています。
これによりam,pmメソッドの実装が強制されるので、実装忘れをすることはありません。
amメソッドでは"打ち合わせ"を行なっています。
pmメソッドでは"コーディング"を行なっています。


Driverクラス

public class Driver extends Worker {

	@Override
	public void am() {
		System.out.println("運転");
	}

	@Override
	public void pm() {
		System.out.println("運転");
	}

	public void loading() {
		System.out.println("積み込み");
	}
}

こちらもWorkerクラスを継承しています。
Programmerクラスとは各メソッドの実装内容が変わっており、am,pmメソッドとも"運転"をするようになっています。
またrefuelingメソッドが新たに実装されています。refuelingは給油という意味です。


テスト用クラス

ublic class Main {

	public Main() {
		System.out.println("--------");
		Worker driver = new Driver();
		driver.work();
		//driver.loading();

		System.out.println("--------");
		Worker programmer = new Programmer();
		programmer.work();
	}

	public static void main(String[] args) {
		new Main();
	}
}

Worker型のdriver変数にはDriverクラスのインスタンスが代入されます。
ここでDriver型ではなくWorker型の変数としているのはクラスを部品として扱いやすいようにしているためです。
仮にこのnew Driver()をnewProgrammer()や新たに作ったnew TaxiDriver()に変更する場合もコンストラクタ呼び出しの1箇所だけ変更すればいいことになります。

もしも変数の型がDriverであり、コードのどこかにloadingメソッドが使用されていた場合を考えてみてください。
その上でdriver変数をTaxiDriver型に変更したいとなった場合、loadingメソッドを削除して、ちゃんと動くかを確認する必要が生じます。

これがWorker型変数であるだけでloadingメソッドの使用を禁止し、結果としてloadingメソッドの削除や変更の確認をしなくても良くなるわけです。

実行

実行してみましょう。

--------
出勤
運転
昼ごはん
運転
退勤
--------
出勤
打ち合わせ
昼ごはん
コーディング
退勤

ProgrammerクラスやDriverクラスにはam,pmメソッドを実装するだけで1日の仕事の流れが作成できました。
TaxiDriverクラスやSportsmanクラスを新たに作りたくなった場合も簡単に対応できるようになります。

また、全ての業種で出勤の後に「朝礼」を追加したくなった場合にはWorkerクラスのworkメソッドをちょっと変更するだけで済みます。

	public void work() {
		System.out.println("出勤");
		System.out.println("朝礼");    //追加
		am();
		System.out.println("昼ごはん");
		pm();
		System.out.println("退勤");
	}

まとめ

冒頭で「処理の流れは決まっているけど、具体的な処理の内容は決まっていないような場合」と言いましたが、「処理の流れ」はworkメソッド、「具体的な処理の内容」はam,pm抽象メソッドのことです。

Template Methodパターンを使用しない場合、ProgrammerクラスとDriverクラスは同じworkメソッドを持つことになります。
クラスが2つであればまだいいですが、10や20も業種別にクラスがあると、workメソッド内のたった1行を変更しようとなった時にも大変な作業になってしまいます。
また複数の業種がランダムに並べられた配列を一つ一つworkメソッドを呼ぶことになった場合でも、各業種ごとにコードを書かなければならなくなります。

for(Worker worker : list){
	worker.work();
}

また、Template Methodパターンの欠点として、具体的な処理の内容を追加するのが難しい点があります。

public abstract class Worker {

	public void work() {
		System.out.println("出勤");
		System.out.println("朝礼");
		am();
		System.out.println("昼ごはん");
		pm();
		System.out.println("退勤");
		overtime();                //追加
	}

	public abstract void am();

	public abstract void pm();
	
	public abstract void overtime();        //追加
}

新たにovertime(残業)抽象メソッドが追加されました。
この場合はWorkerクラスを継承した全てのサブクラスに変更が発生するため、サブクラス数によっては大変な変更になってしまいます。

デザインパターン Singletonについて勉強した

Iteratorパターンに引き続き、Singletonパターンについて理解したことの再確認になります。
chiopino.hatenablog.com

使用言語はJavaです。

Singletonパターンとは

通常インスタンスを複数作成するとそれぞれのインスタンスが別々の参照先を持ち、別々の値を持っています。
つまり、下記のインスタンスは同一性比較(==比較)では不一致となります。

File a = new File("aaaa");
File b = new File("aaaa");
System.out.println(a==b);    //false

インスタンスとはそういうものですが、時には同じクラスのインスタンスが複数あっては困るような場合もあります。
そのような時に使用するのがSingletonパターンです。

Singletonパターンの例

Singletonパターン自体は1つのクラスで構成できます。

public class Person {

	private static Person singleton = new Person();

	private Person() {
	}

	public static Person getInstance() {
		return singleton;
	}
}

コンストラクタを呼ばれると、呼ばれた分だけインスタンスが生成されてしまいます。
そこで外部からの呼び出しを禁止するためにprivateを付与します。

ではどうやって1つ目のインスタンスを生成するかというと、singletonフィールドの初期化でコンストラクタを呼んでいます。
外部からのコンストラクタ呼び出しを禁止し、クラスフィールドで一つだけインスタンスを生成することで、このアプリケーションでは起動から終了まで、このインスタンスしか使用できないことになります。

getInstanceメソッドを呼ぶことでsingletonフィールドを返します。

singletonフィールドをprivateでなくしてやれば、publicにしてやればgetInstanceメソッドは不要になるのではないかと思いますが、そうはいきません。
privateにしてしまうと、外部からの代入も可能になってしまい、nullで上書きされてしまう可能性があるからです。

使用例

public class Main {

	public Main() {
		Person a_san = Person.getInstance();
		Person b_san = Person.getInstance();
		Person c_san = a_san.getInstance();
		Person d_san = new Person();
		Person e_san = (Person)a_san.clone();

		System.out.println("aさん = bさん" + (a_san == b_san ? "です" : "ではありません"));
		System.out.println("aさん = cさん" + (a_san == c_san ? "です" : "ではありません"));
		System.out.println("aさん = dさん" + (a_san == d_san ? "です" : "ではありません"));
		System.out.println("aさん = eさん" + (a_san == e_san ? "です" : "ではありません"));

	}

	public static void main(String[] args) {
		new Main();
	}
}

a_sanとb_sanはクラスメソッドであるgetInstanceメソッドによって初期化します。

c_sanはa_sanのインスタンスからgetInstanceを呼び出しています。
間違いではありませんが、インスタンスからクラスメソッドを呼び出しているということで警告が出ているかと思います。

d_sanはPersonクラスのコンストラクタを呼び出そうとしていますが、コンストラクタはprivateにしているので、コンパイルエラーが発生します。
コンストラクタを呼べないので、複数のインスタンスを生成されることを防止できています。

e_sanはa_sanのインスタンスからcloneメソッドを呼び出そうとしています。
cloneメソッドの詳細については説明を省きますが、簡単に言えばあるインスタンスと同じ値を持った別のインスタンスを複製する手段です。
cloneメソッドを使用するにはCloneableインタフェースを実装するなど準備が必要ですが、Personクラスはそのような準備をしていないので、こちらもコンパイルエラーが発生します。


複数のインスタンスを生成する手段がないことが確認できたところで、コンパイルできるようにコードを修正します。

public class Main {

	public Main() {
		Person a_san = Person.getInstance();
		Person b_san = Person.getInstance();
		Person c_san = a_san.getInstance();
		Person d_san = null;//new Person();           //修正
		Person e_san = null;//(Person)a_san.clone();  //修正

		System.out.println("aさん = bさん" + (a_san == b_san ? "です" : "ではありません"));
		System.out.println("aさん = cさん" + (a_san == c_san ? "です" : "ではありません"));
		System.out.println("aさん = dさん" + (a_san == d_san ? "です" : "ではありません"));
		System.out.println("aさん = eさん" + (a_san == e_san ? "です" : "ではありません"));

	}

	public static void main(String[] args) {
		new Main();
	}
}

手っ取り早くd_sanとe_sanをnullにしてしまいました。
c_sanは警告は出ていますが、コンパイルは可能なので残すことにします。

では実行してみます。

aさん = bさんです
aさん = cさんです
aさん = dさんではありません
aさん = eさんではありません

aさんとbさん、それにcさんは同一のインスタンスであることがわかります。
またdさんとeさんはnullを代入してしまったので不一致になっています。

まとめ

Singletonパターンはあるクラスのインスタンスを一つしか生成できないように制限を課す方法です。
もちろん最初からnew Person()を一度しか呼ばないように注意することもできますが、うっかり二度呼んでしまってもそれに気づくのはおそらくバグが起こってからでしょう。
もしかしたら1年後、忘れた頃になんてことも考えられます。
そんなことに神経をすり減らすくらいならコンパイラにチェックさせる方が楽なものです。

デザインパターン Iteratorについて勉強した

長年趣味プログラマーをやっているつもりでしたが、振り返ってみると代表的な創作物は何一つないことに気づきまして。
実は初心者レベルを抜け出せていないのではないかと、デザインパターンjava入門書を買って読んでいました。
ちなみに今まで入門書も読んだことがなかった(全てネット情報でなんとかやってた)ので、まさに型なし状態でした。

本を1冊読んだ程度の理解なので、間違いなどあるかと思います。


デザインパターンはあくまでプログラムの書き方、考え方です。
今回使用した言語はJavaですが、Javaにしか通用しないというわけではありません。

今回使用した言語

言語:Java

iteratorとは

配列などのようなデータの集合から一つずつデータを取り出す時のデザインパターンです。

for(Object obj : list){
}

このような拡張forでは内部的にiteratorパターンを使用しています。

iteratorパターンの例

iteratorパターン自体は下記2つのインタフェースから構成します。

(1) Aggregate インタフェース

public interface Aggregate {
	Iterator iterator();
}

データの集合に実装します。
iteratorメソッドを呼び出すことで、データ集合からデータを順に取り出すためのクラスを作成します。

(2) Iteratorインタフェース

public interface Iterator {
	boolean hasNext();
	Object next();
}

データを順に取り出すためのクラスに実装します。
hasNextメソッドは、データ集合内にまだ取り出していないデータが残っている場合にtrueを返します。
nextメソッドは、呼び出すごとに集合内のデータを順に返していきます。

実装例

これだけだとイメージが掴めないと思うので、実装例をみてみましょう。
例では並べられたトランプを一枚ずつ順に読み上げていくことにします。

Aggregate実装

public class CardsDeck implements Aggregate {

	private String[] cards ;

	public CardsDeck(String... cards) {
		this.cards = cards.clone();
	}

	public int length() {
		return cards.length;
	}

	public String getCard(int i) {
		return 0<=i && i<cards.length ? cards[i] : null;
	}

	@Override
	public Iterator iterator() {
		return new CardsDeckIterator(this);
	}
}

Aggregateインタフェースを実装したデータの集合です。
トランプが数枚重ねられている状態を想像してください。

cardsフィールドは実際のデータ集合、トランプの山札です。

コンストラクタでは引数で山札を受け取っています。
引数をcloneで複製していますが、外部から勝手に変更されないようにするためのもので、今回は重要ではありません。

lengthメソッドはトランプの枚数を返します。

getCardメソッドは指定された位置のトランプを返します。
ただし、引数に0未満orトランプの枚数以上の数字が渡された場合はnullを返します。

最後のiteratorメソッドはAggregateインタフェースで宣言されていたメソッドです。
後述するCardsDeckIteratorクラスに自身のインスタンスを渡してインスタンス化したものを返します。

Iterator実装

public class CardsDeckIterator implements Iterator{

	int index = 0;
	CardsDeck deck ;

	public CardsDeckIterator(CardsDeck deck) {
		this.deck = deck;
	}

	@Override
	public boolean hasNext() {
		return index<deck.length();
	}

	@Override
	public String next() {
		return deck.getCard(index++);
	}
}

Iteratorインタフェースを実装した、データ取り出しのクラスです。
トランプの前に立ってこれからトランプを読み上げる人といったところでしょうか。

indexフィールドは今見ているトランプの番号です。
0なら先頭、1ならその次、2,3,4と順に移動していきます。

deckフィールドはトランプの山札です。

コンストラクタでは、引数で山札(CardsDeck)を受け取っています。
これはAggregateインタフェースを実装したCardsDeckクラスのiteratorメソッド内から呼び出されていたものです。

hasNextメソッドではindexフィールドが山札の枚数未満であれば、まだ読み上げるトランプがあるということでtrueを返します。

nextメソッドでは山札からindexフィールド番号のカードを取り出して返しています。
indexが後置きインクリメントになっていることに注目してください。
これによって例えば、indexが0の時はnextメソッドに0を渡してその実行後にindexフィールドをインクリメントすることになります。
これを前置きインクリメントにしてしまうと、最初のカードが読み上げられないというバグが発生してしまいます。

テスト用クラス

public class Main {

	public Main() {
		Aggregate aggregate = new CardsDeck(
				"diamond-1",
				"clover-1",
				"heart-1",
				"spade-1",
				"joker",
				"diamond-2",
				"clover-3",
				"heart-5",
				"spade-7",
				"diamond-J"
				);

		Iterator iterator = aggregate.iterator();

		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
	}

	public static void main(String[] args) {
		new Main();
	}
}

実際に山札を作ってトランプを読み上げてみましょう。
CardsDeckクラスのインスタンスを作成してAggregate型変数に代入します。
Aggregate型変数に代入するということは、Aggregateインタフェースで宣言されたiteratorメソッドしか呼べない(呼ぶ必要がない)ということです。
CardsDeckコンストラクタの引数は可変長のString配列でしたので、トランプを表すテキストをいくつか渡してやります。

次にiteratorメソッドを呼び出し、その戻り値をIterator型変数iteratorに代入しています。
ここでもCardsDeckIteratorのインスタンスが代入されている変数はIterator型であるため、Iteratorインタフェースで宣言したメソッドしか使用しないことになります。
こうすることで、コードが可能な限りCardsDeckクラスやCardsDeckIteratorクラスに依存しなくて済むようになります。

最後にwhileで実際にトランプの読み上げを行なっています。
whileの条件はhasNextメソッド、つまりもう読み上げるとランプがなくなるまで繰り返します。
whileブロック内ではnextメソッドを読んで、トランプを一枚ずつ読み上げています。

コードを実行してみるとこのような結果になるはずです。

diamond-1
clover-1
heart-1
spade-1
joker
diamond-2
clover-3
heart-5
spade-7
diamond-J

Iteratorパターンを使用する利点

これまでの例ではIteratorパターンのありがたみは感じられないどころか、無駄に冗長になっているだけに見えます。
実際、これまでのコードは次のコードでも同じ結果が得られます。

public class Main {

	public Main() {
		String[] deck = {
			"diamond-1",
			"clover-1",
			"heart-1",
			"spade-1",
			"joker",
			"diamond-2",
			"clover-3",
			"heart-5",
			"spade-7",
			"diamond-J"
		};
		for(String card : deck) {
			System.out.println(card);
		}
	}

	public static void main(String[] args) {
		new Main();
	}
}

しかし、例えばここで、ダイヤのトランプだけ読み上げたくなったとします。
もちろんここでforブロック内にダイヤであるかをチェックするif文を追加することも可能です。
しかし、加えて素数の時はダイヤに限らず読みたくなったら?
ジョーカーの次は読みたくないなんて場合は?
このような複雑な条件をforブロックに組み込むくらいなら、Iteratorパターンで選択と処理を分けた方が可読性などの面でも有利なはずです。

Iteratorを変更する

ダイヤだけ読みたくなったとします。
そこでもう一つ、nextメソッドでダイヤだけを返してくるDiamondIteratorクラスを作ってみます。

public class DiamondIterator implements Iterator{

	int index = 0;
	CardsDeck deck ;

	public DiamondIterator(CardsDeck deck) {
		this.deck = deck;
	}

	@Override
	public boolean hasNext() {
		String card ;
		for(;index<deck.length();index++) {
			if((card=deck.getCard(index))!=null) {
				if(card.contains("diamond")) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public String next() {
		hasNext();
		return deck.getCard(index++);
	}

}

基本的な部分はCardsDeckIteratorと似ています。
違うのはhasNextメソッドとnextメソッドです。

hasNextメソッドは少々汚いですが、
まずはwhileブロックで次のダイヤを探しています。
1.indexフィールドが山札の数未満である
2.トランプがnullであるorダイヤではない
以上の条件ではindexフィールドをインクリメントし、
そうでなければ全てのトランプを探し終えたか、ダイヤが見つかったと判断してwhileを終了します。

戻り値として、indexフィールドが山札の数未満であればダイヤが見つかったとしてtrueを、さもなくばfalseを返します。

nextメソッドでは最初にhasNextメソッドを呼び出しています。
これは、nextメソッドの初回呼出時にIndexフィールドの位置にダイヤがあるとは限らないためです。
山札の先頭にダイヤ以外があり、かつhasNextメソッドを呼び忘れた場合、nextメソッド初回呼び出し時にはダイヤ以外を読み上げてしまうことになります。
その後、getCardメソッドを呼び出して、indexフィールドが指し示しているダイヤのトランプを返しています。


当然DiamondIteratorクラスを作っただけでは動作は変わりません。
CardsDeckクラスで実装しているiteratorメソッドを書き換えてDiamondIteratorクラスを使用するようにすることで、動作を変更できます。
CardsDeckクラスの太字部分を変更します。

public class CardsDeck implements Aggregate {

	private String[] cards ;

	public CardsDeck(String... cards) {
		this.cards = cards.clone();
	}

	public int length() {
		return cards.length;
	}

	public String getCard(int i) {
		return 0<=i && i<cards.length ? cards[i] : null;
	}

	@Override
	public Iterator iterator() {
		//return new CardsDeckIterator(this);    //ここをコメントアウト
		return new DiamondIterator(this);        //ここを追加
	}

}

変更したコードを実行してみると結果が変化します。

diamond-1
diamond-2
diamond-J

CardsDeckクラスのiterator1行と、DiamondIteratorクラスを追加するだけでiteratorの動作がガラッと変わったのが理解できたかと思います。
CardsDeckIteratorに戻したければiteratorメソッドを1行書き換えるだけですみますし、iteratorメソッドで条件分岐することで動的に切り替えることも可能になります。

まとめ

Iteratorパターンに限らず全てのデザインパターンで言えることですが、それぞれのオブジェクトを取り替え可能な部品としてみることが重要になります。
トランプを読み上げる人(Iteratorインタフェース)が誰であるかではなく、その仕事をこなせる(Iteratorを実装した)誰かがいればいいわけです。
その上で全て読み上げるAさん(CardsDeckIterator)か、ダイヤだけ読み上げるBさん(DiamondIterator)かが自由に切り替えられるように設計するわけです。

複雑な一つの部品を作るよりも単純な10個の部品を作る方がより全体を把握しやすくなります。