裝飾模式
裝飾模式能給一個物件動態新增一些額外的職責。就增加功能來說, Decorator 模式相比生成子類更為靈活。
裝飾模式結構
在裝飾模式中各個角色有:
- 抽象構件(Component)角色: 給出一個抽象介面,以規範準備接收附加責任的物件。
- 具體構件(Concrete Component)角色:定義一個要接收附加責任的類。
- 裝飾(Decorator)角色:持有一個構件(斷腸和)物件的例項,並定義一個與抽象構件介面一致的介面。
- 具體裝飾(Decorator)角色:負責給構件物件 “貼上”附加的責任。
例子:
- 裝飾模式動態地為物件附加額外的責任。新增到松樹或冷杉樹上的裝飾品就是裝飾模式的例子。燈光、花環、手杖糖果、玻璃裝飾品等,都可以新增到樹上,讓它看起來更喜慶。這些裝飾物不會改變聖誕樹本身,不管使用什麼特殊的裝飾物,聖誕樹都可以被認作聖誕樹。作為附加功能的一個例子,燈的新增允許人們“點亮”聖誕樹。
- 另一個例子:突擊槍本身就是致命武器。但你可以應用某些“裝飾”使它更精確、更安靜、更具破壞性。
我們再從程式碼層面看一個例子:
使用裝飾模式前
package DecoratorPattern.Before;
/**
* 使用裝飾模式前
*/
class A {
public void doIt(){
System.out.print('A');
}
}
class AwithX extends A {
@Override
public void doIt() {
super.doIt();
doX();
}
private void doX(){
System.out.print('X');
}
}
class AwithY extends A {
@Override
public void doIt() {
super.doIt();
doY();
}
public void doY(){
System.out.print('Y');
}
}
class AwithZ extends A {
@Override
public void doIt() {
super.doIt();
doZ();
}
public void doZ() {
System.out.print('Z');
}
}
class AwithXY extends AwithX {
private AwithY obj = new AwithY();
@Override
public void doIt() {
super.doIt();
obj.doY();
}
}
class AwithXYZ extends AwithX {
private AwithY obj1 = new AwithY();
private AwithZ obj2 = new AwithZ();
@Override
public void doIt() {
super.doIt();
obj1.doY();
obj2.doZ();
}
}
public class DecoratorDemo{
public static void main(String[] args) {
A[] array = {new AwithX(), new AwithXY(), new AwithXYZ()};
for (A a: array){
a.doIt();
System.out.print(' ');
}
}
}
使用裝飾模式後
package DecoratorPattern.After;
/**
* 使用裝飾模式後
*/
interface I {
void doIt();
}
class A implements I {
@Override
public void doIt() {
System.out.print('A');
}
}
abstract class D implements I {
private I core;
public D(I inner) {
core = inner;
}
@Override
public void doIt() {
core.doIt();
}
}
class X extends D {
public X(I inner) {
super(inner);
}
@Override
public void doIt() {
super.doIt();
doX();
}
private void doX() {
System.out.print('X');
}
}
class Y extends D {
public Y(I inner) {
super(inner);
}
@Override
public void doIt() {
super.doIt();
doY();
}
private void doY() {
System.out.print('Y');
}
}
class Z extends D {
public Z(I inner) {
super(inner);
}
@Override
public void doIt() {
super.doIt();
doZ();
}
private void doZ() {
System.out.print('Z');
}
}
public class DecoratorDemo {
public static void main(String[] args) {
I[] array = {new X(new A()), new Y(new X(new A())), new Z(new Y(new X(new A())))};
for (I anArray: array){
anArray.doIt();
System.out.print(' ');
}
}
}
優點:
- 裝飾模式比靜態繼續更靈活。與物件的靜態繼承相比,裝飾模式提供了更加靈活地向物件新增職責的方式。可以用新增和分享的方法,用裝飾在執行時和刪除職責。相比之下,繼承機制要求為每個新增的職責建立一個新的子類。這會產生許多新的類,並且會增加系統複雜度。此外,為下個特定的 Component 類提供多個不同的 Decorator 類,這就使得你可以對一些職責進行混合和匹配。
- 避免在層次結構高層的類有太多的特徵。裝飾模式提供了一種”即用即付“的方法來新增職責。它並不試圖在一個複雜的可定製的類中支援所有可預見的特徵,相反,你可以定義一個簡單的類,並且用 Decorator 類給它逐漸地新增功能。可以從簡單的部件組合出複雜的功能。這樣,應用程式不必為不需要的特徵付出代價。
缺點:
- Decorator 與它的 Component 不一樣。Decorator 是一個透明的包裝。如果我們從物件標識的觀點出發,一個被裝飾了的元件與這個元件是有差別的,因此,使用裝飾時不應該依賴物件標識。
- 有許多小物件。採用裝飾模式進行系統設計往往會產生許多看上去類似的小物件,這些物件僅僅在它們相互連線的方式上有所不同,而不是它們的類或是它們的屬性值有所不同。儘管對天那些瞭解這些系統的人來說,很容易對它們進行定製,但是很難學習這些系統,排錯也很困難。
參考: