2015年6月25日木曜日

1-19. Observer パターン

1つのオブジェクトの状態が変化したとき、全ての依存するオブジェクトに通知され自動的に
更新されるように、オブジェクト間の1対多数の依存関係を定義します。



ある Subject の状態が変化したときに別の Subject の状態を変更したい場合、
一度 Observer に通知し、Observer が関係する Subject の変更を行います。
void client() throws InterruptedException {
	ConcreteSubject subject = new ConcreteSubject();
	Observer observer = new ConcreteObserver();
	subject.addObserver(observer);
	// スレッド内で実行
	Thread subjectThread = new Thread(subject);
	subjectThread.start();
	subjectThread.join();
}
interface Subject {
	public void addObserver(Observer observer);
	public void _notify();
}
interface Observer {
	public void update();
}
class ConcreteSubject implements Subject, Runnable {
	private List<Observer> observers = new CopyOnWriteArrayList<Observer>();
	public void addObserver(Observer observer) {
		this.observers.add(observer);
	}
	public void _notify() {
		for (Observer observer: this.observers) {
			observer.update();
		}
	}
	public void run() {
		this._notify();
	}
}
class ConcreteObserver implements Observer {
	public void update() {
		System.out.println("update");
	}
}
実行結果:
update
サンプルプログラムは、ダイアログの例です。
ここではダイアログが Observer、ボタン・テキストボックス・リストといった部品が Subject に相当
します。
テキストを入力して Add ボタンを押すとリストに追加されますが、Subject である部品がほかの
部品を直接操作することはせず、一度 Observer であるダイアログを介して操作を行います。
 Add ボタンを押す -> Button.processActionEvent(Subject.needsAdvice() に相当)
  -> Dialog_ConcreteObserver.actionPerformed()(ConcreteObserver.consultation() に相当)
  -> テキストボックスのテキストを取得し、リストに追加(ConcreteSubject.advice() に相当)
ボタン->ダイアログへの呼び出しは、標準ライブラリの Button クラスが行っています。
Button.addActionListener() によって ActionListener インターフェースを実装したダイアログを
登録することで、ボタンでアクションイベントが発生した際に
Dialog_ConcreteObserver.actionPerformed() が呼び出されます。
また、addWindowsListener ... の部分も Observer パターンで、WindowAdapter が Observer、
ダイアログが Subject に相当します。
実行結果:
dialog.actionPerformed(): from button0
dialog.actionPerformed(): from button0
dialog.actionPerformed(): from list0
dialog.actionPerformed(): from button1

package design_pattern;

import java.awt.Button;
import java.awt.Component;
import java.awt.Frame;
import java.awt.List;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * Observer
 * GoF, Code Complete
 * 
 * 1つのオブジェクトの状態が変化したとき、全ての依存するオブジェクトに通知され自動的に
 * 更新されるように、オブジェクト間の1対多数の依存関係を定義します。
 * 
 * @author 2015/06/21 matsushima
 */
public class ObserverSample {

	public static void main(String[] args) throws InterruptedException {
		// ConcreteObserver: dialog
		Dialog_ConcreteObserver dialog = new ObserverSample().new Dialog_ConcreteObserver();
		dialog.setVisible(true);
	}

	/** ConcreteObserver: ダイアログ。 */
	public class Dialog_ConcreteObserver extends Frame implements ActionListener {
		private static final long serialVersionUID = 1L;
		private TextField text;
		private List list;
		private Button buttonAdd, buttonClose;
		public Dialog_ConcreteObserver() {
			super("ObserverSample");
			setLayout(null);
			addWindowListener(new WindowAdapter() { // Subject.addObserver に相当。
				public void windowClosing(WindowEvent e) { // ConcreteObserver.update() に相当。
					System.exit(0);
				}
			});
			this.setSize(290, 230);
			// ConcreteSubject: text
			text = new TextField();
			text.setBounds(20, 50, 200, 25);
			text.addActionListener(this); // Subject.addObserver に相当。
			this.add(text);
			// ConcreteSubject: buttonAdd
			buttonAdd = new Button("Add");
			buttonAdd.setBounds(220, 50, 50, 25);
			buttonAdd.addActionListener(this); // Subject.addObserver に相当。
			this.add(buttonAdd);
			// ConcreteSubject: list
			list = new List(5);
			list.setBounds(20, 80, 250, 100);
			list.addActionListener(this); // Subject.addObserver に相当。
			this.add(list);
			// ConcreteSubject: buttonClose
			buttonClose = new Button("Close");
			buttonClose.setBounds(220, 185, 50, 25);
			buttonClose.addActionListener(this); // Subject.addObserver に相当。
			this.add(buttonClose);
		}
		/** ConcreteObserver.update() に相当。 */
		public void actionPerformed(ActionEvent e) {
			//System.out.println(e);
			System.out.println("dialog.actionPerformed(): from " + ((Component)e.getSource()).getName());
			if (this.buttonAdd == e.getSource() // buttonAdd click
					|| this.text == e.getSource()) { // text enter
				if (!this.text.getText().isEmpty()) {
					this.list.add(this.text.getText());
				}
			} else if (this.list == e.getSource()) { // list double-click
				this.text.setText(this.list.getSelectedItem());
			} else if (this.buttonClose == e.getSource()) { // buttonClose click
				this.dispose();
			}
		}
	}
	
