问题的提出:一个人装扮,可以先穿裤子,再穿衣服,再穿鞋子;也可以先穿衣服,再是裤子,再是鞋子,由于裤子、衣服、鞋子种类很多,故搭配种类就更多了。又如制作蛋糕,鲜奶油蛋糕、草莓蛋糕或生日蛋糕其实都是蛋糕,这是装饰手法不同而已。因此,如果在程序中采用简单的各个类顺序调用的方法为其装饰,那么程序就会显得相当不灵活,其他的可维护性、可扩展性、可复用性也可想而知了。对此,我们可以采用装饰模式:
说明:
ü Component定义了一个对象接口,可以给这些对象动态地添加职责;
ü ConcreteComponent定义了一个具体的对象,也可以给这个对象添加一些职责;
ü Decorator定义了装饰抽象类,继承了Component,从外类来扩展Component类的
功能,但对于Component来说,无需知道Decorator的存在的,至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能;
ü SetComponent是用来对对象进行包装(以及再包装的,实际中也可以使用构造函数实
现),这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中(之所以能被添加到对象链当中,在于每个ConcreteDecorator都实现了Decorator,而Decorator又实现了Component,故每个”装饰好的东西自然可以看作新的装饰,然后作为参数的形式传进去”,这样做最大的好处在于:”装饰的顺序可以随意定,待装饰好后再展示最后的装饰结果”);
ü 程序中如果只有一个ConcreteComponent类而没有抽象的Component类,那么
Decorator类可以是ConcreteComponent的一个子类。同样道理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。
实例一:外出打扮
Ø 抽象类 -- 文件Decorator.java
package com.yilong.decorator;
public abstract class Decorator {
public abstract void show();
}
Ø 装扮对象Person类 -- 文件Person.java – 最基本(起初)的装扮对象
package com.yilong.decorator;
public class Person extends Decorator {
private String name;
public Person(String name) {
this.name = name;
}
public void show() {
System.out.println(this.name + " - 装扮成功!");
}
}
Ø 抽象类PersonDecorator – PersonDecorator.java – 各种装扮的抽象
package com.yilong.decorator;
public abstract class PersonDecorator extends Decorator {
Decorator decorator;
public PersonDecorator(Decorator decorator) {
this.decorator = decorator;
}
public abstract void show();
}
Ø 文件BigTrouserDisplay.java -- 添加”垮裤”的装扮
package com.yilong.decorator;
public class BigTrouserDisplay extends PersonDecorator {
public BigTrouserDisplay(Decorator decorator) {
super(decorator);
}
@Override
public void show() {
System.out.println("穿上 垮裤");
this.decorator.show();
}
}
Ø 文件TShirtDisplay.java – 添加”T-Shirt”的装扮
package com.yilong.decorator;
public class TShirtDisplay extends PersonDecorator {
public TShirtDisplay(Decorator decorator) {
super(decorator);
}
@Override
public void show() {
System.out.println("穿上 T-Shirt");
this.decorator.show();
}
}
Ø 文件Main.java – 测试类
package com.yilong.decorator;
public class Main {
public static void main(String[] args) {
Decorator decorator = new TShirtDisplay
(new BigTrouserDisplay(new Person("逸龙")));
decorator.show();
}
}
打印结果:
穿上 T-Shirt
穿上 垮裤
逸龙 - 装扮成功!
说明:程序中完全可以实现先穿垮裤,再穿T-Shirt,只要改变顺序即可。
实例二:修饰字符串
Ø 文件Main.java
package com.yilong.decorator.string;
public class Main {
public static void main(String[] args) {
Display d1 = new StringDisplay("Hello World!");
System.out.println("展示方式一:");
d1.show();
Display d2 = new SideBorder(d1, '#');
System.out.println("展示方式二:");
d2.show();
Display d3 = new FullBorder(d2);
System.out.println("展示方式三:");
d3.show();
Display d4 = new FullBorder(new SideBorder(d3, '*'));
System.out.println("展示方式四:");
d4.show();
}
}
Ø 文件Display.java
package com.yilong.decorator.string;
public abstract class Display {
public abstract int getColumns();
public abstract int getRows();
public abstract String getRowText(int row);
public void show() {
for(int i=0; i<getRows(); i++) {
System.out.println(getRowText(i));
}
}
}
Ø 文件StringDisplay.java – 基本的字符串显示
package com.yilong.decorator.string;
public class StringDisplay extends Display {
private String string;
public StringDisplay(String string) {
this.string = string;
}
@Override
public int getColumns() {
//return string.getBytes().length;//返回字符串字节数
return string.length();//返回字符串实际长度
}
@Override
public String getRowText(int row) {
if(row == 0) {
return string;
} else {
return null;
}
}
@Override
public int getRows() {
return 1;
}
}
Ø 文件Border.java – 为字符串添加装饰的抽象类
package com.yilong.decorator.string;
public abstract class Border extends Display {
protected Display display;
public Border(Display display) {
this.display = display;
}
}
Ø 文件SideBorder.java – 为字符串两边添加标志字符显示
package com.yilong.decorator.string;
public class SideBorder extends Border {
private char borderChar;
public SideBorder(Display display, char borderChar) {
super(display);
this.borderChar = borderChar;
}
@Override
public int getColumns() {
return this.display.getColumns() + 2;
}
@Override
public String getRowText(int row) {
return borderChar + this.display.getRowText(row) + borderChar;
}
@Override
public int getRows() {
return this.display.getRows();
}
}
Ø 文件FullBorder.java – 为字符串添加边框显示的类
package com.yilong.decorator.string;
public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}
@Override
public int getColumns() {
return this.display.getColumns() + 2;
}
@Override
public String getRowText(int row) {
if(row == 0 || row == display.getRows()+1) {
return "+" + makeLine('-', getColumns()) + "+";
} else {
return "|" + this.display.getRowText(row-1) + "|";
}
}
@Override
public int getRows() {
return display.getRows() + 2;
}
public StringBuffer makeLine(char ch, int count) {
StringBuffer strb = new StringBuffer();
for(int i=0; i<count; i++) {
strb.append("-");
}
return strb;
}
}
打印结果:
展示方式一:
Hello World!
展示方式二:
#Hello World!#
展示方式三:
+----------------+
|#Hello World!#|
+----------------+
展示方式四:
+--------------------+
|*+----------------+*|
|*|#Hello World!#|*|
|*+----------------+*|
+--------------------+
总结:装饰模式是为已有功能动态地添加更多功能的一种方式;装饰模式把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象。