問題的提出:一個人裝扮,可以先穿褲子,再穿衣服,再穿鞋子;也可以先穿衣服,再是褲子,再是鞋子,由於褲子、衣服、鞋子種類很多,故搭配種類就更多了。又如製作蛋糕,鮮奶油蛋糕、草莓蛋糕或生日蛋糕其實都是蛋糕,這是裝飾手法不同而已。因此,如果在程序中採用簡單的各個類順序調用的方法爲其裝飾,那麼程序就會顯得相當不靈活,其他的可維護性、可擴展性、可複用性也可想而知了。對此,我們可以採用裝飾模式:
說明:
ü 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!#|*|
|*+----------------+*|
+--------------------+
總結:裝飾模式是爲已有功能動態地添加更多功能的一種方式;裝飾模式把每個要裝飾的功能放在單獨的類中,並讓這個類包裝它所要裝飾的對象,因此,當需要執行特殊行爲時,客戶代碼就可以在運行時根據需要有選擇地、按順序地使用裝飾功能包裝對象。