Java におけるコード進化パターン (Code Evolution Patterns in Java)

asato shimotaki <asatohan at gmail.com>

最終更新日 : 2009/6/21 (2004/4/22 より)



[...] For twenty years, I spent two or three hours a day looking at pairs of things -- buildings, tiles, stones, windows, carpets, figures, carvings of flowers, paths, seats, funiture, streets, paintings, fountains, doorways, arches, friezes -- comparing them, and asking myself: Which one has more life? And asking: What are the common features of the examples that have most life?

Christopher Alexander, The Nature of Order



我々は、コードの変化過程 の観察から何かを学べるでしょうか? 我々は、コードの変化について何を知っているでしょうか? このドキュメントは、繰り返し発生するコードの変化過程を コード進化のパターン としてカタログ化することを目的にしています。Java 言語を対象としたコード進化パターンの具体例を紹介します。
* 注意 * ドラフト版
嘘や適当なことが書かれている可能性があります。
嘘臭い部分 (1) Marker interface という用語の使い方。

近況報告 2008/8/24: 同様の試みとして、Scala におけるコード進化のカタログ化 を開始しました。

近況報告 2008/8/2: デザインパターンAspectJ 関連の進化パターンを別ページにしました。

近況報告 2008/7/31: ここ数日の間で、はてなブックマークで 沢山ブークマーク されていること、そしてこのテーマに興味を持っている方がいることを嬉しく思っています。

このドキュメントを読む上でフレンドリーでない部分がいくつか指摘されています。一つはドキュメントが長すぎること、もう一つは、Firefox でうまく見れないこと。近日中に対応したいと思います。

内容に関しては、図とコードのみで説明がほとんどないため分かりにくいと思います。少しずつ改善していく予定ですので、気長にお待ちください。もしくは、みなさんお気に入りの言語で、同じようにコード進化のパターンをカタログ化に挑戦するのも面白いかと思います。


注意:新しいデザインパターンを提案してるわけではありません。

はじめに

ソフトウェア進化の現実

ソフトウェアは、一度作られたら終わりというわけではなく、以下の変化に対応して進化できる必要があります。 コスト、時間、品質の観点から、ソフトウェアを適切に進化させていくことは容易ではありません。

どうするか

ソフトウェアの進化は、開発プロセス全体を横断する事柄です。 したがって、各工程において進化に対する様々なアプローチがあります。このドキュメントは、ソフトウェアの構造、より正確には、プログラムの構造に焦点を当てます。プログラム構造の変化を、観察・分析の主要な対象とします。

過去のソフトウェア開発の経験から、ソフトウェアの構造には、似た構造が繰り返し現れることが知られています。それら繰り返し現れる構造は、アーキテクチャパターンやデザインパターンと呼ばれています。

また、ソフトウェアの構造の進化にも繰り返し発生するものがあることが知られています。ソフトウェア構造の改善のために繰り返し起こるパターンは、リファクタリングと呼ばれています。

このドキュメントは、ソフトウェア構造の進化のパターンを特定し、カタログ化することを目的としています。

進化パターンを特定し集めることは重要です。

このドキュメントでは、オブジェクト指向言語である Java と、アスペクト指向のための Java の拡張である AspectJ におけるコードの進化を考えます。しかし、進化パターンのアイデア自体は、言語や環境に依存しません。

進化パターンの範囲

最も広範囲にわたる進化パターンのカバー範囲は、コードの変化のすべてを含みます。たとえば、変数名の変更や、バグの除去なども含みます。しかし、このドキュメントでは、以下のようなコードの変化(発展)のみに注目します。 以下のようなコードの変化は、ここで、進化パターンとして扱いません。

進化パターン収集方法

本カタログで紹介している進化パターンは、原則として、ソフトウェアを開発しているときに実際に遭遇した時のコードとその変化過程を抽象化・一般化したものです。ただし、現実のプロジェクトの経験を通して得られたのではなく、以下のアプリケーションを個人的に開発した経験から得られたものです。

Part 1. 理論


我々は、数十年の経験からソフトウェアは継続的な変更が要求されることを知っています。しかし、我々はまだ、ソフトウェアの進化の理論的な基礎を持ちません。ソフトウェアの進化を適切に説明できる概念とモデルを持ちません。

コード進化の概念的基礎

設計者とコード進化

コード進化を考える上でいくつかの基本的な前提があります。
  1. コードには特性が伴う:
  2. コードから様々な特性が得られます。特性には、定性的・定量的なものがあります。

  3. 要求を満たすようなコードは多数ある:
  4. 要求を満たすようなコードは多数あります。

  5. コード進化には異なるパスがある:
  6. 変化した要求を満たすようなコードは多数あります。

  7. コードの進化はランダムではなく、意図的である:
  8. 変化した要求を満たすようなコードはランダムに選ばれるのではなく、意図的に決定されます。コードの変更内容を決定する役割を持つ人を、ここでは 設計者 と呼ぶことにします。

  9. コードの変更内容は、設計者の経験に依存する: 同じ設計者であっても、変更する時期が異なれば、つまり、経験が異なれば、変更内容は異なります。

  10. コードの変更内容は、設計者に依存する: 設計者はそれぞれ異なる経験を持っているため、コードの変更内容は異なります。

コードと特性

コードを分析することで、コードに関する様々な特性を得ることができます。特性には、定量的・定性的なものがあります。定量的な特性には、たとえば、クラス数やメソッドの行数の平均値、実行速度などが考えられます。定性的なものには、再利用性や拡張性などが考えられます。定性的な特性の度合いは、分析者に依存します。


コードの特性を考える必要がある理由は、これらの特性がコードを変化させる理由となるためです。たとえば、既存のコード拡張性が不十分だという結果が得られたのなら、拡張性をさせるためにコードを変更するという決定が行われるかもしれません。

一般的なプロセスは次のようになると言えます。
ただし、この図では、コードの変更が実際に行われることは想定していません。変更後のコードと、コードから得られる特性を想像することでこのプロセスが行われてもかまわないとします。

要求を満たす多数のコード




異なるパスのコード進化

意図的なコード進化

設計者の経験に依存するコード進化

設計者に依存するコード進化

コード進化

ここでは、コード進化 をあるコードから他のコードへの変化であると考えます。コードとは、ここでは、ソースコードの集合を表します。

コード進化の一連の流れが コード進化の過程 となります。

コード進化のルール

コード進化はランダムではありませんが、コード進化には、特定の コード進化のルール があると考えられます。たとえば、Stategy パターンが適用された構造があるとし、新しいアルゴリズムを追加する要求を満たす必要が出てきたとします。この場合、この要求を満たすコードは多数考えられますが、多くの場合、新しいアルゴリズムは、新たな ConcreteStrategy として実装されます。このような場合、コード進化は特定のルールに従っていると言えます。

コード進化のパターン

コード進化のルールがあるため、特定のコード進化は繰り返し発生します。このようなコード進化を コード進化のパターン とここでは呼びます。

コード進化パス

ある進化パターンが起こった後に起こる進化パターンがあると考えられます。たとえば、Strategy パターンが導入された構造には、新しい Strategy (Concrete Strategy) が追加されるような進化のパターンが起こります。

コード進化パターンのメタモデル

コード進化の概念を表現するためにメタモデルを定義します。

コード進化の分類

いくつかの観点からコード進化パターンの分類します。

機能変化

インタフェース変化

モジュール変化

進化リクエスタとプロバイダ

プログラムの構成要素(コンポーネント、あるいはモジュール、クラス)間の関係の観点から、進化リクエスタ進化プロバイダという概念を導入します。コード進化の過程において、ある構成要素は、進化リクエスタもしくは進化プロバイダのロールを持ちます。

進化パターン間の関係

進化パターン間には関係があります。

コード進化の表現レベル

コード進化は、様々な抽象度・粒度の観点から表現できます。

コード進化のパターン化の課題

コード進化の原理

コード進化の性質

適応

Part 2. カタログ


Java 言語を具体例とし、コード進化のパターン化とカタログ化を行います。

パターンテンプレート

パターンテンプレートとして次の項目を考えます。

Java 基本関連

クラスの追加

状況&動機:

タイプ:

before:


	after:
public class MyClass {
	public void myMethod() {
		System.out.println("MyClass.myMethod()");
	}
}

参考文献とリソース

パターン関係

進化パス

関連パターン

参考文献とリソース

todo

スーパークラスの導入

状況&動機:

タイプ:リファクタリング

before:

public class MyClass {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	public void myMethod() {
		System.out.println("MyClass.myMethod()");
	}
}
public class YourClass {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	public void yourMethod() {
		System.out.println("YourClass.yourMethod()");
	}
}
after:
public class SuperClass {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
}
public class MyClass extends SuperClass {
	
	public void myMethod() {
		System.out.println("MyClass.myMethod()");
	}
}
public class YourClass extends SuperClass {
	
	public void yourMethod() {
		System.out.println("YourClass.yourMethod()");
	}
}

参考文献とリソース

進化パス

関連パターン

参考文献とリソース

todo

サブクラスの追加

状況&動機:

タイプ:

構成要素:

before:

public class MySuperClass {

}
public class MyClassA extends MySuperClass {
}
public class MyClassB extends MySuperClass {
}
after:
public class MySuperClass {

}
public class MyClassA extends MySuperClass {
}
public class MyClassB extends MySuperClass {
}
public class MyClassC extends MySuperClass {
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

メソッド関連

特化の次元。

メソッドの追加

状況&動機:

タイプ:

before:

public class MyClass {
	
	public void methodA() {
		System.out.println("MyClass.methodA");
	}
}
after:
public class MyClass {
	
	public void methodA() {
		System.out.println("MyClass.methodA");
	}
	
	public void methodB() {
		System.out.println("MyClass.methodB");
	}
}

参考文献とリソース

パターン関係

進化パス

関連パターン

参考文献とリソース

todo

public メソッドの追加

状況&動機:public なメソッドを追加する。

タイプ:

進化タイプ:

前提:public なメソッドを追加できる具象クラスまたは抽象クラスが存在する。

構成要素:

before:

public class Component {
	
	public void methodA() {
		System.out.println("Component.methodA()");
	}
}
after:

public class Component {
	
	public void methodA() {
		System.out.println("Component.methodA()");
	}
	public void methodB() {
		System.out.println("Component.methodA()");
	}
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

参考文献とリソース

その他/疑問

public な具象メソッドの追加

状況&動機:クラスに、public な具象メソッドを追加する。

タイプ:

進化タイプ:

前提:public な具象メソッドを追加できるクラスが存在する。

構成要素:

before:

public class Component {
	public void methodA() {
		System.out.println("Component.methodA()");
	}
}
after:

public class Component {

	public void methodA() {
		System.out.println("Component.methodA()");
	}
	
	public void methodB() {
		System.out.println("Component.methodB()");
	}
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

private メソッドの追加

状況&動機:private なメソッドを追加する。

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class Component {
	
	public void m() {
		System.out.println("Component method");
	}
}
after:

public class Component {
	
	public void m() {
		privateMethod();
	}
	
	private void privateMethod() {
		System.out.println("Component method");
	}
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

static メソッドの追加

状況&動機:

タイプ:

before:

public class UtilClass {
	
	public static void myStaticMethodA() {
		System.out.println("UtilClass - myStaticMethodA");
	}
}
after:
public class UtilClass {
	
	public static void myStaticMethodA() {
		System.out.println("UtilClass - myStaticMethodA");
	}

	public static void myStaticMethodB() {
		System.out.println("UtilClass - myStaticMethodB");
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

get method の追加

状況&動機:

タイプ:

before:

public class MyClass {
	
	public void method() {
		System.out.println("MyClass.method()");	
	}
}
after:
public class MyClass {
	
	private String name;
	
	public MyClass(String name) {
		this.name = name;
	}
	
	public void method() {
		System.out.println("MyClass.method()");	
	}
	
	public String getName() {
		return name;
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

set method の追加

状況&動機:

タイプ:

before:

public class MyClass {
	
	private String name;
	
	public MyClass(String name) {
		this.name = name;
	}
	
	public void method() {
		System.out.println("MyClass.method()");	
	}
	
	public String getName() {
		return name;
	}
}
after:
public class MyClass {
	
	private String name;
	
	public MyClass(String name) {
		this.name = name;
	}
	
	public void method() {
		System.out.println("MyClass.method()");	
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

Type アノテーションの追加

状況&動機: 型(Type)へのアノテーションの追加

タイプ:

before:


	after:
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {

}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

Interface へのメソッドの追加

状況&動機:

タイプ:

構成要素:

before:

public interface MyInterface {
	public void methodA();
}
public class MyInterfaceImpl implements MyInterface {

	public void methodA() {
		System.out.println("MyInterfaceImpl.methodA()");
	}
}
after:

public interface MyInterface {
	public void methodA();
	public void methodB();
}
public class MyInterfaceImpl implements MyInterface {

	public void methodA() {
		System.out.println("MyInterfaceImpl.methodA()");
	}
	
	public void methodB() {
		System.out.println("MyInterfaceImpl.methodB()");
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

Method アノテーションの追加

状況&動機:

タイプ:

before:


	after:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface MyMethodAnnotation {

}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

ランタイム例外の追加

状況&動機:

タイプ:

before:


	after:
public class MyRuntimeException extends RuntimeException {

}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

Field アノテーションの追加

状況&動機:

タイプ:

before:


	after:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.FIELD) 
public @interface MyFieldAnnotation {

}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

フィールドアクセスのための get method の追加

状況&動機:

タイプ:拡張

前提:

before:

public class Component {
	
	private String name;

	public Component(String name) {
		this.name = name;
	}
	
	public void outputName() {
		System.out.println("name = " + name);
	}
	
	public void setName(String name) {
		this.name = name;
	}
}
after:
public class EvolutionRequester {
	
	private Component comp;
	
	public EvolutionRequester(Component comp) {
		this.comp = comp;
	}
	
	public void method() {
		System.out.println("*** " + comp.getName() + " ***");
	}
}
public class Component {
	
	private String name;

	public Component(String name) {
		this.name = name;
	}
	
	public void outputName() {
		System.out.println("name = " + name);
	}
	
	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

オーバーロードメソッドの追加

状況&動機:

タイプ:

before:

public class Outputter {
	
	public void output(String str) {
		System.out.println(str);
	}
}
after:
public class Outputter {
	
	public void output(String str) {
		System.out.println(str);
	}
	
	public void output(int i) {
		System.out.println(i);
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

フィールド更新のための set method の追加

状況&動機:

タイプ:

before:

public class MyClass {
	
	private String name;
	
	public MyClass(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
}
after:
public class MyClass {
	
	private String name;
	
	public MyClass(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

interface の追加

状況&動機:

タイプ:

before:



	after:
public interface MyInterface {
	public void method();
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

interface の実装

状況&動機:

タイプ:

before:

public interface MyInterface {
	public void method();
}
public class MyClass {

	public void method() {
		System.out.println("MyClass.method()");
	}
}
after:
public class MyClass implements MyInterface {

	public void method() {
		System.out.println("MyClass.method()");
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

コレクションフィールドアクセスのための get method の追加

状況&動機:

タイプ:

before:

public class Item { }
public class ItemContainer {
	
	private List items = new ArrayList();
	
	public void addItem(Item item) {
		items.add(item);
	}
}
after:
public class Item { }
public class ItemContainer {
	
	private List items = new ArrayList();
	
	public void addItem(Item item) {
		items.add(item);
	}
	
	public Item[] getItems() {
		return items.toArray( new Item[items.size()] );
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

forwarding method(転送メソッド)の追加

状況&動機:

タイプ:

before:

public class MyClass {
	
	private ForwardedClass forwarded = new ForwardedClass();
	
	public void myMethod() {
		System.out.println("MyClass.myMethod()");
	}
}
public class ForwardedClass {
	
	public void method() {
		System.out.println("ForwardedClass.method()");
	}
}
after:
public class MyClass {
	
	private ForwardedClass forwarded = new ForwardedClass();
	
	public void myMethod() {
		System.out.println("MyClass.myMethod()");
	}
	
	public void method() { // 転送メソッド
		forwarded.method();
	}
}
public class ForwardedClass {

	public void method() {
		System.out.println("ForwardedClass.method()");
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

抽象クラス継承によるクラスの追加

状況&動機:

タイプ:

before:

public abstract class MyAbstractClass {
	
	public abstract void abstractMethod();
	
	public void method() {
		System.out.println("MyAbstractClass.method()");
	}
}
after:
public abstract class MyAbstractClass {
	
	public abstract void abstractMethod();
	
	public void method() {
		System.out.println("MyAbstractClass.method()");
	}
}
public class MyClass extends MyAbstractClass {

	@Override
	public void abstractMethod() {
		System.out.println("MyClass.abstractMethod()");
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

リファクタリング時におけるフィールドアクセスのための get method の追加

状況&動機:リファクタリング時。リファクタリング時に、あるクラスのフィールドにアクセスする必要が出てきた。

タイプ:リファクタリング

前提:

before:

public class MyClass {

	private String str;

	public MyClass(String str) {
		this.str = str;
	}
	
	public void print() {
		System.out.println("str = " + str);
	}
}
after:
public class MyClass {

	private String str;

	public MyClass(String str) {
		this.str = str;
	}
	
	public void print() {
		System.out.println("str = " + str);
	}
	
	public String getStr() {
		return str;
	}
}
public class MyClient {
	
	public void method(MyClass my) {
		System.out.println( "my.getStr() = " + my.getStr() );
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

interface の継承

状況&動機:

タイプ:拡張

前提:

before:

public interface SomeInterface {
	public void someMethod();
}
public interface MyInterface  {
	public void myMethod();
}
public class MyClass implements MyInterface {

	public void myMethod() { }
}

after:
public interface SomeInterface {
	public void someMethod();
}
public interface MyInterface extends SomeInterface {
	public void myMethod();
}
public class MyClass implements MyInterface {

	public void myMethod() { }
	
	public void someMethod() { }
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

setter/getter の追加

状況&動機:

タイプ:拡張

前提:

before:

public class Component {
	
}
after:
public class Component {

	private String name;

	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
}
public class EvolutionRequester {
	
	public void editName(Component comp) {

		String old = comp.getName();

		comp.setName("*** " + old + " ***");
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

interface におけるメソッドの置き換え

状況&動機:

タイプ:

前提:

before:

public interface Component {
	public String getName();
}
public class MyComponent implements Component {
	
	private String name;
	
	public MyComponent(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
}
public class Main {

	public static void main(String[] args) {
		printName( new MyComponent("aaa") );
	}
	
	private static void printName(Component comp) {
		System.out.println("name : " + comp.getName());
	}

}
after:

public class Name {
	
	private String name;
	
	public Name(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
}
public interface Component {
	public Name getName();
}

public class MyComponent implements Component {
	
	private Name name;
	
	public MyComponent(Name name) {
		this.name = name;
	}
	
	public Name getName() {
		return name;
	}
}
public class Main {

	public static void main(String[] args) {
		printName( new MyComponent( new Name("aaa") ) );
	}
	
	private static void printName(Component comp) {
		System.out.println("name : " + comp.getName().getName());
	}

}

参考文献とリソース

before:
import java.awt.image.BufferedImage;

public interface Player extends MapCharactor {

	public int getHP();
	public void setHP(int hp);

	public int getMaxHP();
	public void setMaxHP(int maxhp);
	
	public void setImage(BufferedImage image);	
	public BufferedImage getImage();
}
public class MainPanel extends JPanel implements Runnable {
	
	// ... 省略

	private void drawPlayer(Player player) {

		// ... 省略

		dbg.drawImage(player.getAnimationImage().getImage(), x, y, null);
	}
}
after:
public interface Player extends MapCharactor {

	public int getHP();
	public void setHP(int hp);

	public int getMaxHP();
	public void setMaxHP(int maxhp);
	
	public void setAnimationImage(AnimationImage image);	
	public AnimationImage getAnimationImage();
}
public class MainPanel extends JPanel implements Runnable {
	
	// ... 省略

	private void drawPlayer(Player player) {

		// ... 省略

		dbg.drawImage(player.getAnimationImage().getImage(), x, y, null);
	}
}

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

クラス階層の追加

状況&動機:

タイプ:

前提:

before:

public class Component {

	public void method() {
		// ... 
	}
}
after:

public class Component {
	
	public void method() {
		
		// ...

		MyComponentInterface comp = new MyComponent();
		comp.method();
	}
}

public interface MyComponentInterface {
	public void method();
}
public class MyComponent implements MyComponentInterface {
	
	public void method() {
		System.out.println("MyComponent.method()");
	}
}

参考文献とリソース

議論

この進化パターンで重要なのは、単に MyComponent を追加するのではなく、MyComponetInterface も同時に追加している点です。動機の一つは、将来なんらかの機能追加があった時に MyComponentInterface を実装する新しいクラスを追加すると予測しているということです。したがって、MyComponent インスタンス化するときにも、単に:
MyComponent comp = new MyComponent();
とせずに
MyComponentInterface comp = new MyComponent();
としています。

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

抽象クラスの継承によるコードの重複の削除

状況&動機:

タイプ:リファクタリング時

前提:抽象クラスが存在する。クラスが存在する。

before:

public class ConcreteComponentA {

	private String name;

	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	
	public void methoA() { }
}
public abstract class AbstractComponent {

	private String name;

	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
public class ConcreteComponentB extends AbstractComponent {
	public void methodB() { }
}
after:

public abstract class AbstractComponent {

	private String name;

	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
public class ConcreteComponentA extends AbstractComponent {
	
	public void methoA() { }
}
public class ConcreteComponentB extends AbstractComponent {
	public void methodB() { }
}

参考文献とリソース

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

抽象クラスへの具象メソッドの追加

状況&動機:すでに存在する抽象クラスに対し、具象メソッドを追加する。

タイプ:

進化タイプ:

前提:抽象クラスが存在する。

構成要素:

before:

public abstract class AbstractClass {

	public abstract void abstractMethod();

	public void methodA() {
		System.out.println("AbstractClass.methodA() ");
	}
}
after:

public abstract class AbstractClass {

	public abstract void abstractMethod();

	public void methodA() {
		System.out.println("AbstractClass.methodA() ");
	}
	public void methodB() {
		System.out.println("AbstractClass.methodB() ");
	}
}

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

その他/疑問

抽象クラスへの抽象メソッドの追加

状況&動機:

タイプ:

前提:抽象クラスが存在する。

before:

public abstract class AbstractClass {

	private String name;

	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	
	public abstract void abstractMethod1();
}
public class ConcreteClassA extends AbstractClass {

	@Override
	public void abstractMethod1() {
		System.out.println("ConcreteClassA.abstractMethod1()");
	}
}
public class ConcreteClassB extends AbstractClass {

	@Override
	public void abstractMethod1() {
		System.out.println("ConcreteClassB.abstractMethod1()");
	}
}
after:

public abstract class AbstractClass {

	private String name;

	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	
	public abstract void abstractMethod1();
	
	public abstract void abstractMethod2();
}
public class ConcreteClassA extends AbstractClass {

	@Override
	public void abstractMethod1() {
		System.out.println("ConcreteClassA.abstractMethod1()");
	}
	
	@Override
	public void abstractMethod2() {
		System.out.println("ConcreteClassA.abstractMethod2()");
	}
}
public class ConcreteClassB extends AbstractClass {

	@Override
	public void abstractMethod1() {
		System.out.println("ConcreteClassB.abstractMethod1()");
	}
	
	@Override
	public void abstractMethod2() {
		System.out.println("ConcreteClassB.abstractMethod2()");
	}
}

参考文献とリソース

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

抽象クラスへの具象メソッドの引き上げ

状況&動機:具象クラスの具象メソッドを抽象クラスに引き上げる。

タイプ:

進化タイプ:

前提:具象クラスと抽象クラスの間に継承関係がある。

構成要素:

before:

public abstract class AbstractClass {

	public void method() {
		System.out.println("method");
	}

	public abstract void abstractMethod();
}
public class ConcreteClass extends AbstractClass {

	public void concreteMethod() {
		System.out.println("concreteMethod");
	}
	
	@Override
	public void abstractMethod() {
		System.out.println("abstractMethod");
	}
}
after:

public abstract class AbstractClass {
	
	public void method() {
		System.out.println("method");
	}
	public abstract void abstractMethod();

	public void concreteMethod() {
		System.out.println("concreteMethod");
	}
}
public class ConcreteClass extends AbstractClass {

	@Override
	public void abstractMethod() {
		System.out.println("abstractMethod");
	}
}

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

その他/疑問

抽象クラスのジェネリック化

状況&動機:

タイプ:リファクタリング

進化タイプ:

前提:

before:

public class Component {
	
	public void componentMethod() {
		System.out.println("Component.componentMethod()");
	}
}
public class SubComponentA extends Component {
	
	public void subMethodA() {
		System.out.println("SubComponentA.subMethodA()");
	}
}
public class SubComponentB extends Component {
	
	public void subMethodB() {
		System.out.println("SubComponentB.subMethodB()");
	}
}
public abstract class AbstractClass {
	
	private Component component;
	
	public AbstractClass(Component component) {
		this.component = component;
	}
	
	public void method() {
		component.componentMethod();
	}
}
public class ConcreteClassA extends AbstractClass {

	private SubComponentA component;
	
	public ConcreteClassA(SubComponentA component) {
		super(component);
		this.component = component;
	}
	
	public void methodA() {
		component.subMethodA();
	}
}
public class ConcreteClassB extends AbstractClass {
	
	private SubComponentB component;
	
	public ConcreteClassB(SubComponentB component) {
		super(component);
		this.component = component;
	}
	
	public void methodB() {
		component.subMethodB();
	}
}
after:

public class Component { // 変更なし
	
	public void componentMethod() {
		System.out.println("Component.componentMethod()");
	}
}
public class SubComponentA extends Component { // 変更なし
	
	public void subMethodA() {
		System.out.println("SubComponentA.subMethodA()");
	}
}

public class SubComponentB extends Component { // 変更なし
	
	public void subMethodB() {
		System.out.println("SubComponentB.subMethodB()");
	}
}
public abstract class AbstractClass<T extends Component> { // ジェネリック化
	
	protected T component;
	
	public AbstractClass(T component) {
		this.component = component;
	}
	
	public void method() {
		component.componentMethod();
	}
}
public class ConcreteClassA extends AbstractClass<SubComponentA> {

	public ConcreteClassA(SubComponentA comp) {
		super(comp);
	}
	
	public void methodA() {
		component.subMethodA();
	}
}
public class ConcreteClassB extends AbstractClass<SubComponentB> {
	
	public ConcreteClassB(SubComponentB comp) {
		super(comp);
	}
	
	public void methodB() {
		component.subMethodB();
	}
}

参考文献とリソース

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

トップレベル interface の追加

状況&動機:

タイプ:

進化タイプ:

前提:

before:

public abstract class AbstractClass {

	public void commonMethod() {
		System.out.println("AbstractClass.commonMethod()");
	}

	public abstract void specificMethod();
}
public class ConcreteClassA extends AbstractClass {

	@Override
	public void specificMethod() {
		System.out.println("ConcreteClassA.specificMethod()");
	}
	
}
public class ConcreteClassB extends AbstractClass {

	@Override
	public void specificMethod() {
		System.out.println("ConcreteClassB.specificMethod()");
	}
	
}
after:

public interface Interface {

	public void commonMethod();
	public void specificMethod();
}

public abstract class AbstractClass implements Interface {

	public void commonMethod() {
		System.out.println("AbstractClass.commonMethod()");
	}

	public abstract void specificMethod();
}
public class ConcreteClassA extends AbstractClass {

	@Override
	public void specificMethod() {
		System.out.println("ConcreteClassA.specificMethod()");
	}
	
}
public class ConcreteClassB extends AbstractClass {

	@Override
	public void specificMethod() {
		System.out.println("ConcreteClassB.specificMethod()");
	}
	
}

参考文献とリソース

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

列挙型への定数の追加

状況&動機:

タイプ:

進化タイプ:

前提:

before:

public enum EnumType {
	TypeA,
	TypeB
}
after:

public enum EnumType {
	TypeA,
	TypeB,
	TypeC
}

参考文献とリソース

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

コンストラクタへのパラメータの追加

状況&動機:

タイプ:

進化タイプ:

前提:

before:

public class MyClass {

	private int i;

	public MyClass(int i) {
		this.i = i;
	}
	
	public void method() {
		System.out.println(i);
	}
}
after:

public class MyClass {

	private int i;
	private int j;

	public MyClass(int i, int j) {
		this.i = i;
		this.j = j;
	}
	
	public void method() {
		System.out.println(i);
		System.out.println(j);
	}
}

参考文献とリソース

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

todo

フィールドの削除

状況&動機:

タイプ:

進化タイプ:

前提:

before:

public class MyClass {

	private String s;
}
after:

public class MyClass {

}

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

getter/setter の移動

状況&動機:

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class ComponentA {

	private String name;

	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
public class ComponentB {
}
after:

public class ComponentA {
}
public class ComponentB {

	private String name;

	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

その他/疑問

Interface Adapter の導入

状況&動機:

タイプ:

進化タイプ:

前提:

構成要素:

before:

public interface MyInterface {

	public void methodA();
	public void methodB();
}
public class MyClassA implements MyInterface {

	@Override
	public void methodA() {
		System.out.println("MyClassA.methodA()");
	}
	
	@Override
	public void methodB() { } // 何もしない
	
}


public class MyClassB implements MyInterface {

	@Override
	public void methodA() { } // 何もしない
	
	@Override
	public void methodB() {
		System.out.println("MyClassB.methodB()");
	}
}
after:

public interface MyInterface { // 変更なし

	public void methodA();
	public void methodB();
}
public abstract class MyInterfaceAdapter implements MyInterface {

	public void methodA() { }
	public void methodB() { }
}
public class MyClassA extends MyInterfaceAdapter {

	@Override
	public void methodA() {
		System.out.println("MyClassA.methodA()");
	}
}
public class MyClassB extends MyInterfaceAdapter {

	@Override
	public void methodB() {
		System.out.println("MyClassA.methodB()");
	}
}

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

その他/疑問

interface のジェネリック化

状況&動機:interface に型パラメータを導入する。

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class Component {

}
public class SubComponentA extends Component {

}
public class SubComponentB extends Component {

}
public interface MyInterface {
	
	public void setComponent(Component c);
	
	public Component getComponent();
}
after:

public class Component { // 変更無し

}
public class SubComponentA extends Component {  // 変更無し

}
public class SubComponentB extends Component {  // 変更無し

}
public interface MyInterface<T extends Component> {
	
	public void setComponent(T c);
	
	public T getComponent();
}

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

その他/疑問

Java 基本関連 - メソッド関連 - メソッドボディ関連

進化パターン: パターン関係:

進化パス:

メソッドボディの変更

状況&動機:クラスのメソッドボディのコードを変更する。

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class Component {

	public void method() {
		System.out.println("method");
	}
}
after:

public class Component {

	public void method() {

		System.out.println("new code"); // 変更部分

		System.out.println("method");
	}
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

public メソッドのボディの変更

状況&動機:クラスのpublic メソッドのボディのコードを変更する。

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class Component {

	public void method() {
		System.out.println("method");
	}
}
after:

public class Component {

	public void method() {

		System.out.println("new code"); // 変更部分

		System.out.println("method");
	}
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

コンストラクタへのパラメータの追加によるメソッドボディの変更

状況&動機:あるクラスのコンストラクタのパラメータに追加により、そのコンストラクタを呼び出しているメソッドボディを変更が必要になった。

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class Component {

	private String param1;
	private String param2;

	public Component(String param1, String param2) {
		this.param1 = param1;
		this.param2 = param2;
	}
	
	public String getParam1() {
		return param1;
	}

	public String getParam2() {
		return param2;
	}
}
public class ComponentClient {
	
	public void method() {
		
		Component comp = new Component("aaa", "bbb");
	
		System.out.println( comp.getParam1() );
		System.out.println( comp.getParam2() );
	}
}
after:

public class Component {

	private String param1;
	private String param2;
	private String param3;

	public Component(String param1, String param2, String param3) { // コンストラクタへのパラメータの追加
		this.param1 = param1;
		this.param2 = param2;
		this.param3 = param3;
	}
	
	public String getParam1() {
		return param1;
	}

	public String getParam2() {
		return param2;
	}

	public String getParam3() {
		return param3;
	}
}
public class ComponentClient {
	
	public void method() {
		
		Component comp = new Component("aaa", "bbb", "ccc");
	
		System.out.println( comp.getParam1() );
		System.out.println( comp.getParam2() );
		System.out.println( comp.getParam3() );
	}
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

Java 関連

Java 関連 - Event Listener 関連

構成要素: パターン: パターン関係:

進化パス:

EventListener の導入

状況&動機:

タイプ:

進化タイプ:

前提:

before:

public class Comonent {
		
	public void method() {
		runEvent();
	}
	
	private void runEvent() {
		System.out.println("event");
	}
}
public class Main {

	public static void main(String[] args) {

		Comonent p = new Comonent();

		p.method();
	}
	
}
実行結果:
event
after:

public class ComponentEvent {  }
public interface ComponentEventListener {

	public void eventStarted(ComponentEvent e);
	public void eventFinished(ComponentEvent e);
}
public class ComponentEventListenerImpl implements ComponentEventListener {

	public void eventStarted(ComponentEvent e) {
		System.out.println("started");
	}

	public void eventFinished(ComponentEvent e) {
		System.out.println("finished");
	}
}
public class Comonent {
	
	private List listeners = new ArrayList();
	
	public void addEventListener(ComponentEventListener l) {
		this.listeners.add(l);
	}

	public void removeEventListener(ComponentEventListener l) {
		this.listeners.add(l);
	}
	
	private void fireEventStarted() {

		ComponentEvent e = new ComponentEvent();

		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			ComponentEventListener l = (ComponentEventListener)itr.next();
			l.eventStarted(e);
		}
	}

	private void fireEventFinished() {

		ComponentEvent e = new ComponentEvent();

		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			ComponentEventListener l = (ComponentEventListener)itr.next();
			l.eventFinished(e);
		}
	}
		
	public void method() {

		fireEventStarted();

		runEvent();

		fireEventFinished();
	}
	
	private void runEvent() {
		System.out.println("event");
	}
}
public class Main {

	public static void main(String[] args) {

		Comonent p = new Comonent();

		p.addEventListener( new ComponentEventListenerImpl() );
	
		p.method();
	}
}
実行結果:
started
event
finished

EventListener の導入2

状況&動機:あるオブジェクトが状態を変えた前後である処理を行っているが、この処理を分離したい。

タイプ:リファクタリング

進化タイプ:

前提:

before:

public class Component {

	private int data;

	public void setData(int data) {
		this.data = data;
	}
	
	public int getData() {
		return data;
	}
}
public class ComponentClient {

	private Component component;

	public ComponentClient(Component component) {
		this.component = component;
	}
	
	public void method() {
		
		System.out.println("action");

		component.setData(10);
	}
}
public class Main {

	public static void main(String[] args) {
		
		Component comp = new Component();
		
		ComponentClient client = new ComponentClient(comp);
		
		client.method();
	}
}
after:

public interface ComponentListener {
	
	public void dataChanged(ComponentEvent e);
}
public class ComponentDataChangedAction implements ComponentListener {

	@Override
	public void dataChanged(ComponentEvent e) {
		System.out.println("action");
	}
}
public class ComponentEvent {

	private Component comp;
	
	public ComponentEvent(Component comp) {
		this.comp = comp;
	}
	
	public Component getComponent() {
		return comp;
	}
}
public class Component {

	private int data;

	private List<ComponentListener> listeners = new ArrayList<ComponentListener>();
	
	public void addComponentListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void setData(int data) {
		
		this.data = data;
		
		// private or protected メソッドにリファクタリングしてもよい
		for(ComponentListener l: listeners) { 
			l.dataChanged(new ComponentEvent(this));
		}
	}
	
	public int getData() {
		return data;
	}
}
public class ComponentClient {

	private Component component;

	public ComponentClient(Component component) {
		this.component = component;
	}
	
	public void method() {
		component.setData(10);
	}
}
public class Main {

	public static void main(String[] args) {
			
		Component comp = new Component();
		
		comp.addComponentListener( new ComponentDataChangedAction() );
		
		ComponentClient client = new ComponentClient(comp);
		
		client.method();
	}
}

進化リクエスタ/プロバイダ

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

EventListener の導入 - パラメータの追加

before:
public interface ComponentListener { // Listener
	public void myMethodExecuted(Class type); // eventPerformed(Type param1)
}
public class Component {
	
	private ComponentListener listener = new ComponentListener() {
		public void myMethodExecuted(Class type) { }
	};
	
	public void myMethod() {

		listener.myMethodExecuted( getClass() );
	
		System.out.println("Component.myMethod()");
	}
}
public class Main {

	public static void main(String[] args) {
		
		Component comp = new Component();
		comp.myMethod();
	}
}
after:
public interface ComponentListener { // Listener

	public void myMethodExecuted(Class type, String methodName); // eventPerformed(Type param1, Type param2)
}
public class Component {
	
	private ComponentListener listener = new ComponentListener() {

		public void myMethodExecuted(Class type, String methodName) { }
	};
	
	public void myMethod() {

		listener.myMethodExecuted( getClass(), "myMethod");
	
		System.out.println("Component.myMethod()");
	}
}
public class Main {

	public static void main(String[] args) {
		
		Component comp = new Component();
		comp.myMethod();
	}
}

EventListener の導入 - イベントの追加

before:
public interface ComponentListener {

	public void myMethodExecuted(Class type);
}
public class Component {
	
	private ComponentListener listener = new ComponentListener() {

		public void myMethodExecuted(Class type) { }
	};
	
	public void setComponentListener(ComponentListener listener) {
		this.listener = listener;
	}
	
	public void myMethod() {

		listener.myMethodExecuted( getClass() );
	
		System.out.println("Component.myMethod()");
	}
}
after:
public interface ComponentListener {
	
	public void myMethodExecuted(Class type);
	public void yourMethodExecuted(Class type);
}
public class Component {
	
	private ComponentListener listener = new ComponentListener() {

		public void myMethodExecuted(Class type) { }
		public void yourMethodExecuted(Class type) { }
	};
	
	public void setComponentListener(ComponentListener listener) {
		this.listener = listener;
	}
	
	public void myMethod() {

		listener.myMethodExecuted( getClass() );
	
		System.out.println("Component.myMethod()");
	}

	public void yourMethod() {

		listener.yourMethodExecuted( getClass() );
	
		System.out.println("Component.yourMethod()");
	}
}

Event Listener の導入 - Event の追加2

状況&動機:

タイプ:

before:

public class Event { }
public interface EventListener {
	public void eventAOccured(Event event);
	public void eventBOccured(Event event);
}
public class EventSource {
	
	private List listeners = new ArrayList();
	
	
	public void addEventListener(EventListener l) {
		listeners.add(l);
	}
	
	public void method() {

		fireEventAOccured( new Event() );

		// ...
		
		fireEventBOccured( new Event() );
	}
	
	private void fireEventAOccured(Event event) {
		
		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			( (EventListener)itr.next() ).eventAOccured(event);
		}
	}

	private void fireEventBOccured(Event event) {
		
		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			( (EventListener)itr.next() ).eventBOccured(event);
		}
	}
}
after:
public class Event { }
public interface EventListener {

	public void eventAOccured(Event event);
	public void eventBOccured(Event event);
	public void eventCOccured(Event event);
}
public class EventSource {
	
	private List listeners = new ArrayList();
	
	
	public void addEventListener(EventListener l) {
		listeners.add(l);
	}
	
	public void method() {

		fireEventAOccured( new Event() );

		// ...
		
		fireEventBOccured( new Event() );

		// ...
		
		fireEventCOccured( new Event() );
	}
	
	private void fireEventAOccured(Event event) {
		
		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			( (EventListener)itr.next() ).eventAOccured(event);
		}
	}

	private void fireEventBOccured(Event event) {
		
		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			( (EventListener)itr.next() ).eventBOccured(event);
		}
	}
	
	private void fireEventCOccured(Event event) {
		
		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			( (EventListener)itr.next() ).eventCOccured(event);
		}
	}
}

参考文献とリソース

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

ConcreteListener の追加

状況&動機:ConcreteListener を追加する。

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class ComponentEvent {
	
	private String contextInfo1;
	private String contextInfo2;
	
	public ComponentEvent(String contextInfo1, String contextInfo2) {
		this.contextInfo1 = contextInfo1;
		this.contextInfo2 = contextInfo2;
	}
	
	public String getContextInfo1() {
		return contextInfo1;
	}
	public String getContextInfo2() {
		return contextInfo2;
	}
}
public interface ComponentListener {

	public void eventOccurred(ComponentEvent e);
}
public class ConcreteComponentListenerA implements ComponentListener {

	@Override
	public void eventOccurred(ComponentEvent e) {
	
		System.out.println("ConcreteComponentListenerA - " + e.getContextInfo1() );
	}
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
		
		String eventContextInfo1 = "eventContextInfo1";
		String eventContextInfo2 = "eventContextInfo2";
		
		// ... 何らかの処理
		
		fireEventOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2) );

		// ... 何らかの処理
	}
	
	private void fireEventOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventOccurred(e);
			  
		}
	}	
}
after:

public class ComponentEvent {
	
	private String contextInfo1;
	private String contextInfo2;
	
	public ComponentEvent(String contextInfo1, String contextInfo2) {
		this.contextInfo1 = contextInfo1;
		this.contextInfo2 = contextInfo2;
	}
	
	public String getContextInfo1() {
		return contextInfo1;
	}
	public String getContextInfo2() {
		return contextInfo2;
	}
}
public interface ComponentListener {

	public void eventOccurred(ComponentEvent e);
}
public class ConcreteComponentListenerA implements ComponentListener {

	@Override
	public void eventOccurred(ComponentEvent e) {
	
		System.out.println("ConcreteComponentListenerA - " + e.getContextInfo1() );
	}
}
public class ConcreteComponentListenerB implements ComponentListener {

	@Override
	public void eventOccurred(ComponentEvent e) {
	
		System.out.println("ConcreteComponentListenerB - " + e.getContextInfo2() );
	}
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
		
		String eventContextInfo1 = "eventContextInfo1";
		String eventContextInfo2 = "eventContextInfo2";
		
		// ... 何らかの処理
		
		fireEventOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2) );

		// ... 何らかの処理
	}
	
	private void fireEventOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventOccurred(e);
			  
		}
	}	
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

EventListener の導入 - Null Listener の導入

before:
public interface ComponentListener {
	public void methodExecuted(String methodName);
}

public class Component {
	
	private ComponentListener listener = new ComponentListener() {

		public void methodExecuted(String methodName) { }
	};	
	

	public void myMethod() {

		listener.methodExecuted("myMethod");
	
		System.out.println("Component.myMethod()");
	}
	
	public void setComponentListener(ComponentListener listener) {

		this.listener = listener;
	}
}
after:
public interface ComponentListener {
	public void methodExecuted(String methodName);
}

public class NullComponentListener implements ComponentListener {

	public void methodExecuted(String methodName) { }
}
public class Component {
	
	private ComponentListener listener = new NullComponentListener();


	public void myMethod() {

		listener.methodExecuted("myMethod");
	
		System.out.println("Component.myMethod()");
	}
	
	public void setComponentListener(ComponentListener listener) {

		this.listener = listener;
	}
}

EventListener の導入 - Listener Method の追加

before:
public interface ComponentListener {
	public void beforeMyMethodExecution();
}
public class ConcreteComponentListener implements ComponentListener {

	public void beforeMyMethodExecution() {
		System.out.println("ConcreteComponentListener.beforeMyMethodExecution()");
	}
}
public class Component {
	
	private ComponentListener listener = new ComponentListener() {
		public void beforeMyMethodExecution() { }
	};
	
	public void myMethod() {

		listener.beforeMyMethodExecution();
	
		System.out.println("Component.myMethod()");
	}
	
	public void setComponentListener(ComponentListener listener) {
		this.listener = listener;
	}
}
after:
public interface ComponentListener {
	public void beforeMyMethodExecution();
	public void afterMyMethodExecution();
}
public class ConcreteComponentListener implements ComponentListener {

	public void beforeMyMethodExecution() {
		System.out.println("ConcreteComponentListener.beforeMyMethodExecution()");
	}
	public void afterMyMethodExecution() {
		System.out.println("ConcreteComponentListener.afterMyMethodExecution()");
	}
}
public class Component {
	
	private ComponentListener listener = new ComponentListener() {

		public void beforeMyMethodExecution() { }

		public void afterMyMethodExecution() { }
	};
	
	public void myMethod() {

		listener.beforeMyMethodExecution();
	
		System.out.println("Component.myMethod()");

		listener.afterMyMethodExecution();
	}
	
	public void setComponentListener(ComponentListener listener) {
		this.listener = listener;
	}
}

進化パスと関連パターン

参考文献とリソース

todo

Event Object への get method の追加

状況&動機:Event Object から情報を得るためのメソッドを追加する。

タイプ:

進化タイプ:メソッドの追加

前提:Event Listener パターンが適用されている。Event Object が存在する。

before:

public class ComponentEvent {

	private String eventInfo1;

	public ComponentEvent(String eventInfo1) {
		this.eventInfo1 = eventInfo1;
	}
	
	public String getEventInfo1() {
		return eventInfo1;
	}
}
public interface ComponentListener {
	public void eventOccurred(ComponentEvent e);
}
public class MyComponentListenerA implements ComponentListener {
	
	public void eventOccurred(ComponentEvent e) {
		System.out.println("event info1: " + e.getEventInfo1() );
	}
}
public class Component {
	
	private List listeners = new ArrayList();

	public void addComponentListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
	
		System.out.println("before evnet");
		
		fireEventOccurred( new ComponentEvent("aaa") );

		System.out.println("after evnet");
	}
	
	private void fireEventOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventOccurred(e);
		}
	}
}
public class Main {

	public static void main(String[] args) {
	
		Component comp = new Component();
		
		comp.addComponentListener( new MyComponentListenerA() );

		comp.method();
	}
}
実行結果:
before evnet
event info1: aaa
after evnet
after:

public class ComponentEvent {

	private String eventInfo1;
	private int eventInfo2;

	public ComponentEvent(String eventInfo1, int eventInfo2) {
		this.eventInfo1 = eventInfo1;
		this.eventInfo2 = eventInfo2;
	}
	
	public String getEventInfo1() {
		return eventInfo1;
	}

	public int getEventInfo2() {
		return eventInfo2;
	}
}
public interface ComponentListener { // 変更なし

	public void eventOccurred(ComponentEvent e);
}
public class MyComponentListenerA implements ComponentListener { // 変更なし
	
	public void eventOccurred(ComponentEvent e) {
		System.out.println("event info1: " + e.getEventInfo1() );
	}
}
public class MyComponentListenerB implements ComponentListener { // 新規追加
	
	public void eventOccurred(ComponentEvent e) {
		System.out.println("event info2: " + e.getEventInfo2() );
	}
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addComponentListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
	
		System.out.println("before evnet");
	
		fireEventOccurred( new ComponentEvent("aaa", 111) );
	
		System.out.println("after evnet");
	}
	
	private void fireEventOccurred(ComponentEvent e) {
	
		for(ComponentListener l : listeners) {
			l.eventOccurred(e);
		}
	}
}
public class Main {

	public static void main(String[] args) {
	
		Component comp = new Component();
		
		comp.addComponentListener( new MyComponentListenerA() );
		comp.addComponentListener( new MyComponentListenerB() );

		comp.method();
	}
}
実行結果:
before evnet
event info1: aaa
event info2: 111
after evnet

参考文献とリソース

進化リクエスタ

進化を要求するクラスには、次が考えられる:

議論

進化パス

パターン関係

関連パターン

参考文献とリソース

todo

EventObject のコンストラクタへのパラメータの追加

状況&動機:EventObject のコンストラクタにイベントのコンテキスト情報を表すパラメータを追加する。

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class ComponentEvent {
	
	private String contextInfo1;
	private String contextInfo2;
	
	public ComponentEvent(String contextInfo1, String contextInfo2) {
		this.contextInfo1 = contextInfo1;
		this.contextInfo2 = contextInfo2;
	}
	
	public String getContextInfo1() {
		return contextInfo1;
	}
	public String getContextInfo2() {
		return contextInfo2;
	}
}
public interface ComponentListener {

	public void eventOccurred(ComponentEvent e);
}
public class ConcreteComponentListener implements ComponentListener {

	@Override
	public void eventOccurred(ComponentEvent e) {
	
		System.out.println( e.getContextInfo1() );
		System.out.println( e.getContextInfo2() );
	}
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
		
		String eventContextInfo1 = "eventContextInfo1";
		String eventContextInfo2 = "eventContextInfo2";
		
		// ... 何らかの処理
		
		fireEventOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2) );

		// ... 何らかの処理
	}
	
	private void fireEventOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventOccurred(e);
			  
		}
	}	
}
after:

public class ComponentEvent {
	
	private String contextInfo1;
	private String contextInfo2;
	private String contextInfo3;

	public ComponentEvent(String contextInfo1, String contextInfo2, String contextInfo3) {
		this.contextInfo1 = contextInfo1;
		this.contextInfo2 = contextInfo2;
		this.contextInfo3 = contextInfo3;
	}
	
	public String getContextInfo1() {
		return contextInfo1;
	}
	public String getContextInfo2() {
		return contextInfo2;
	}
}

public interface ComponentListener {

	public void eventOccurred(ComponentEvent e);
}
public class ConcreteComponentListener implements ComponentListener {

	@Override
	public void eventOccurred(ComponentEvent e) {
	
		System.out.println( e.getContextInfo1() );
		System.out.println( e.getContextInfo2() );
	}
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
		
		String eventContextInfo1 = "eventContextInfo1";
		String eventContextInfo2 = "eventContextInfo2";
		String eventContextInfo3 = "eventContextInfo3";
		
		// ... 何らかの処理
		
		fireEventOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2, eventContextInfo3) );

		// ... 何らかの処理
	}
	
	private void fireEventOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventOccurred(e);
			  
		}
	}	
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

event handler メソッドの実装

状況&動機:発生したイベントを処理するために、event handler メソッドを実装する。

タイプ:

進化タイプ:

前提:具体的な処理が記述されていない event handler メソッドが存在する。

構成要素:

before:

public class ComponentEvent {
	
	private String contextInfo1;
	private String contextInfo2;
	
	public ComponentEvent(String contextInfo1, String contextInfo2) {
		this.contextInfo1 = contextInfo1;
		this.contextInfo2 = contextInfo2;
	}
	
	public String getContextInfo1() {
		return contextInfo1;
	}
	public String getContextInfo2() {
		return contextInfo2;
	}
}
public interface ComponentListener {

	public void eventAOccurred(ComponentEvent e);
	public void eventBOccurred(ComponentEvent e);
}
public class ConcreteComponentListenerA implements ComponentListener {

	@Override
	public void eventAOccurred(ComponentEvent e) {
	
		System.out.println( "ConcreteComponentListenerA.eventAOccurred" );
	}

	@Override
	public void eventBOccurred(ComponentEvent e) {
		System.out.println( "ConcreteComponentListenerA.eventBOccurred" );
	}
}
public class ConcreteComponentListenerB implements ComponentListener {

	@Override
	public void eventAOccurred(ComponentEvent e) {
	
		System.out.println( "ConcreteComponentListenerB.eventAOccurred" );
	}
	@Override
	public void eventBOccurred(ComponentEvent e) { // このイベントには対応しない
	}
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
		
		String eventContextInfo1 = "eventContextInfo1";
		String eventContextInfo2 = "eventContextInfo2";
		
		// ... 何らかの処理
		
		fireEventAOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2) );

		// ... 何らかの処理

		fireEventBOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2) );

		// ... 何らかの処理
	}
	
	private void fireEventAOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventAOccurred(e);
		}
	}	
	private void fireEventBOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventBOccurred(e);
		}
	}	
}
after:

public class ComponentEvent { // 変更なし
	