	///** Observer: ConcreteSubject からのイベント受け取り。 */
	//public interface ActionListener extends EventListener {
	//    /** Observer.update() に相当。 */
	//    public void actionPerformed(ActionEvent e);
	//}

	///** ConcreteSubject: ボタン。 */
	//public class Button(or TextField, List) extends Component {
	//	/** ConcreteSubject.needsAdvice() に相当。 */
	//	protected void processActionEvent(ActionEvent e) {
	//	    ActionListener listener = actionListener;
	//	    if (listener != null) {
	//	        listener.actionPerformed(e); // ConcreteObserver.update() に相当
	//	    }
	//	}
	//}
}

2015年6月21日日曜日

1-18. Memento パターン

カプセル化を維持したまま、あとでその状態に戻せるようにオブジェクトの内部状態を保存して
外面化します。



originator に操作を行っている途中で内部状態を保存したい場合、originator.createMemento()
によって memento を要求し、caretaker.addMemento() で保存します。内部状態は Memento
によってカプセル化されているので、Originator のカプセル化は維持されています。
保存した時点の状態に戻したい場合は、caretaker.getMemento() で保存した memento を取得し、
originator.setMemento() で内部状態を復帰します。
void client() {
	Caretaker caretaker = new Caretaker();
	Originator originator = new Originator();
	originator.operation("aaa");
	originator.operation("bbb");
	System.out.println("// 内部状態要求、保存");
	caretaker.addMemento(originator.createMemento()); // 0
	originator.operation("ccc");
	System.out.println("// 内部状態要求、保存");
	caretaker.addMemento(originator.createMemento()); // 1
	originator.operation("ddd");
	originator.operation("eee");
	System.out.println(originator.result());
	System.out.println("// 内部状態取得、復帰");
	originator.setMemento(caretaker.getMemento(1));
	System.out.println(originator.result());
}
class Originator {
	private String state;
	public Memento createMemento() {
		Memento memento = new Memento();
		memento.setState(this.state);
		return memento;
	}
	public void setMemento(Memento memento) { this.state = memento.getState(); }
	public void operation(String state) { this.state = state; }
	public String result() { return this.state; }
}
class Memento {
	private String state;
	public void setState(String state) { this.state = state; }
	public String getState() { return this.state; }
}
class Caretaker {
	private LinkedList<Memento> mementos = new LinkedList<>();
	public void addMemento(Memento memento) { this.mementos.add(memento); }
	public Memento getMemento(int index) { return this.mementos.get(index); }
}
出力結果:
// 内部状態要求、保存
// 内部状態要求、保存
eee
// 内部状態取得、復帰
ccc

サンプルプログラムとして、簡易的なエディタを示します。
指定した状態に保存、復帰しています。
実行結果:
// 内部状態要求、保存
// 内部状態要求、保存
Hello World!!
// 内部状態取得、復帰
Hello World
package design_pattern;

import java.util.LinkedList;

/**
 * Memento パターン
 * GoF
 * 
 * カプセル化を維持したまま、あとでその状態に戻せるようにオブジェクトの内部状態を保存して
 * 外面化します。
 * 
 * @author 2015/05/30 matsushima
 */
public class MementoSample {

	/**
	 * main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		new MementoSample().client();
	}
	
	/**
	 * client
	 */
	void client() {
		Editor_Caretaker caretaker = new Editor_Caretaker();
		Editor_Originator editor = new Editor_Originator();
		editor.input("Hello");
		editor.input(" ");
		System.out.println("// 内部状態要求、保存");
		caretaker.addMemento(editor.createMemento()); // 0
		editor.input("World");
		System.out.println("// 内部状態要求、保存");
		caretaker.addMemento(editor.createMemento()); // 1
		editor.input("!!");
		System.out.println(editor.getText());
		System.out.println("// 内部状態取得、復帰");
		editor.setMemento(caretaker.getMemento(1));
		System.out.println(editor.getText());
	}

	/** Originator: エディタ。 */
	class Editor_Originator {
		private StringBuilder text = new StringBuilder();
		public Editor_Memento createMemento() {
			Editor_Memento memento = new Editor_Memento();
			memento.setState(this.text.toString());
			return memento;
		}
		public void setMemento(Editor_Memento memento) {
			this.text.setLength(0);
			this.text.append(memento.getState());
		}
		public void input(String text) {
			this.text.append(text);
		}
		public String getText() {
			return this.text.toString();
		}
	}
	/** Memento: エディタの保存状態。 */
	class Editor_Memento {
		private String state;
		public void setState(String state) {
			this.state = state;
		}
		public String getState() {
			return this.state;
		}
	}
	/** Caretaker: エディタの管理人。 */
	class Editor_Caretaker {
		private LinkedList<Editor_Memento> mementos = new LinkedList<>();
		public void addMemento(Editor_Memento memento) {
			this.mementos.add(memento);
		}
		public Editor_Memento getMemento(int index) {
			return this.mementos.get(index);
		}
	}
}

2015年6月20日土曜日

1-17. Mediator パターン

オブジェクト間の相互作用をカプセル化するオブジェクトを定義します。
Mediator は相互参照による結合度を弱め、それにより独立させることができます。



クラスが増えてオブジェクト間の相互参照が増えると、プログラムが複雑になるだけでなく、
単独でクラスを再利用することが難しくなります。
Mediator パターンでは、Mediator を介することで Colleague 間の結合度を弱めます。
void client() {
	Mediator mediator = new ConcreteMediator();
	Colleague colleague1 = new ConcreteColleague(mediator, "colleague1");
	mediator.addColleague(colleague1);
	Colleague colleague2 = new ConcreteColleague(mediator, "colleague2");
	mediator.addColleague(colleague2);
	colleague1.needsAdvice();
	colleague2.needsAdvice();
}
interface Mediator {
	public void addColleague(Colleague colleague);
	public void consultation(Colleague colleague);
}
interface Colleague {
	public void needsAdvice();
	public void advice(Colleague colleague);
}
class ConcreteMediator implements Mediator {
	private LinkedList<Colleague> colleagues = new LinkedList<>();
	public void addColleague(Colleague colleague) {
		this.colleagues.add(colleague);
	}
	public void consultation(Colleague colleague) {
		System.out.println("mediator.consultation(): from " + colleague);
		for (Colleague c: this.colleagues) {
			c.advice(colleague);
		}
	}
}
class ConcreteColleague implements Colleague {
	private Mediator mediator;
	private String name;
	public ConcreteColleague(Mediator mediator, String name) {
		this.mediator = mediator;
		this.name = name;
	}
	public void needsAdvice() {
		this.mediator.consultation(this);
	}
	public void advice(Colleague colleague) {
		System.out.println(this + ".advice(): from " + colleague);
	}
	public String toString() {
		return this.name;
	}
}
出力結果:
mediator.consultation(): from colleague1
colleague1.advice(): from colleague1
colleague2.advice(): from colleague1
mediator.consultation(): from colleague2
colleague1.advice(): from colleague2
colleague2.advice(): from colleague2
サンプルプログラムは、ダイアログの例です。
ここではダイアログが Mediator、ボタン・テキストボックス・リストといった部品が Colleague に相当
します。
テキストを入力して Add ボタンを押すとリストに追加されますが、Colleague である部品がほかの
部品を直接操作することはせず、一度 Mediator であるダイアログを介して操作を行います。
Add ボタンを押す -> Button.processActionEvent(Colleague.needsAdvice() に相当)
-> Dialog_ConcreteMediator.actionPerformed()(ConcreteMediator.consultation() に相当)
-> テキストボックスのテキストを取得し、リストに追加(ConcreteColleague.advice() に相当)
ボタン->ダイアログへの呼び出しは、標準ライブラリの Button クラスが行っています。
Button.addActionListener() によって ActionListener インターフェースを実装したダイアログを
登録することで、ボタンでアクションイベントが発生した際に
Dialog_ConcreteMediator.actionPerformed() が呼び出されます。
実行結果:
dialog.actionPerformed(): from button0
dialog.actionPerformed(): from button0
dialog.actionPerformed(): from list0
dialog.actionPerformed(): from button1

package design_pattern;

import java.awt.Button;
import java.awt.Component;
import java.awt.Frame;
import java.awt.List;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * Mediator
 * GoF
 * 
 * オブジェクト間の相互作用をカプセル化するオブジェクトを定義します。
 * Mediator は相互参照による結合度を弱め、それにより独立させることができます。
 * 
 * @author 2015/05/30 matsushima
 *
 */
