設計模式:裝飾模式(Decorator )
定義與結構
裝飾模式(Decorator)也叫包裝器模式(Wrapper)。GOF 在《設計模式》一書中給出的定義為:動態地給一個物件新增一些額外的職責。就增加功能來說,Decorator 模式相比生成子類更為靈活。
讓我們來理解一下這句話。我們來設計“門”這個類。假設你根據需求為“門”類作了如下
定義:
現在,在系統的一個地方需要一個能夠報警的Door,你來怎麼做呢?你或許寫一個Door的子類AlarmDoor,在裡面新增一個子類獨有的方法alarm()。嗯,那在使用警報門的地方你必須讓客戶知道使用的是警報門,不然無法使用這個獨有的方法。而且,這個還違反了Liskov 替換原則。
也許你要說,那就把這個方法新增到Door 裡面,這樣不就統一了?但是這樣所有的門都必須有警報,至少是個“啞巴”警報。而當你的系統僅僅在一兩個地方使用了警報門,這明顯是不合理的——雖然可以使用預設介面卡來彌補一下。
這時候,你可以考慮採用裝飾模式來給門動態的新增些額外的功能。
下面我們來看看裝飾模式的組成,不要急著去解決上面的問題,到了下面自然就明白了!
1) 抽象構件角色(Component):定義一個抽象介面,以規範準備接收附加責任的物件。
2) 具體構件角色(Concrete Component):這是被裝飾者,定義一個將要被裝飾增加功能的類。
3) 裝飾角色(Decorator):
4) 具體裝飾角色(Concrete Decorator):負責給構件新增增加的功能。
看下裝飾模式的類圖:
圖中 ConcreteComponent 可能繼承自其它的體系,而為了實現裝飾模式,他還要實現Component 介面。整個裝飾模式的結構是按照組合模式來實現的——兩者都有類似的結構圖,都基於遞迴組合來組織可變數目的物件。但是兩者的目的是截然不同的,組合(Composite)模式側重通過遞迴組合構造類,使不同的物件、多重的物件可以“一視同仁”;而裝飾(Decorator)模式僅僅是借遞迴組合來達到定義中的目的。
裝飾模式的實現
//抽象構件角色--Component
public interface Shape {
void draw();
}
//具體構件角色--Concrete ComponentA
public class Circle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Shape: Circle");
}
}
//具體構件角色--Concrete ComponentB
public class Rectangle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("Shape: Rectangle");
}
}
//裝飾角色--Decorator
public abstract class ShapeDecorator implements Shape{
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}
public void draw() {
decoratedShape.draw();
}
}
//具體裝飾角色--Concrete Decorator
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
// TODO Auto-generated constructor stub
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape) {
System.out.println("Border Color: Red");
}
}
//客戶端
public class DecoratorPatternDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
驗證輸出
Circle with normal border
Shape: Circle
Circle of red border
Shape: Circle
Border Color: Red
Rectangle of red border
Shape: Rectangle
Border Color: Red