	private String contextInfo1;
	private String contextInfo2;
	
	public ComponentEvent(String contextInfo1, String contextInfo2) {
		this.contextInfo1 = contextInfo1;
		this.contextInfo2 = contextInfo2;
	}
	
	public String getContextInfo1() {
		return contextInfo1;
	}
	public String getContextInfo2() {
		return contextInfo2;
	}
}
public interface ComponentListener { // 変更なし

	public void eventAOccurred(ComponentEvent e);
	public void eventBOccurred(ComponentEvent e);
}
public class ConcreteComponentListenerA implements ComponentListener { // 変更なし

	@Override
	public void eventAOccurred(ComponentEvent e) {
	
		System.out.println( "ConcreteComponentListenerA.eventAOccurred" );
	}

	@Override
	public void eventBOccurred(ComponentEvent e) {
		System.out.println( "ConcreteComponentListenerA.eventBOccurred" );
	}
}
public class ConcreteComponentListenerB implements ComponentListener {

	@Override
	public void eventAOccurred(ComponentEvent e) {
	
		System.out.println( "ConcreteComponentListenerB.eventAOccurred" );
	}
	@Override
	public void eventBOccurred(ComponentEvent e) { // このイベントには対応するように変更
		System.out.println( "ConcreteComponentListenerB.eventAOccurred" );
	}
}
public class Component { // 変更なし
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
		