public class MediatorSample {
	/**
	 * main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		// ConcreteMediator: dialog
		Dialog_ConcreteMediator dialog = new MediatorSample().new Dialog_ConcreteMediator();
		dialog.setVisible(true);
	}

	/** ConcreteMediator: ダイアログ。 */
	public class Dialog_ConcreteMediator extends Frame implements ActionListener {
		private static final long serialVersionUID = 1L;
		private TextField text;
		private List list;
		private Button buttonAdd, buttonClose;
		public Dialog_ConcreteMediator() {
			super("MediatorSample");
			setLayout(null);
			addWindowListener(new WindowAdapter() {
				public void windowClosing(WindowEvent e) { System.exit(0); }
			});
			this.setSize(290, 230);
			// ConcreteColleague: text
			text = new TextField();
			text.setBounds(20, 50, 200, 25);
			text.addActionListener(this);
			this.add(text); // Mediator.addColleague に相当。
			// ConcreteColleague: buttonAdd
			buttonAdd = new Button("Add");
			buttonAdd.setBounds(220, 50, 50, 25);
			buttonAdd.addActionListener(this);
			this.add(buttonAdd); // Mediator.addColleague に相当。
			// ConcreteColleague: list
			list = new List(5);
			list.setBounds(20, 80, 250, 100);
			list.addActionListener(this);
			this.add(list); // Mediator.addColleague に相当。
			// ConcreteColleague: buttonClose
			buttonClose = new Button("Close");
			buttonClose.setBounds(220, 185, 50, 25);
			buttonClose.addActionListener(this);
			this.add(buttonClose); // Mediator.CloseColleague に相当。
		}
		/** ConcreteMediator.consultation() に相当。 */
		public void actionPerformed(ActionEvent e) {
			//System.out.println(e);
			System.out.println("dialog.actionPerformed(): from " + ((Component)e.getSource()).getName());
			if (this.buttonAdd == e.getSource() // buttonAdd click
					|| this.text == e.getSource()) { // text enter
				if (!this.text.getText().isEmpty()) {
					this.list.add(this.text.getText());
				}
			} else if (this.list == e.getSource()) { // list double-click
				this.text.setText(this.list.getSelectedItem());
			} else if (this.buttonClose == e.getSource()) { // buttonClose click
				this.dispose();
			}
		}
	}
	
	///** Mediator: ConcreteColleague からのイベント受け取り。 */
	//public interface ActionListener extends EventListener {
	//    /** Mediator.consultation() に相当。 */
	//    public void actionPerformed(ActionEvent e);
	//}

	///** ConcreteColleague: ボタン。 */
	//public class Button(or TextField, List) extends Component {
	//	/** ConcreteColleague.needsAdvice() に相当。 */
	//	protected void processActionEvent(ActionEvent e) {
	//	    ActionListener listener = actionListener;
	//	    if (listener != null) {
	//	        listener.actionPerformed(e); // ConcreteMediator.consultation() に相当
	//	    }
	//	}
	//}
}

2015年6月19日金曜日

1-16. Iterator パターン

集合オブジェクトの内部を公開せずに、要素への順次にアクセスする方法を提供します。



ConcreteAggregate は String 配列の集合オブジェクトを持ちますが、Iterator のインターフェース
によってそれを意識せずに順次アクセスすることができます。
void client() {
	Aggregate aggregate = new ConcreteAggregate("aaa", "bbb", "ccc");
	System.out.println("// Iterator 未使用");
	String[] list = ((ConcreteAggregate)aggregate).list;
	for (int i = 0; i < list.length; ++ i) {
		System.out.print(list[i] + ", ");
	}
	System.out.println();
	System.out.println("// Iterator 使用");
	Iterator iterator = aggregate.iterator();
	while (iterator.hasNext()) {
		System.out.print(iterator.next() + ", ");
	}
	System.out.println();
}
interface Aggregate {
	public Iterator iterator();
}
interface Iterator {
	public boolean hasNext();
	public String next();
}
class ConcreteAggregate implements Aggregate {
	public String[] list;
	public ConcreteAggregate(String... strings) {
		this.list = strings;
	}
	public Iterator iterator() {
		return new ConcreteIterator(this);
	}
}
class ConcreteIterator implements Iterator {
	private ConcreteAggregate aggregate;
	private int index = 0;
	public ConcreteIterator(ConcreteAggregate aggregate) {
		this.aggregate = aggregate;
	}
	public boolean hasNext() {
		return (this.index < this.aggregate.list.length);
	}
	public String next() {
		if (!this.hasNext()) {
			throw new NoSuchElementException();
		}
		return this.aggregate.list[this.index ++];
	}
}
出力結果:
// Iterator 未使用
aaa, bbb, ccc, 
// Iterator 使用
aaa, bbb, ccc, 
サンプルプログラムとして、DOM ノードの子供のノードを Iterator パターンで実装する例を示し
ます。
Iterator インターフェースとして標準ライブラリの Iterator インターフェースを、
同様に Aggregate は Iterable を使用しています。また、Iterable インターフェースを実装する
ことで、順次アクセスに foreach 文を使用することができます。
出力結果:
// Iterator パターン未使用
<root>
  <entry><name>Google</name><URL>http://www.google.co.jp/</URL></entry>
  <entry><name>Yahoo</name><URL>http://www.yahoo.co.jp/</URL></entry>
  <entry><name>bing</name><URL>http://www.bing.com/</URL></entry>
</root>
// Iterator パターン使用
<root>
  <entry><name>Google</name><URL>http://www.google.co.jp/</URL></entry>
  <entry><name>Yahoo</name><URL>http://www.yahoo.co.jp/</URL></entry>
  <entry><name>bing</name><URL>http://www.bing.com/</URL></entry>
</root>
// Iterator パターン使用 + foreach
<root>
  <entry><name>Google</name><URL>http://www.google.co.jp/</URL></entry>
  <entry><name>Yahoo</name><URL>http://www.yahoo.co.jp/</URL></entry>
  <entry><name>bing</name><URL>http://www.bing.com/</URL></entry>
</root>
package design_pattern;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * Iterator
 * GoF, Code Complete
 * 
 * 集合オブジェクトの内部を公開せずに、要素への順次にアクセスする方法を提供します。
 * 
 * @author 2015/05/29 matsushima
 */
public class IteratorSample {
	/**
	 * main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		new IteratorSample().client();
	}
	/**
	 * client
	 */
	void client() {
		Document_ConcreteAggregate document = new Document_ConcreteAggregate();
		Element_ConcreteAggregate root = document.createElement("root");
		document.appendChild(root);
		root.appendChild(document.createTextNode("\n  "));
		Element_ConcreteAggregate element;
		root.appendChild(element = document.createElement("entry"));
		element.appendChild(document.createElement("name"))
				.appendChild(document.createTextNode("Google"));
		element.appendChild(document.createElement("URL"))
				.appendChild(document.createTextNode("http://www.google.co.jp/"));
		root.appendChild(document.createTextNode("\n  "));
		root.appendChild(element = document.createElement("entry"));
		element.appendChild(document.createElement("name"))
				.appendChild(document.createTextNode("Yahoo"));
		element.appendChild(document.createElement("URL"))
				.appendChild(document.createTextNode("http://www.yahoo.co.jp/"));
		root.appendChild(document.createTextNode("\n  "));
		root.appendChild(element = document.createElement("entry"));
		element.appendChild(document.createElement("name"))
				.appendChild(document.createTextNode("bing"));
		element.appendChild(document.createElement("URL"))
				.appendChild(document.createTextNode("http://www.bing.com/"));
		root.appendChild(document.createTextNode("\n"));
		System.out.println("// Iterator パターン未使用");
		printNode("node", document.getDocumentElement());
		System.out.println();
		System.out.println("// Iterator パターン使用");
		printNode("iterator", document.getDocumentElement());
		System.out.println();
		System.out.println("// Iterator パターン使用 + foreach");
		printNode("iterable", document.getDocumentElement());
	}
	/**
	 * ノードを出力。
	 */
	void printNode(String method, Node_Aggregate node) {
		if (node instanceof Text_ConcreteAggregate) {
			System.out.print(node.getNodeValue());
		} else {
			System.out.print("<" + node.getNodeName() + ">");
		}
		if ("node".equals(method)) {
			for (Node_Aggregate child = node.getFirstChild();
					null != child; child = child.getNextSibling()) {
				this.printNode(method, child);
			}
		} else if ("iterator".equals(method)) {
			for (Iterator<Node_Aggregate> it = node.iterator(); it.hasNext(); ) {
				this.printNode(method, it.next());
			}
		} else if ("iterable".equals(method)) {
			for (Node_Aggregate child: node) {
				this.printNode(method, child);
			}
		}
		if (node instanceof Text_ConcreteAggregate) {
		} else {
			System.out.print("</" + node.getNodeName() + ">");
		}
	}

	/*
	// Iterator
	package java.util;
	public interface Iterator<E> {
	    boolean hasNext();
	    E next();
	    void remove();
	}
	// Aggregate
	package java.lang;
	import java.util.Iterator;
	public interface Iterable<T> {
	    Iterator<T> iterator();
	}
	*/
	/** Aggregate: Node インターフェース。 */
	public interface Node_Aggregate extends Iterable<Node_Aggregate> {
		public String getNodeName();
		public String getNodeValue();
		public Node_Aggregate getFirstChild();
		public Node_Aggregate getNextSibling();
		public Node_Aggregate appendChild(Node_Aggregate node);
		//public Iterator<Node> iterator(); // Iterable<Node>
	}
	/** ConcreteAggregate: Node 抽象クラス。 */
	public abstract class AbstractNode_ConcreteAggregate implements Node_Aggregate {
		private Node_Aggregate firstChild = null, lastChild = null, nextSibling = null;
		public Node_Aggregate getFirstChild() { return this.firstChild; }
		public Node_Aggregate getNextSibling() { return this.nextSibling; }
		public Node_Aggregate appendChild(Node_Aggregate node) {
			if (null == this.firstChild) { this.firstChild = node; }
			if (null != this.lastChild) {
				((AbstractNode_ConcreteAggregate)this.lastChild).nextSibling = node; }
			this.lastChild = node;
			return node;
		}
		public Iterator<Node_Aggregate> iterator() {
			return new Node_ConcreteIterator();
		}
		/** ConcreteIterator: Node の Iterator。 */
		private class Node_ConcreteIterator implements Iterator<Node_Aggregate> {
			private Node_Aggregate node = firstChild;
			public boolean hasNext() {
				return (null != this.node);
			}
			public Node_Aggregate next() {
				if (!this.hasNext()) {
					throw new NoSuchElementException();
				}
				Node_Aggregate result = this.node;
				this.node = this.node.getNextSibling();
				return result;
			}
			public void remove() {
				throw new UnsupportedOperationException();
			}
		}
	}
	/** ConcreteAggregate: Text。 */
	public class Text_ConcreteAggregate extends AbstractNode_ConcreteAggregate {
		private String value;
		public Text_ConcreteAggregate(String value) { this.value = value; }
		public String getNodeName() { return "#text"; }
		public String getNodeValue() { return this.value; }
	}
	/** ConcreteAggregate: Element。 */
	public class Element_ConcreteAggregate extends AbstractNode_ConcreteAggregate {
		private String name;
		public Element_ConcreteAggregate(String name) { this.name = name; }
		public String getNodeName() { return this.name; }
		public String getNodeValue() { return null; }
	}
	/** ConcreteAggregate: Document。 */
	public class Document_ConcreteAggregate extends AbstractNode_ConcreteAggregate {
		public String getNodeName() { return "#document"; }
		public String getNodeValue() { return null; }
		public Element_ConcreteAggregate getDocumentElement() {
			return (Element_ConcreteAggregate)this.getFirstChild();
		}
		public Element_ConcreteAggregate createElement(String name) {
			return new Element_ConcreteAggregate(name);
		}
		public Text_ConcreteAggregate createTextNode(String value) {
			return new Text_ConcreteAggregate(value);
		}
	}
}

2015年6月16日火曜日

1-15. Interpreter パターン

与えられた言語に対して、その文法表現とインタープリタを定義します。

サンプルとして数式を解釈する例を示します。
文法表現を以下のように定義します。
expression ::= number | operation | '(' expression ')'
operation ::= expression operator expression
operator ::= '+' | '-' | '*' | '/' | '%' 
number ::= { '0' | ... | '9' } +
実行結果:
expression=1+(2-3)*4/5%6
exprList=[1.0, 2.0, 3.0, -, 4.0, *, +, 5.0, /, 6.0, %]
-0.6
package design_pattern;

import java.util.Deque;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Interpreter パターン
 * GoF
 * 
 * 与えられた言語に対して、その文法表現とインタープリタを定義します。
 * 
 * サンプルとして数式を解釈する例を示します。
 * 文法表現を以下のように定義します。
 * <pre>
 * expression ::= number | operation | '(' expression ')'
 * operation ::= expression operator expression
 * operator ::= '+' | '-' | '*' | '/' | '%' 
 * number ::= { '0' | ... | '9' } +
 * </pre>
 * 実行結果:
 * <pre>
 * expression=1+(2-3)*4/5%6
 * exprList=[1.0, 2.0, 3.0, -, 4.0, *, +, 5.0, /, 6.0, %]
 * -0.6
 * </pre>
 * 
 * @author 2015/05/29 matsushima
 */
public class InterpreterSample {
	/**
	 * main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Parser parser = new InterpreterSample().new Parser();
		parser.parse(args.length >= 1 ? args[0] : "1+(2-3)*4/5%6");
		double value = parser.interpret();
		System.out.println(value);
	}
	/** 数式のパーサ。 */
	public class Parser {
		private Pattern pattern = Pattern.compile("(\\d+)|([+\\-*/%(])|(\\))");
		private LinkedList<Expr> exprList; // パースした数式の BNF リスト
		/** 数式のパース。 */
		public void parse(String expression) {
			System.out.println("expression=" + expression);
			exprList = new LinkedList<>();
			Deque<OperatorExpr> operStack = new LinkedList<>(); // 演算子スタック
			Matcher matcher = pattern.matcher(expression);
			while (matcher.find()) {
				String token = matcher.group();
				if (null != matcher.group(1)) { // 123
					// BNF リストに追加
					this.exprList.add(new NumberExpr(token));
				} else if (null != matcher.group(2)) { // +-*/%(
					OperatorExpr expr = new OperatorExpr(token);
					if (!"(".equals(token)
							&& !operStack.isEmpty()
							&& expr.priority <= operStack.peek().priority) {
						// 優先順位の高い演算子を取り出し
						do {
							this.exprList.add(operStack.pop());
						} while (!operStack.isEmpty()
								&& expr.priority >= operStack.peek().priority);
					}
					// BNF リストに追加
					operStack.push(expr);
				} else if (null != matcher.group(3)) { // )
					// "(" まで演算子を取り出し
					while (!((OperatorExpr)operStack.peek()).operator.equals("(")) {
						this.exprList.add(operStack.pop());
					}
					operStack.pop();
				}
			}
			this.exprList.addAll(operStack);
			System.out.println("exprList=" + this.exprList);
		}
		/** パースした数式の計算。 */
		public double interpret() {
			Deque<Double> valueStack = new LinkedList<>();
			for (Expr expr: this.exprList) {
				expr.interpret(valueStack);
			}
			return valueStack.pop();
		}
	}
	/** expression のインターフェース。 */
	public abstract class Expr {
		public abstract void interpret(Deque<Double> valueStack);
	}
	/** number。 */
	public class NumberExpr extends Expr {
		public double value;
		public NumberExpr(String value) {
			this.value = Double.valueOf(value);
		}
		@Override
		public void interpret(Deque<Double> valueStack) {
			valueStack.push(this.value);
		}
		@Override
		public String toString() {
			return String.valueOf(this.value);
		}
	}
	/** operator。 */
	public class OperatorExpr extends Expr {
		public String operator;
		public int priority;
		public OperatorExpr(String str) {
			this.operator = str;
			switch (this.operator) {
			case "+": this.priority = 1; break;
			case "-": this.priority = 1; break;
			case "*": this.priority = 2; break;
			case "/": this.priority = 2; break;
			case "%": this.priority = 2; break;
			case "(": this.priority = 0; break;
			default: throw new RuntimeException(this.operator);
			}
		}
		@Override
		public void interpret(Deque<Double> valueStack) {
			double value1 = valueStack.pop();
			double value2 = valueStack.pop();
			switch (this.operator) {
			case "+": valueStack.push(value2 + value1); break;
			case "-": valueStack.push(value2 - value1); break;
			case "*": valueStack.push(value2 * value1); break;
			case "/": valueStack.push(value2 / value1); break;
			case "%": valueStack.push(value2 % value1); break;
			default: throw new RuntimeException(this.operator);
			}
		}
		@Override
		public String toString() {
			return this.operator;
		}
	}
}

2015年6月15日月曜日

1-14. Command パターン

リクエストをオブジェクトにカプセル化することで、リクエストの情報はパラメータされ、
また取り消し操作をサポートします。



client はリクエストに応じた ConcreteCommand オブジェクトを作成します。
リクエストは client -> Invoker -> ConcreteCommand -> Receiver の順に処理されます。
また、Invoker では Command の保存処理が行われます。
取り消し操作は Invoker に保存された Command を取り出し、同様に処理されます。
void client() {
	Invoker invoker = new Invoker();
	invoker.execute(new ConcreteCommandA());
	invoker.execute(new ConcreteCommandB());
	invoker.undo();
}
interface Command {
	public void execute(Receiver receiver);
	public void undo(Receiver receiver);
}
class ConcreteCommandA implements Command {
	public void execute(Receiver receiver) { receiver.actionA(); }
	public void undo(Receiver receiver) { receiver.undoA(); }
}
class ConcreteCommandB implements Command {
	public void execute(Receiver receiver) { receiver.actionB(); }
	public void undo(Receiver receiver) { receiver.undoB(); }
}
class Receiver {
	public void actionA() { System.out.println("actionA"); }
	public void actionB() { System.out.println("actionB"); }
	public void undoA() { System.out.println("undoA"); }
	public void undoB() { System.out.println("undoB"); }
}
class Invoker {
	private LinkedList<Command> commands = new LinkedList<>();
	private Receiver receiver = new Receiver();
	public void execute(Command command) {
		this.commands.push(command);
		command.execute(this.receiver);
	}
	public void undo() {
		this.commands.pop().undo(this.receiver);
	}
}
実行結果:
actionA
actionB
undoB

