kiyo_hikoのブログ

メモ+日記?

プログラミングメモ:Decoratorパターン

独習デザインパターン C++

独習デザインパターン C++

これを参考にして、JavaでDecoratorを書いた。
(動くかどうかは謎い)
あまり誰かに伝えたいとかではなくてあとで自分が読んで理解できればいいやというスタンスなので、読んでも意味が通じないかもしれない。

Decoratorパターンのメリットは、継承に依らない機能の継ぎ足しができるということらしい。
たとえば上の本で紹介されている例としては、文字列を何がしかに出力する Writer というクラスを、クラスの爆発を避けながら機能追加していくという技ができるようになる。


Writer.java

public interface Writer {
	public void writeData(List<String> ss);
}


Writerを実装し、コンソールに出力するクラスやファイルに出力するクラスを作る。

ConsoleWriter.java

public class ConsoleWriter implements Writer {
	@Override
	public void writeData(List<String> ss) {
		for (String s : ss) {System.out.println(s);}
	}
}

FileWriter.java

public class FileWriter implements Writer {
	@Override
	public void writeData(List<String> ss) {
		Path path = new File("./log.txt").toPath();
		try {
			Files.write(path, ss, Charset.forName("UTF-8"), StandardOpenOption.WRITE);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


ここまででできた具体的な Writer に後付けで機能を追加したい場合、継承ベースだとそれぞれに対して拡張されたクラスを書かないといけず、それがとてもダルい。

だから Decorator を作る。 Decorator は Writer の操作を持っていて、しかも心の中に Writer を持っていて、 Writer の操作を呼ばれたとき、心の中の Writer を呼び出す。

BRWriter.java

public class BRWriter implements Writer {
	Writer sink;

	public BRWriter(Writer sink) {
		this.sink = sink;
	}

	@Override
	public void writeData(List<String> ss) {
		List<String> tt = new ArrayList<>(ss);
		for (String t : tt) {
			t = new StringBuilder(t).append("<br />").toString();
		}
		sink.writeData(tt);
	}
}


この委譲する性質を使って、クライアントコードからは Decorator をマトリョーシカのように入れ子にしていけば、機能追加されたクラスを自由にお作りすることができる。

Main.java

public class Main {
	public static void main(String[] args) {
		Writer out = new BRWriter(new FileWriter());
		out.writeData(Arrays.asList(new String[]{"うおーっ", "うおーっ", "うおーっ"}));
	}
}


委譲部分を基底クラスにしておけば、 Decorator も単純になって少しうれしい

AbstraceWriterDecorator.java

public abstract class AbstraceWiterDecorator implements Writer {
	Writer sink;
	
	public AbstraceWiterDecorator(Writer sink) {
		this.sink = sink;
	}
}

ということらしい。