		String eventContextInfo1 = "eventContextInfo1";
		String eventContextInfo2 = "eventContextInfo2";
		
		// ... 何らかの処理
		
		fireEventAOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2) );

		// ... 何らかの処理

		fireEventBOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2) );

		// ... 何らかの処理
	}
	
	private void fireEventAOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventAOccurred(e);
		}
	}	
	private void fireEventBOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventBOccurred(e);
		}
	}	
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

EventObject のコンストラクタへのパラメータの追加による Event Source のメソッドボディの変更

状況&動機:EventObject ののコンストラクタのパラメータに追加により、そのコンストラクタを呼び出している Event Source のメソッドボディを変更が必要になった。

タイプ:

進化タイプ:

前提:

構成要素:

before:

public class ComponentEvent {
	
	private String contextInfo1;
	private String contextInfo2;
	
	// Event Object のコンストラクタへのパラメータの追加
	public ComponentEvent(String contextInfo1, String contextInfo2, String contextInfo3) { 
		this.contextInfo1 = contextInfo1;
		this.contextInfo2 = contextInfo2;
	}
	
	public String getContextInfo1() {
		return contextInfo1;
	}
	public String getContextInfo2() {
		return contextInfo2;
	}
}
public interface ComponentListener {

	public void eventOccurred(ComponentEvent e);
}
public class ConcreteComponentListener implements ComponentListener {