サンプルプログラムとして、簡易的なエディタを示します。
テキスト入力、カーソル移動といった操作を Command 化しています。
実行結果:
// 直接実行
EditorReceiver.input(): hello 
EditorReceiver.input(): japan
EditorReceiver.backspace(): 5
EditorReceiver.input(): word
EditorReceiver.cursor(): -1
EditorReceiver.input(): l
hello world
// Command パターン
EditorReceiver.input(): hello 
EditorReceiver.input(): japan
EditorInvoker.undo(): input[japan]
EditorReceiver.backspace(): 5
EditorInvoker.execute(): remove undo -> input[japan]
EditorReceiver.input(): word
EditorReceiver.cursor(): -1
EditorReceiver.input(): l
hello world
package design_pattern;

import java.util.LinkedList;
import java.util.ListIterator;

/**
 * Command
 * GoF
 * リクエストをオブジェクトにカプセル化することで、リクエストの情報はパラメータされ、
 * また取り消し操作をサポートします。
 * 
 * @author 2015/05/28 matsushima
 */
public class CommandSample {
	/**
	 * main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		new CommandSample().client();
	}
	/**
	 * Client
	 */
	void client() {
		System.out.println("// 直接実行");
		EditorReceiver receiver = new EditorReceiver();
		receiver.input("hello ");
		receiver.input("japan");
		receiver.backspace(5);
		receiver.input("word");
		receiver.cursor(-1);
		receiver.input("l");
		System.out.println(receiver.result());
		System.out.println("// Command パターン");
		EditorInvoker invoker = new EditorInvoker();
		invoker.execute(new InputConcreteCommand("hello "));
		invoker.execute(new InputConcreteCommand("japan"));
		invoker.undo();
		invoker.execute(new InputConcreteCommand("word"));
		invoker.execute(new CursorConcreteCommand(-1));
		invoker.execute(new InputConcreteCommand("l"));
		System.out.println(invoker.result());
	}

	/** エディタのコマンドのインターフェース。 */
	public interface EditorCommand {
		public void execute(EditorReceiver receiver);
		public void undo(EditorReceiver receiver);
	}
	/** テキスト入力コマンド。 */
	public class InputConcreteCommand implements EditorCommand {
		private String text;
		public InputConcreteCommand(String text) {
			this.text = text;
		}
		public void execute(EditorReceiver receiver) {
			receiver.input(this.text);
		}
		public void undo(EditorReceiver receiver) {
			receiver.backspace(this.text.length());
		}
		public String toString() {
			return "input[" + this.text + "]";
		}
	}
	/** カーソル入力コマンド。 */
	public class CursorConcreteCommand implements EditorCommand {
		private int distance;
		public CursorConcreteCommand(int distance) {
			this.distance = distance;
		}
		public void execute(EditorReceiver receiver) {
			receiver.cursor(this.distance);
		}
		public void undo(EditorReceiver receiver) {
			receiver.cursor(-this.distance);
		}
		public String toString() {
			return "cursor[" + this.distance + "]";
		}
	}
	/** エディタレシーバ。 */
	public class EditorReceiver {
		private StringBuilder text = new StringBuilder();
		private int cursorX = 0;
		public void input(String text) {
			System.out.println("EditorReceiver.input(): " + text);
			this.text.insert(this.cursorX, text);
			this.cursorX += text.length();
		}
		public void backspace(int distance) {
			System.out.println("EditorReceiver.backspace(): " + distance);
			this.text.delete(this.cursorX - distance, this.cursorX);
			this.cursorX -= distance;
		}
		public void cursor(int distance) {
			System.out.println("EditorReceiver.cursor(): " + distance);
			this.cursorX += distance;
		}
		public String result() {
			return this.text.toString();
		}
	}
	/** エディタインボーカ。 */
	public class EditorInvoker {
		private LinkedList<EditorCommand> commands = new LinkedList<>();
		private ListIterator<EditorCommand> commandIt = commands.listIterator();
		EditorReceiver receiver = new EditorReceiver();
		public void execute(EditorCommand command) {
			// undo 済みを削除
			while (this.commandIt.hasNext()) {
				EditorCommand c = this.commandIt.next();
				System.out.println("EditorInvoker.execute(): remove undo -> " + c);
				this.commandIt.remove();
			}
			// command 登録
			this.commandIt.add(command);
			// command 実行
			command.execute(receiver);
		}
		public void undo() {
			// command undo
			EditorCommand command = this.commandIt.previous();
			System.out.println("EditorInvoker.undo(): " + command);
			command.undo(this.receiver);
		}
		public String result() {
			return this.receiver.result();
		}
	}
}