Index
参考書籍によると
中心となるオブジェクトがあり、それに飾り付けとなる機能を一皮一皮かぶせて行って、より目的に合ったオブジェクトに仕上げていくのです。
オブジェクトににどんどんデコレーションを施して行くようなデザインパターン
- 透過的なインターフェース
- 中身を変えずに、機能追加ができる
- 動的な機能追加ができる
- 単純な品揃えでも、多様な機能追加ができる
詳細についてはサンプルコードを見ながら確認していきましょう。
文字列の周りに飾り枠をつけて表示するサンプルコードを作っていきます。
UMLは以下の通り
複数行からなる文字列を表示するための抽象クラスです。
show()メソッドは全ての行を表示するメソッドで、TemplateMethodパターンを使って作成されています。
Template Methodパターンについては以下の記事を参照してください。
TemplateMethodパターン@Java言語で学ぶデザインパターン入門
1 2 3 4 5 6 7 8 9 10 11 12 |
public abstract class Display { public abstract int getColumns(); public abstract int getRows(); public abstract String getRowText(final int row); //全ての行を表示する public void show() { for(int i = 0 ; i < getRows(); i++) { System.out.println(getRowText(i)); } } } |
一行の文字列を表示するためのクラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class StringDisplay extends Display { private final String m_string; //表示文字列 public StringDisplay(final String string) { m_string = string; } @Override public int getColumns() { return m_string.length(); } @Override public int getRows() { return 1; } @Override public String getRowText(final int row) { if (row != 0) throw new IndexOutOfBoundsException(); return m_string; } } |
飾り枠を表す抽象クラスですが、文字列表示を行うDisplayクラスを継承しています。
つまりBorder(飾り)と中身(Display)を同一視することができます。
こうすることで、DisplayのAPIを全てのクラスから見ることができます。
これを「インターフェースが透明的」であると言います。」
似たような構造はCompositeパターンにも登場しますので、併せて参照してみてください。
Compositeパターン@Java言語によるデザインパターン入門
1 2 3 4 5 6 |
public abstract class Border extends Display { protected Display m_display; //飾り枠が包んでいる中身 protected Border(final Display display) { m_display = display; } } |
具体的な飾り枠です。
文字列の左右に決まった文字列をくっつけます。
getRowText()メソッドでdecorateしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class SideBorder extends Border { private char m_borderChar;//飾りの文字 //中身となるDisplayと飾り文字を指定 public SideBorder(final Display display, final char ch) { super(display); m_borderChar = ch; } @Override public int getColumns() { //文字数は中身の両側に飾り文字分を加えたもの return 1 + m_display.getColumns() + 1; } @Override public int getRows() { //桁数は中身と同じ return m_display.getRows(); } @Override public String getRowText(final int row) { //指定行の内容は、中身の指定行の両側に飾り文字をつけたもの return m_borderChar + m_display.getRowText(row) + m_borderChar; } } |
具体的な飾りつけクラスです。
Side Borderと違い、上下左右に飾り付けをします。
このようにBorderを継承してさえいれば、多様な機能追加をすることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class FullBorder extends Border { protected FullBorder(final Display display) { super(display); } @Override public int getColumns() { return 1 + m_display.getColumns() + 1; } @Override public int getRows() { return 1 + m_display.getRows() + 1; } @Override public String getRowText(final int row) { if(row == 0) return "+" + makeLine('-',m_display.getColumns()) + '+'; else if(row == m_display.getRows() + 1) return "+" + makeLine('-',m_display.getColumns()) + "+"; else return "|" + m_display.getRowText(row - 1) + "|"; } //文字chをcount個連続させた文字列を作成する private String makeLine(final char ch,final int count) { final StringBuilder line = new StringBuilder(); for(int i = 0; i < count; i++) { line.append(ch); } return line.toString(); } } |
display4をみてください。
包みに包んでいます。
また包まれるものを変更せずに動的に機能追加を行うことでできています
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class Main { public static void main(String[] args) { final Display display1 = new StringDisplay("hello"); final Display display2 = new SideBorder(display1 , '@'); final Display display3 = new FullBorder(display2); display1.show(); display2.show(); display3.show(); final Display display4 = new SideBorder( new FullBorder( new FullBorder( new SideBorder( new FullBorder( new StringDisplay("Hello") ), '*' ) ) ), '*' ); display4.show(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
hello @hello@ +-------+ |@hello@| +-------+ *+-----------+* *|+---------+|* *||*+-----+*||* *||*|Hello|*||* *||*+-----+*||* *|+---------+|* *+-----------+* |
Compositeパターン@Java言語によるデザインパターン入門