	@Override
	public void eventOccurred(ComponentEvent e) {
	
		System.out.println( e.getContextInfo1() );
		System.out.println( e.getContextInfo2() );
	}
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
		
		String eventContextInfo1 = "eventContextInfo1";
		String eventContextInfo2 = "eventContextInfo2";
		
		// ... 何らかの処理
		
		// コンパイルエラー
		fireEventOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2) );

		// ... 何らかの処理
	}
	
	private void fireEventOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventOccurred(e);
			  
		}
	}	
}
after:

public class ComponentEvent {
	
	private String contextInfo1;
	private String contextInfo2;
	
	// Event Object のコンストラクタへのパラメータの追加
	public ComponentEvent(String contextInfo1, String contextInfo2, String contextInfo3) { 
		this.contextInfo1 = contextInfo1;
		this.contextInfo2 = contextInfo2;
	}
	
	public String getContextInfo1() {
		return contextInfo1;
	}
	public String getContextInfo2() {
		return contextInfo2;
	}
}
public interface ComponentListener {

	public void eventOccurred(ComponentEvent e);
}
public class ConcreteComponentListener implements ComponentListener {

	@Override
	public void eventOccurred(ComponentEvent e) {
	
		System.out.println( e.getContextInfo1() );
		System.out.println( e.getContextInfo2() );
	}
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener l) {
		listeners.add(l);
	}
	
	public void method() {
		
		String eventContextInfo1 = "eventContextInfo1";
		String eventContextInfo2 = "eventContextInfo2";
		String eventContextInfo3 = "eventContextInfo3";
		
		// ... 何らかの処理
		
		fireEventOccurred( new ComponentEvent(eventContextInfo1, eventContextInfo2, eventContextInfo3) );

		// ... 何らかの処理
	}
	
	private void fireEventOccurred(ComponentEvent e) {

		for(ComponentListener l : listeners) {
			l.eventOccurred(e);
			  
		}
	}	
}

進化リクエスタ/プロバイダ

議論

パターン関係

進化パス

関連パターン

参考文献とリソース

その他/疑問

Listener interface の implements

状況&動機:

タイプ:

before:

public class MyClass {
	
	public void myMethod() {
		System.out.println("MyClass.myMethod()");
	}
}
public interface ComponentListener {
	public void methodInvoked(Component comp);
}
public class Component {
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener listener) {
		listeners.add(listener);
	}
	public void removeListener(ComponentListener listener) {
		listeners.remove(listener);
	}
	
	private void fireMethodInvoked() {
		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			( (ComponentListener)itr.next() ).methodInvoked(this);
		}
	}
	
	public void method() {
		fireMethodInvoked();
		
		System.out.println("Component.method()");
	}
}

after:
public class MyClass implements ComponentListener {
	
	public void methodInvoked(Component comp) {
	}
	
	public void myMethod() {
		System.out.println("MyClass.myMethod()");
	}
}

 
public interface ComponentListener { // 変更なし
	public void methodInvoked(Component comp);
}
public class Component { // 変更なし
	
	private List listeners = new ArrayList();
	
	public void addListener(ComponentListener listener) {
		listeners.add(listener);
	}
	public void removeListener(ComponentListener listener) {
		listeners.remove(listener);
	}
	
	private void fireMethodInvoked() {
		for(Iterator itr = listeners.iterator(); itr.hasNext();) {
			( (ComponentListener)itr.next() ).methodInvoked(this);
		}
	}
	
	public void method() {
		fireMethodInvoked();
		
		System.out.println("Component.method()");
	}
}

参考文献とリソース

進化パス

関連パターン

参考文献とリソース

todo

ElementType の導入

before:
public interface Element { }
public class ConcreteElementA implements Element { }
public class ConcreteElementB implements Element { }
after:
public class ElementType {

	public static final ElementType ELEMENT_A = new ElementType();
	public static final ElementType ELEMENT_B = new ElementType();

	private ElementType() { }
}
public interface Element {

	public ElementType getType();
}
public class ConcreteElementA implements Element {

	public ElementType getType() {
		return ElementType.ELEMENT_A;
	}
}
public class ConcreteElementB implements Element {

	public ElementType getType() {
		return ElementType.ELEMENT_B;
	}

}

インタフェースの実装 + DI

before:

public class MyConcreteClass {

	public void methodA() { }
	public void methodB() { }
	public void methodC() { }
}
public class Main {

	public static void main(String[] args) {

		MyConcreteClass my = new MyConcreteClass();
		my.methodA();
	}
}
after:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="my" class="evo.impl_inter.MyConcreteClass"/>
</beans>
public interface MyInterface {
	public void methodA();
}
public class MyConcreteClass implements MyInterface {

	public void methodA() { }
	public void methodB() { }
	public void methodC() { }
}
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {

		BeanFactory factory = new ClassPathXmlApplicationContext("bean.xml");

		MyInterface my = (MyInterface)factory.getBean("my");
		my.methodA();
	}
}

参考文献とリソース

  • Spring (http://www.springframework.org/)
  • Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler (http://martinfowler.com/articles/injection.html)
  • todo

    Preprocess Command Executor の導入

    タイプ:リファクタリング?

    before:

    public class Data {
    	// データを編集する適当なメソッド
    }
    
    public class ConcretePreprocessCommandA {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandA - param = " + param);
    	}
    }
    
    public class ConcretePreprocessCommandB {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandB - param = " + param);
    	}
    }
    
    public class ConcretePreprocessCommandC {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandC - param = " + param);
    	}
    }
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		Data data = new Data();
    
    		ConcretePreprocessCommandA commA = new ConcretePreprocessCommandA();
    		commA.setParam("aaa");
    		
    		commA.execute(data);
    
    
    		ConcretePreprocessCommandB commB = new ConcretePreprocessCommandB();
    		commB.setParam("bbb");
    		
    		commB.execute(data);
    
    
    		ConcretePreprocessCommandC commC = new ConcretePreprocessCommandC();
    		commC.setParam("ccc");
    		
    		commC.execute(data);
    	}
    }
    
    after:
    public class Data {
    	// データを編集する適当なメソッド
    }
    
    public interface PreprocessCommand {
    	public void execute(Data data);
    }
    
    public class ConcretePreprocessCommandA implements PreprocessCommand {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandA - param = " + param);
    	}
    }
    
    public class ConcretePreprocessCommandB implements PreprocessCommand  {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandB - param = " + param);
    	}
    }
    
    public class ConcretePreprocessCommandC implements PreprocessCommand  {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandC - param = " + param);
    	}
    }
    
    public class PreprocessCommandExecutor {
    	
    	private List commands = new ArrayList();
    	
    	public void addCommand(PreprocessCommand command) {
    		commands.add(command);
    	}
    	
    	public void execute(Data data) {
    		
    		for(Iterator itr = commands.iterator(); itr.hasNext(); ) {
    			( (PreprocessCommand)itr.next() ).execute(data);
    		}
    	}
    }
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		Data data = new Data();
    
    
    		ConcretePreprocessCommandA commA = new ConcretePreprocessCommandA();
    		commA.setParam("aaa");
    		
    		ConcretePreprocessCommandB commB = new ConcretePreprocessCommandB();
    		commB.setParam("bbb");
    		
    		ConcretePreprocessCommandC commC = new ConcretePreprocessCommandC();
    		commC.setParam("ccc");
    		
    
    		PreprocessCommandExecutor executor = new PreprocessCommandExecutor();
    
    		executor.addCommand(commA);
    		executor.addCommand(commB);
    		executor.addCommand(commC);
    		
    		executor.execute(data);
    	}
    }
    

    進化パス

    関連パターン

    参考文献とリソース

    todo

    TestCase の追加

    状況&動機:

    タイプ:

    before:

    public class Item {
    	
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    }
    
    public class ItemContainer {
    	
    	private List items = new ArrayList();
    	
    	public void addItem(Item item) {
    		items.add(item);
    	}
    	public void removeItem(Item item) {
    		items.remove(item);
    	}
    	
    	public boolean contains(Item item) {
    		return items.contains(item);
    	}
    }
    
    after:
    public class Item {
    	
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    }
    
    public class ItemContainer {
    	
    	private List items = new ArrayList();
    	
    	public void addItem(Item item) {
    		items.add(item);
    	}
    	public void removeItem(Item item) {
    		items.remove(item);
    	}
    	
    	public boolean contains(Item item) {
    		return items.contains(item);
    	}
    }
    
    public class ItemContainerTest extends TestCase {
    
    	public void test() {
    		
    		Item item = new Item("aaa");
    		
    		ItemContainer container = new ItemContainer();
    		container.addItem(item);
    		
    		assertTrue(container.contains(item));
    	}
    }
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    Test Method の追加

    状況&動機:

    タイプ:

    before:

    public class Item {
    	
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    }
    
    public class ItemContainer {
    	
    	private List items = new ArrayList();
    	
    	public void addItem(Item item) {
    		items.add(item);
    	}
    	public void removeItem(Item item) {
    		items.remove(item);
    	}
    	
    	public boolean contains(Item item) {
    		return items.contains(item);
    	}
    }
    
    public class ItemContainerTest extends TestCase {
    
    	public void test() {
    		
    		Item item = new Item("aaa");
    		
    		ItemContainer container = new ItemContainer();
    		container.addItem(item);
    		
    		assertTrue(container.contains(item));
    	}
    }
    
    after:
    public class Item {
    	
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    }
    
    public class ItemContainer {
    	
    	private List items = new ArrayList();
    	
    	public void addItem(Item item) {
    		items.add(item);
    	}
    	public void removeItem(Item item) {
    		items.remove(item);
    	}
    	
    	public boolean contains(Item item) {
    		return items.contains(item);
    	}
    	
    	public int size() {
    		return items.size();
    	}
    }
    
    public class ItemContainerTest extends TestCase {
    
    
    	public void testContains() {
    		
    		Item item = new Item("aaa");
    		
    		ItemContainer container = new ItemContainer();
    		container.addItem(item);
    		
    		assertTrue(container.contains(item));
    	}
    
    	public void testSize() {
    		
    		ItemContainer container = new ItemContainer();
    
    		container.addItem( new Item("aaa") );
    		container.addItem( new Item("bbb") );
    		
    		assertEquals(2, container.size());
    	}
    }
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    既存の interface の実装

    状況&動機:

    タイプ:

    before:

    public class Item {
    	
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    	
    	public String getName() {
    		return name;
    	}
    }
    
    public class ItemContainer {
    	
    	private List items = new ArrayList();
    	
    	public void add(Item item) {
    		items.add(item);
    	}
    	public void remove(Item item) {
    		items.remove(item);
    	}
    }
    
    after:
    public class Item {
    	
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    	
    	public String getName() {
    		return name;
    	}
    }
    
    public class ItemContainer implements Iterable<Item> {
    	
    	private List<Item> items = new ArrayList<Item>();
    	
    	public void add(Item item) {
    		items.add(item);
    	}
    	public void remove(Item item) {
    		items.remove(item);
    	}
    	
    	public Iterator<Item> iterator() {
    		return items.iterator();
    	}
    }

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    boolean query method 関連

    boolean query method の追加

    状況&動機:

    タイプ:

    before:

    public class Item {
    	
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    	
    	public String getName() {
    		return name;
    	}
    }
    
    public class ItemContainer {
    	
    	private List items = new ArrayList();
    	
    	public void add(Item item) {
    		items.add(item);
    	}
    	public void remove(Item item) {
    		items.remove(item);
    	}
    }
    
    after:
    public class Item {
    	
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    	
    	public String getName() {
    		return name;
    	}
    }
    
    public class ItemContainer {
    	
    	private List items = new ArrayList();
    	
    	public void add(Item item) {
    		items.add(item);
    	}
    	public void remove(Item item) {
    		items.remove(item);
    	}
    
    	public boolean isEmpty() {
    		return items.isEmpty();
    	}
    
    }
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    composite boolean query method の追加

    状況&動機:他の boolean query method を呼び出す boolean query method である composite boolean query method を追加する。

    タイプ:

    進化タイプ:

    前提:

    構成要素:

    before:

    public class Component {
    	
    	private String s;
    	
    	public Component(String s) {
    		this.s = s;
    	}
    	
    	public boolean isAAA() {
    		return s.equals("AAA");
    	}
    	public boolean isBBB() {
    		return s.equals("BBB");
    	}
    }
    
    after:

    public class Component {
    	
    	private String s;
    	
    	public Component(String s) {
    		this.s = s;
    	}
    	
    	public boolean isAAA() {
    		return s.equals("AAA");
    	}
    	public boolean isBBB() {
    		return s.equals("BBB");
    	}
    	public boolean isAAAorBBB() {
    		return isAAA() || isBBB();
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    その他/疑問

    public boolean query method の追加

    状況&動機:public な boolean query method を追加する。

    タイプ:

    進化タイプ:

    前提:

    構成要素:

    before:

    public class Player {
    	
    	private int hp;
    	private int maxHP;
    	
    	public Player(int hp, int maxHP) {
    		this.hp    = hp;
    		this.maxHP = maxHP;
    	}
    	
    	public void setHP(int hp) {
    		this.hp = hp;
    	}
    	
    	public int getHP() {
    		return hp;
    	}
    	
    	public String getState() {
    
    		if (hp <= 0) {
    			return "Dead";
    
    		} else {
    			return "Normal";
    		}
    	}
    }
    
    ppublic class PlayerClient {
    	
    	public void printStatus(Player player) {
    
    		System.out.println( player.getState() );
    		System.out.println( player.getHP() );
    	}
    }
    
    after:

    public class Player {
    	
    	private int hp;
    	private int maxHP;
    	
    	public Player(int hp, int maxHP) {
    		this.hp    = hp;
    		this.maxHP = maxHP;
    	}
    	
    	public void setHP(int hp) {
    		this.hp = hp;
    	}
    	
    	public int getHP() {
    		return hp;
    	}
    	
    	public String getState() {
    
    		if (hp <= 0) {
    			return "Dead";
    
    		} else {
    			return "Normal";
    		}
    	}
    	
    	public boolean isMaxHP() {
    		return hp == maxHP;
    	}
    }
    
    public class PlayerClient {
    	
    	public void printStatus(Player player) {
    	
    		System.out.println( player.getState() );
    		System.out.println( player.getHP() );
    		System.out.println( player.isMaxHP() );
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    その他/疑問

    private boolean query method の追加

    状況&動機:private な boolean query method を追加する。

    タイプ:

    進化タイプ:

    前提:

    構成要素:

    before:

    public class Player {
    	
    	private int hp;
    	
    	public Player(int hp) {
    		this.hp = hp;
    	}
    	
    	public String getState() {
    
    		if (hp <= 0) {
    			return "Dead";
    
    		} else {
    			return "Normal";
    		}
    	}
    }
    
    after:

    public class Player {
    	
    	private int hp;
    	
    	public Player(int hp) {
    		this.hp = hp;
    	}
    	
    	public String getState() {
    
    		if ( isDead() ) {
    			return "Dead";
    
    		} else {
    			return "Normal";
    		}
    	}
    	
    	private boolean isDead() {
    		return hp <= 0;
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    その他/疑問

    ファクトリメソッド関連

    パターン: パターン関係:

    進化パス:

    public メソッドからの private なファクトリメソッドの抽出

    状況&動機:public なメソッドのコード部分を、private なファクトリメソッドとして抽出する。

    タイプ:リファクタリング。

    進化タイプ:モジュール内進化。

    前提:public なメソッドが存在する。

    構成要素:

    before:

    public class Product {
    	
    	private String param;
    	
    	public Product(String param) {
    		this.param = param;
    	}
    }
    
    public class Component {
    	
    	public void publicMethod() {
    	
    		// ... 処理
    		
    		String type = // ... オブジェクト生成用のパラメータ
    
    		Product product;
    
    		if (type.equals("type_a")) {
    			product = new Product("param_a");
    		}
    		else if (type.equals("type_b")) {
    			product = new Product("param_b");
    		}
    		else {
    			product = new Product("no_param");
    		}
    		
    		// ... product を使った処理
    	}
    }
    
    after:

    public class Product { // 変更なし
    	
    	private String param;
    	
    	public Product(String param) {
    		this.param = param;
    	}
    }
    
    public class Component {
    	
    	public void publicMethod() {
    	
    		// ... 処理
    		
    		String type = // ... オブジェクト生成用のパラメータ
    
    		Product product = createProduct(type);
    
    		
    		// ... product を使った処理
    	}
    	
    	private Product createProduct(String type) { // factory method
    
    		if (type.equals("type_a")) {
    			return new Product("param_a");
    		}
    		else if (type.equals("type_b")) {
    			return new Product("param_b");
    		}
    		else {
    			return new Product("no_param");
    		}
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    パターン関係

    進化パス

    関連パターン

    参考文献とリソース

    その他/疑問

    public な具象ファクトリメソッドへのパラメータの追加

    状況&動機:public な具象ファクトリメソッドにパラメータを追加する。

    タイプ:

    進化タイプ:

    前提:public な具象ファクトリメソッドが存在する。

    構成要素:

    before:

    public class StringListFactory {
    	
    	public List create(String param1) { // パラメータを使ってオブジェクトを生成
    		
    		List list = new ArrayList();
    		
    		// ...
    
    		return list;
    	}
    }
    
    after:

    public class StringListFactory {
    	
    	public List create(String param1, String param2) { // パラメータを使ってオブジェクトを生成
    		
    		List list = new ArrayList();
    		
    		// ...
    
    		return list;
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    パターン関係

    進化パス

    関連パターン

    参考文献とリソース

    その他/疑問

    public な具象ファクトリメソッドのパラメータの削除

    状況&動機:public な具象ファクトリメソッドの不要となったパラメータを削除する。

    タイプ:

    進化タイプ:

    前提:パラメータを持つ public な具象ファクトリメソッドが存在する。

    構成要素:

    before:

    public class StringListFactory {
    	
    	public List create(String param1, int param2) { // 適当にオブジェクトを生成
    		
    		List list = new ArrayList();
    		
    		// パラメータを使って要素を追加
    		// ...
    
    		return list;
    	}
    }
    
    after:

    public class StringListFactory {
    	
    	public List create(String param) { // 不要なパラメータを削除
    		
    		List list = new ArrayList();
    		
    		// パラメータを使って要素を追加
    		// ...
    
    		return list;
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    パターン関係

    進化パス

    関連パターン

    参考文献とリソース

    その他/疑問

    public メソッドからの private メソッドの抽出

    状況&動機:あるクラスの public メソッドの部分的コードを private メソッドとして抽出する。

    タイプ:

    進化タイプ:モジュール内進化

    前提:

    構成要素:

    before:

    public class Component {
    	
    	public void method() {
    		
    		// 何らかの処理
    		System.out.println("***");
    		
    		System.out.println("aaa");
    		
    		System.out.println("***");
    	}	
    }
    
    after:

    public class Component {
    	
    	public void method() {
    		
    		// 何らかの処理
    		System.out.println("***");
    		
    		privateMethod();
    
    		System.out.println("***");
    	}
    	
    	private void privateMethod() {
    		System.out.println("aaa");
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    パターン関係

    進化パス

    関連パターン

    参考文献とリソース

    その他/疑問

    ユーティリティクラスへの static メソッドの追加

    状況&動機:

    タイプ:

    before:

    public class Item {
    
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    	
    	public String getName() {
    		return name;
    	}
    }
    
    public class ItemUtils {
    	
    	public static String[] getNames(Item[] items) {
    
    		String[] names = new String[items.length];
    
    		for(int i = 0 ; i < items.length; i++) {
    			names[i] = items[i].getName();
    		}
    		
    		return names;
    	}
    }
    
    after:
    public class Item {
    
    	private String name;
    	
    	public Item(String name) {
    		this.name = name;
    	}
    	
    	public String getName() {
    		return name;
    	}
    }
    
    public interface ItemFilter {
    	public boolean accept(Item item);
    }
    
    public class ItemUtils {
    	
    	public static String[] getNames(Item[] items) {
    
    		String[] names = new String[items.length];
    
    		for(int i = 0 ; i < items.length; i++) {
    			names[i] = items[i].getName();
    		}
    		
    		return names;
    	}
    	
    	public static Item[] filterItems(Item[] items, ItemFilter filter) {
    		
    		
    		List itemList = new ArrayList();
    		
    		for(int i = 0; i < items.length; i++)  {
    
    			if ( filter.accept(items[i]) ) {
    				itemList.add(items[i]);
    			}
    		}
    		
    		return (Item[])itemList.toArray(new Item[itemList.size()]);
    	}
    }
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    実装クラスの追加

    状況&動機: interface を実装するクラスを追加する。

    タイプ:

    構成要素:

    before:

    public interface MyInterface {
    	public void method();
    }
    
    public class MyInterfaceImplA implements MyInterface {
    	
    	public void method() {
    		System.out.println("MyInterfaceImplA.method()");
    	}
    }
    
    after:
    public interface MyInterface {
    	public void method();
    }
    
    public class MyInterfaceImplA implements MyInterface {
    	
    	public void method() {
    		System.out.println("MyInterfaceImplA.method()");
    	}
    }
    
    public class MyInterfaceImplB implements MyInterface {
    	
    	public void method() {
    		System.out.println("MyInterfaceImplB.method()");
    	}
    }
    

    参考文献とリソース

    パターン関係

    進化パス

    関連パターン

    参考文献とリソース

    todo

    Filter interface の導入

    状況&動機:

    タイプ:

    before:

    public class Item {
    	
    	private String name;
    	private int price;
    	
    	public Item(String name, int price) {
    		this.name  = name;
    		this.price = price; 
    	}
    	
    	public String getName() {
    		return name;
    	}
    	
    	public int getPrice() {
    		return price;
    	}
    }
    
    public class ItemContainer {
    
    	private List items = new ArrayList();
    
    	public void addItem(Item item) {
    		items.add(item);
    	}
    	
    	public Item[] getItems() {
    		return (Item[])items.toArray(new Item[items.size()]);
    	}
    }
    
    after:
    public class Item { // 変更なし
    	
    	private String name;
    	private int price;
    	
    	public Item(String name, int price) {
    		this.name  = name;
    		this.price = price; 
    	}
    	
    	public String getName() {
    		return name;
    	}
    	
    	public int getPrice() {
    		return price;
    	}
    }
    
    public interface ItemFilter {
    	public boolean accpet(Item item);
    }
    
    public class ItemContainer {
    
    	private List items = new ArrayList();
    
    	public void addItem(Item item) {
    		items.add(item);
    	}
    	
    	public Item[] getItems() {
    		return (Item[])items.toArray(new Item[items.size()]);
    	}
    	
    	public Item[] getItems(ItemFilter filter) {
    		
    		List itemList = new ArrayList();
    		
    		Item[] allItems = getItems();
    
    		for(int i = 0; i < allItems.length; i++) {
    
    			if ( filter.accpet(allItems[i]) ) {
    				itemList.add(allItems[i]);
    			}
    		}
    		
    		return (Item[])itemList.toArray(new Item[itemList.size()]);
    	}
    }
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    アノテーションの追加

    状況&動機:

    タイプ:

    before:

    
    	after:
    public @interface MyAnnotation {
    
    }
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    メソッドの抽出によるユーティリティクラスの追加

    状況&動機:

    タイプ:

    before:

    public class MyClass {
    
    	public void method1() {
    
    		method2("abc");
    	}
    	
    	private void method2(String str) {
    	
    		System.out.println(str);
    	}
    }
    
    after:
    public class MyClass {
    
    	public void method1() {
    
    		MyUtils.method("abc");
    	}
    }
    
    public class MyUtils {
    
    	public static void method(String str) {
    		System.out.println(str);
    	}
    }
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    varargs method の追加

    状況&動機:varargs method を追加する。

    タイプ:

    進化タイプ:

    前提:

    構成要素:

    before:

    public class StringContainer {
    
    	private List srts = new ArrayList();
    
    	public void addString(String s) {
    		srts.add(s);
    	}	
    }
    
    public class StringContainerClient {
    	
    	public void method() {
    		
    		StringContainer c = new StringContainer();
    
    		c.addString("aaa");
    		c.addString("bbb");
    		c.addString("ccc");
    	}
    }
    
    after:

    public class StringContainer {
    
    	private List srts = new ArrayList();
    
    	public void addString(String s) {
    		srts.add(s);
    	}	
    
    	public void addStrings(String... ss) {
    		srts.addAll( Arrays.asList(ss) );
    	}
    }
    
    public class StringContainerClient {
    	
    	public void method() {
    		
    		StringContainer c = new StringContainer();
    
    		c.addStrings("aaa", "bbb", "ccc");
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    その他/疑問

    メソッドのパラメータの削除

    状況&動機:メソッドの不要となったパラメータを削除する。

    タイプ:

    進化タイプ:

    前提:パラメータを持つメソッドが存在する。

    構成要素:

    before:

    public class Component {
    	
    	public void method(String param) {
    		System.out.println(param);
    		System.out.println("***"); // 何かの処理
    	}
    }
    
    after:

    public class Component {
    	
    	public void method() {
    		System.out.println("***"); // 何かの処理
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    パターン関係

    進化パス

    関連パターン

    参考文献とリソース

    その他/疑問

    public メソッドのパラメータの削除

    状況&動機:public メソッドの不要となったパラメータを削除する。

    タイプ:

    進化タイプ:

    前提:パラメータを持つ public メソッドが存在する。

    構成要素:

    before:

    public class Component {
    	
    	public void method(String param) {
    		System.out.println(param);
    		System.out.println("***"); // 何かの処理
    	}
    }
    
    after:

    public class Component {
    	
    	public void method() {
    		System.out.println("***"); // 何かの処理
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    パターン関係

    進化パス

    関連パターン

    参考文献とリソース

    その他/疑問

    public な具象メソッドのパラメータの削除

    状況&動機:public な具象メソッドの不要となったパラメータを削除する。

    タイプ:

    進化タイプ:

    前提:パラメータを持つ public な具象メソッドが存在する。

    構成要素:

    before:

    public class Component {
    	
    	public void method(String param) {
    		System.out.println(param);
    		System.out.println("***"); // 何かの処理
    	}
    }
    
    after:

    public class Component {
    	
    	public void method() {
    		System.out.println("***"); // 何かの処理
    	}
    }
    

    進化リクエスタ/プロバイダ

    議論

    パターン関係

    進化パス

    関連パターン

    参考文献とリソース

    その他/疑問

    View コードの変更

    状況&動機:ビュー(見た目)の変更が必要になった。

    タイプ:

    進化タイプ:

    前提:

    before:

    public class Model {
    	
    	private String data;
    	
    	public Model(String data) {
    		this.data = data;
    	}
    	
    	public String getData() {
    		return data;
    	}
    }
    
    import java.awt.Graphics;
    
    public class View {
    	
    	private Model model;
    	
    	public View(Model model) {
    		this.model = model;
    	}
    	
    	public void draw(Graphics g) {
    		g.drawString(model.getData(), 50, 50);
    	}
    }
    
    import java.awt.Dimension;
    import java.awt.Graphics;
    
    import javax.swing.JPanel;
    
    public class MyPanel extends JPanel {
    
    	private Model model;
    	private View view;
    
    	public MyPanel() {
    		setPreferredSize(new Dimension(100, 100));
    		
    		model = new Model("aaa");
    		view  = new View(model);
    	}
    	
    	@Override
    	protected void paintComponent(Graphics g) {
    		super.paintComponent(g);
    		
    		view.draw(g);
    	}
    }
    
    import javax.swing.JFrame;
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		JFrame f = new JFrame();
    		f.add(new MyPanel());
    		
    		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		f.pack();
    		f.setVisible(true);
    	}
    
    }
    
    after:

    public class Model {
    	
    	private String data;
    	
    	public Model(String data) {
    		this.data = data;
    	}
    	
    	public String getData() {
    		return data;
    	}
    }
    
    import java.awt.Graphics;
    
    public class View {
    	
    	private Model model;
    	
    	public View(Model model) {
    		this.model = model;
    	}
    	
    	public void draw(Graphics g) {
    		g.drawString("data: " + model.getData(), 50, 50);
    	}
    }
    
    import java.awt.Dimension;
    import java.awt.Graphics;
    
    import javax.swing.JPanel;
    
    public class MyPanel extends JPanel {
    
    	private Model model;
    	private View view;
    
    	public MyPanel() {
    		setPreferredSize(new Dimension(100, 100));
    		
    		model = new Model("aaa");
    		view  = new View(model);
    	}
    	
    	@Override
    	protected void paintComponent(Graphics g) {
    		super.paintComponent(g);
    		
    		view.draw(g);
    	}
    }
    
    import javax.swing.JFrame;
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		JFrame f = new JFrame();
    		f.add(new MyPanel());
    		
    		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		f.pack();
    		f.setVisible(true);
    	}
    
    }
    

    進化リクエスタ/プロバイダ

    議論

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    ハードコードされたパラメータ設定を DI コンテナにより外部設定化

    before:

    public class Config {
    
    	private String param1;
    	private int param2;
    	
    	public void setParam1(String param1) {
    		this.param1 = param1;
    	}
    
    	public String getParam1() {
    		return param1;
    	}
    
    	public void setParam2(int param2) {
    		this.param2 = param2;
    	}
    
    	public int getParam2() {
    		return param2;
    	}
    }
    
    public class Main {
    
    	public static void main(String[] args) {
    	
    		Config config = new Config();
    
    		config.setParam1("xyz");
    		config.setParam2(100);
    	}
    }
    
    after:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
      <bean id="config" class="evo.di.Config">
        <property name="param1"><value>xyz</value></property>
        <property name="param2"><value>100</value></property>
      </bean>
    </beans>
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		BeanFactory factory = new ClassPathXmlApplicationContext("bean.xml");
    
    		Config config = (Config)factory.getBean("config");
    	}
    }
    

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

  • Spring (http://www.springframework.org/)
  • Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler (http://martinfowler.com/articles/injection.html)
  • todo

    Setter の追加

    before:

    public class Component {
    	
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
      <bean id="comp" class="evo.spring.setter.Component"/>
    </beans>
    
    after:
    public class Component {
    
    	private String name;
    	
    	public void setName(String name) {
    		this.name = name;
    	}
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
      <bean id="comp" class="evo.spring.setter.Component">
     	<property name="name"><value>my</value></property>
      </bean>
    </beans>
    

    参考文献とリソース

  • Spring (http://www.springframework.org/)
  • Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler (http://martinfowler.com/articles/injection.html)
  • todo

    Spring: value/ref のショートカット

    Spring のバージョン 1.2 からは、value element や ref element を省略できるショートカットして、value attribute や ref attribute が追加されました。詳しくは こちら

    before:

    public class MyBean {
    
    	private String value1;
    	private String value2;
    
    	public void setValue1(String value1) {
    		this.value1 = value1;
    	}
    	
    	public String getValue1() {
    		return value1;
    	}
    
    	public void setValue2(String value2) {
    		this.value2 = value2;
    	}
    
    	public String getValue2() {
    		return value2;
    	}
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    	<bean id="mybean" class="evo.spring.shortcut.MyBean">
    		
    		<property name="value1">
    			<value>aaa</value>
    		</property>
    		
    		<property name="value2">
    			<value>bbb</value>
    		</property>
    	
    	</bean>
    
    </beans>
    
    after:
    public class MyBean { // 変更なし
    
    	private String value1;
    	private String value2;
    
    	public void setValue1(String value1) {
    		this.value1 = value1;
    	}
    	
    	public String getValue1() {
    		return value1;
    	}
    
    	public void setValue2(String value2) {
    		this.value2 = value2;
    	}
    
    	public String getValue2() {
    		return value2;
    	}
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    	<bean id="mybean" class="evo.spring.shortcut.MyBean">
    		
    		<property name="value1" value="aaa"/>
    		<property name="value2" value="bbb"/>
    	
    	</bean>
    
    </beans>
    

    参考文献とリソース

  • Spring
    (http://www.springframework.org/)

  • Introduction to the Spring Framework
    (http://www.theserverside.com/articles/article.tss?l=SpringFramework)

  • Spring Reference Documentation, 3.3.3.1. Value and Ref shortcut forms
    (http://static.springframework.org/spring/docs/1.2.x/reference/beans.html#d0e937)
  • todo

    ハードコードされた Preprocess Command の外部設定化

    タイプ:リファクタリング?

    before:

    public class Data {
    	// データを編集する適当なメソッド
    }
    
    public interface PreprocessCommand {
    	public void execute(Data data);
    }
    
    public class ConcretePreprocessCommandA implements PreprocessCommand {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandA - param = " + param);
    	}
    }
    
    public class ConcretePreprocessCommandB implements PreprocessCommand  {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandB - param = " + param);
    	}
    }
    
    public class ConcretePreprocessCommandC implements PreprocessCommand  {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandC - param = " + param);
    	}
    }
    
    public class PreprocessCommandExecutor {
    	
    	private List commands = new ArrayList();
    	
    	public void addCommand(PreprocessCommand command) {
    		commands.add(command);
    	}
    	
    	public void execute(Data data) {
    		
    		for(Iterator itr = commands.iterator(); itr.hasNext(); ) {
    			( (PreprocessCommand)itr.next() ).execute(data);
    		}
    	}
    }
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		Data data = new Data();
    
    
    		ConcretePreprocessCommandA commA = new ConcretePreprocessCommandA();
    		commA.setParam("aaa");
    		
    		ConcretePreprocessCommandB commB = new ConcretePreprocessCommandB();
    		commB.setParam("bbb");
    		
    		ConcretePreprocessCommandC commC = new ConcretePreprocessCommandC();
    		commC.setParam("ccc");
    		
    
    		PreprocessCommandExecutor executor = new PreprocessCommandExecutor();
    
    		executor.addCommand(commA);
    		executor.addCommand(commB);
    		executor.addCommand(commC);
    		
    		executor.execute(data);
    	}
    }
    
    after:
    public class Data {
    	// データを編集する適当なメソッド
    }
    
    public interface PreprocessCommand {
    	public void execute(Data data);
    }
    
    public class ConcretePreprocessCommandA implements PreprocessCommand {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandA - param = " + param);
    	}
    }
    
    public class ConcretePreprocessCommandB implements PreprocessCommand  {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandB - param = " + param);
    	}
    }
    
    public class ConcretePreprocessCommandC implements PreprocessCommand  {
    
    	private String param;
    
    	public void setParam(String param) {
    		this.param = param;
    	}
    
    	public void execute(Data data) {
    		// param を利用してデータの前処理
    		System.out.println("ConcretePreprocessCommandC - param = " + param);
    	}
    }
    
    public class PreprocessCommandExecutor {
    	
    	private List commands = new ArrayList();
    	
    	public void addCommand(PreprocessCommand command) {
    		commands.add(command);
    	}
    	
    	public void setCommandList(List commandList) {
    
    		for(Iterator itr = commandList.iterator(); itr.hasNext(); ) {
    			addCommand( (PreprocessCommand)itr.next() );
    		}
    	}
    	
    	public void execute(Data data) {
    		
    		for(Iterator itr = commands.iterator(); itr.hasNext(); ) {
    			( (PreprocessCommand)itr.next() ).execute(data);
    		}
    	}
    }
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main2 {
    
    	public static void main(String[] args) {
    
    		Data data = new Data();
    
    		BeanFactory factory = new ClassPathXmlApplicationContext("bean.xml");
    
    		PreprocessCommandExecutor executor =
    			(PreprocessCommandExecutor)factory.getBean("commandExecutor");
    		
    		executor.execute(data);
    	}
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    	<bean id="commandExecutor" class="evo.command.PreprocessCommandExecutor">
    		<property name="commandList">
    			<list>
    				<ref bean="commA"/>
    				<ref bean="commB"/>
    				<ref bean="commC"/>
    			</list>	
    		</property>
    	</bean>
    
    	<bean id="commA" class="evo.command.ConcretePreprocessCommandA">
    		<property name="param" value="aaa"/>	
    	</bean>
    
    	<bean id="commB" class="evo.command.ConcretePreprocessCommandB">
    		<property name="param" value="bbb"/>	
    	</bean>
    
    	<bean id="commC" class="evo.command.ConcretePreprocessCommandC">
    		<property name="param" value="ccc"/>	
    	</bean>
    
    </beans>
    

    進化パス

    関連パターン

    参考文献とリソース

    todo

    ハードコードされた Strategy の外部設定化

    タイプ:リファクタリング?

    before:

    public interface Strategy {
    	public void execute();
    }
    
    public class ConcreteStrategyA implements Strategy {
    
    	public void execute() {
    		System.out.println("strategyA");
    	}
    }
    
    public class ConcreteStrategyB implements Strategy {
    
    	public void execute() {
    		System.out.println("strategyB");
    	}
    }
    
    public class ConcreteStrategyC implements Strategy {
    
    	public void execute() {
    		System.out.println("strategyC");
    	}
    }
    
    public class Context {
    
    	private List strategies = new ArrayList();
    
    	public void addStrategy(Strategy strategy) {
    		strategies.add(strategy);
    	}
    	
    	public void executeStrategies() {
    		for(Iterator itr = strategies.iterator(); itr.hasNext();) {
    			Strategy strategy = (Strategy)itr.next();
    			strategy.execute();
    		}
    	}
    }
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		Context context = new Context();
    
    		context.addStrategy( new ConcreteStrategyA() );
    		context.addStrategy( new ConcreteStrategyB() );
    		context.addStrategy( new ConcreteStrategyC() );
    		
    		context.executeStrategies();
    	}
    }
    
    after:
    public interface Strategy {
    	public void execute();
    }
    
    public class ConcreteStrategyA implements Strategy {
    
    	public void execute() {
    		System.out.println("strategyA");
    	}
    }
    
    public class ConcreteStrategyB implements Strategy {
    
    	public void execute() {
    		System.out.println("strategyB");
    	}
    }
    
    public class ConcreteStrategyC implements Strategy {
    
    	public void execute() {
    		System.out.println("strategyC");
    	}
    }
    
    public class Context {
    
    	private List strategies = new ArrayList();
    
    	public void addStrategy(Strategy strategy) {
    		strategies.add(strategy);
    	}
    	
    	public void setStrategies(List strategies) {
    		for(Iterator itr = strategies.iterator(); itr.hasNext();) {
    			addStrategy( (Strategy)itr.next() );
    		}
    	}
    		
    	public void executeStrategies() {
    		for(Iterator itr = strategies.iterator(); itr.hasNext();) {
    			Strategy strategy = (Strategy)itr.next();
    			strategy.execute();
    		}
    	}
    }
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		BeanFactory factory = new ClassPathXmlApplicationContext("bean.xml");
    
    		Context context = (Context)factory.getBean("context");
    		
    		context.executeStrategies();
    	}
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    	<bean id="context" class="evo.strategy2.strategy.spring.Context">
    		<property name="strategies">
    			<list>
    				<bean class="evo.strategy2.strategy.spring.ConcreteStrategyA"/>
    				<bean class="evo.strategy2.strategy.spring.ConcreteStrategyB"/>
    				<bean class="evo.strategy2.strategy.spring.ConcreteStrategyC"/>
    			</list>	
    		</property>
    	</bean>
    
    </beans>
    

    進化パス

    関連パターン

    参考文献とリソース

    todo

    ハードコードされた Strategy の外部設定化2

    状況&動機:特定の Strategy を使う前にパラメータを設定する必要がある。

    タイプ:リファクタリング

    before:

    public interface Strategy {
    	public void algorithmInterface();
    }
    
    public class ConcreteStrategy implements Strategy {
    
    	private String paramA;
    	private int paramB;
    
    	public void algorithmInterface() {
    		System.out.println("param A = " + paramA);
    		System.out.println("param B = " + paramB);
    	}
    	
    	public void setParameterA(String paramA) {
    		this.paramA = paramA;
    	}
    	
    	public void setParameterB(int paramB) {
    		this.paramB = paramB;
    	}	
    }
    
    public class Context {
    	
    	public void contextInterface(Strategy strategy) {
    		strategy.algorithmInterface();
    	}
    }
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		ConcreteStrategy strategy = new ConcreteStrategy();
    
    		strategy.setParameterA("aaa");
    		strategy.setParameterB(100);
    	
    		Context context = new Context();
    		context.contextInterface(strategy);
    	}
    }
    
    after:
    public interface Strategy { // 変更なし
    	public void algorithmInterface();
    }
    
    public class ConcreteStrategy implements Strategy { // 変更なし
    
    	private String paramA;
    	private int paramB;
    
    	public void algorithmInterface() {
    		System.out.println("param A = " + paramA);
    		System.out.println("param B = " + paramB);
    	}
    	
    	public void setParameterA(String paramA) {
    		this.paramA = paramA;
    	}
    	
    	public void setParameterB(int paramB) {
    		this.paramB = paramB;
    	}	
    }
    
    public class Context { // 変更なし
    	
    	public void contextInterface(Strategy strategy) {
    		strategy.algorithmInterface();
    	}
    }
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
    
    	public static void main(String[] args) throws Exception {
    
    		BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml");
    
    		ConcreteStrategy strategy = (ConcreteStrategy)factory.getBean("strategy");
    	
    		Context context = new Context();
    		context.contextInterface(strategy);
    	}
    }
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    
      <bean id="strategy" class="evo.spring.externalize.strategy.after.ConcreteStrategy">
        <property name="parameterA" value="aaa"/>
        <property name="parameterB" value="100"/>
      </bean>
    
    </beans>
    
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    Injected クラスの追加

    状況&動機:

    タイプ:

    before:

    
    	after:
    public class InjectedClass {
    	
    	private String name;
    	
    	public void method() {
    		System.out.println("name : " + name);
    	}
    	
    	public void setName(String name) {
    		this.name = name;
    	}
    }
    
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @author asato
     */
    public class Main {
    
    	public static void main(String[] args) throws Exception {
    
    		BeanFactory factory = new ClassPathXmlApplicationContext("beans.xml");
    
    
    		InjectedClass injected = (InjectedClass)factory.getBean("injected");
    		injected.method();
    	}
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    
      <bean id="injected" class="evo.add_injected_class.after.InjectedClass">
        <property name="name" value="aaa"/>
      </bean>
    
    </beans>
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    Injected Setter Method の追加

    状況&動機:

    タイプ:リファクタリング

    before:

    public class MyLogger {
    	
    	public void log(String str) {
    		System.out.println(str);
    	}
    }
    
    after:
    public class MyLogger {
    	
    	private boolean enabled;
    	
    	public void log(String str) {
    		
    		if (enabled) {
    			System.out.println(str);
    		}
    	}
    	
    	public void setEnabled(boolean enabled) {
    		this.enabled = enabled;
    	}
    }
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    
      <bean id="logger" class="evo.spring.add_injected_setter_method.after.MyLogger">
        <property name="enabled" value="true"/>
      </bean>
    
    </beans>
    

    参考文献とリソース

    進化パス

    パターン関係

    関連パターン

    参考文献とリソース

    todo

    参考文献とリソース

    参考文献:

    リソース:

    更新履歴

    todo