本文共 4036 字,大约阅读时间需要 13 分钟。
本文源码见:
装饰器模式(Decorator Pattern)以客户端透明的方式扩展对象的功能。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装,是继承关系的一个替代方案。
说到装饰者模式,估计大家都不陌生,Java I/O的设计就是采用了装饰者模式。想必初学Java I/O的时候大家都经历过一段“懵逼”期,各种InputStream
和OutputStream
层层嵌套,感觉就像洋葱,如果给装饰者一个形象化的吉祥物,想必非洋葱莫属。
这里装饰者的例子就直接拿来主义了,因为《图解设计模式》这本书中的例子比较形象。
这个例子的功能是给文字增加装饰边框。通过不同的装饰类实现不同的边框效果。
比如,我有个字符串“Hello, world!”,我想在两侧加上边框,那么就显示为
|Hello, world!|
如果我想在上下左右均加上边框,那么就显示为
+-------------+|Hello, world!|+-------------+
并且可以一层层嵌套:
+---------------+|+-------------+|||Hello, world!|||+-------------+|+---------------+
怎么样,是不是很像洋葱啊?
总体来说,还是字符显示的问题,通过一个抽象类Display
来定义。
Display.java
public abstract class Display { public abstract int getColumn(); public abstract int getRows(); public abstract String getRowText(int row); public final void show() { for (int i = 0; i < getRows(); i++) { System.out.println(getRowText(i)); } }}
其中show()
方法将所有行的内容显示出来。那么对于没有任何边框的文字显示来说:
StringDisplay.java
public class StringDisplay extends Display { private String string; public StringDisplay(String string) { this.string = string; } public int getColumn() { return string.getBytes().length; } public int getRows() { return 1; } public String getRowText(int row) { if (row == 0) { return string; } else { return null; } }}
因为只有一行,所以getRows()
返回1。我们再来看一下边框装饰后的文本:
BoardStringDisplay.java
public abstract class BoardStringDisplay extends Display { protected Display display; protected BoardStringDisplay(Display display) { this.display = display; }}
SideBoardStringDisplay.java
public class SideBoardStringDisplay extends BoardStringDisplay { protected SideBoardStringDisplay(Display display) { super(display); } public int getColumns() { return 1 + display.getColumns() + 1; // 文字两侧各增加一个字符 } public int getRows() { return display.getRows(); // 行数不变 } public String getRowText(int row) { return "|" + display.getRowText(row) + "|"; }}
FullBoardStringDisplay.java
public class FullBoardStringDisplay extends BoardStringDisplay { protected FullBoardStringDisplay(Display display) { super(display); } public int getColumns() { return 1 + display.getColumns() + 1; } public int getRows() { return 1 + display.getRows() + 1; } public String getRowText(int row) { if (row == 0 || row == display.getRows() + 1) { return "+" + makeLine('-', display.getColumns()) + "+"; } else { return "|" + display.getRowText(row - 1) + "|"; } } private String makeLine(char ch, int count) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < count; i++) { buf.append(ch); } return buf.toString(); }}
试一下洋葱效果:
Client.java
public class Client { public static void main(String[] args) { Display d1 = new StringDisplay("Hello, world!"); Display d2 = new SideBoardStringDisplay(d1); Display d3 = new FullBoardStringDisplay( new SideBoardStringDisplay( new FullBoardStringDisplay(d2))); System.out.println("显示字符串>>>>>>"); d1.show(); System.out.println("\n增加两侧边框>>>>>>"); d2.show(); System.out.println("\n再增加全边框、两侧边框、全边框>>>>>>"); d3.show(); }}
输入如下:
这个例子的代码量比以前的多一些,但是思路并不复杂。
这里,StringDisplay
是被装饰者,SideBoardStringDisplay
和FullBoardStringDisplay
是装饰器,同时也能够被装饰,因为说到底,它们都是继承自Display
,所以可以层层嵌套,不断增强。
我们再回头看装饰器模式的特点:
各种装饰器并非一定要有一个抽象(本例中的BoardStringDisplay
),直接装饰StringDisplay
也是OK的,这个并不是装饰器模式的特点,只是具体使用时看是否有进一步抽象的需要。
转载于:https://blog.51cto.com/liukang/2046423