設計模式之(7)——裝飾設計模式

2022-09-02 12:00:24

  定義:裝飾設計模式允許向一個現有的物件新增功能,而不改變其結構(這就很符合程式設計的「開閉原則」),重點突出類功能的增強,屬於結構型建立模式,這種模式建立了一個裝飾類,用來包裝原有類,保持類方法簽名完整的情況下,提供了額外的功能。

  意圖:動態地給一個物件增加一些額外的職責,裝飾是比子類更為靈活和優秀的一種方案;

  主要解決:通常擴充套件一個類我們通常使用繼承實現,由於繼承為類引入特徵,通常隨著功能的擴充套件,子類會很膨脹,容易造成類爆炸;裝飾設計模式是一種使用組合替代繼承的最佳實現,《effective java》中也建議「組合優先於繼承」,物件導向程式設計有封裝、繼承和多型三大特徵,其中封裝和繼承卻有一點矛盾,繼承意味著子類依賴了父類別的實現,一旦父類別中改變了規則,會對子類造成影響,這是打破封裝的一種表現,而組合就是巧用封裝來實現繼承功能的程式碼複用;

  何時使用:在不想增加很多子類的情況下擴充套件類的功能;

  設計模式結構圖:

  

  模式角色分析:

  1、抽象構件角色(Component):定義一個抽象介面,用來規範被裝飾類的行為;

  2、具體構件角色(ConcreteComponent):需要被裝飾的目標物件;

  3、抽象裝飾角色(decorator):持有一個具體構件物件的範例,並定義一個與抽象構建介面一致的介面;

  4、具體裝飾角色(ConcreteDecorator):具體的裝飾類,負責給被裝飾者擴充套件功能;

  優點:

  1、裝飾和繼承都能達到擴充套件類功能的目的,但是裝飾模式更加靈活;

  2、裝飾設計模式有很好的可延伸性;

  3、通過使用不同的具體裝飾類,以及這些類的排列組合,可以實現多重灌飾;

  缺點:

  裝飾設計或是會導致程式設計中出現許多小物件,如果過度使用會使程式變得更加複雜;

  UML類圖:

  

  測試程式碼:

  

package cn.com.pep.model.decarator.impl2;
/**
 * 
 * @Title: Shape  
 * @Description:  抽象構件角色(Component),用來約束被裝飾物件的行為
 * @author wwh 
 * @date 2022-9-1 16:11:29
 */
public interface Shape {

    /**
     * 
     * @Title: draw 
     * @Description:
     */
    public void draw();
}

 

package cn.com.pep.model.decarator.impl2;
/**
 * 
 * @Title: Circle  
 * @Description:  具體構件角色(ConcreteComponent),被裝飾的物件
 * @author wwh 
 * @date 2022-9-1 16:14:50
 */
public class Circle implements Shape {

    @Override
    public void draw() {
        System.err.println("Shape: Circle.");
    }

}
package cn.com.pep.model.decarator.impl2;
/**
 * 
 * @Title: Rectangle  具體構件角色(ConcreteComponent),被裝飾的物件
 * @Description:  
 * @author wwh 
 * @date 2022-9-1 16:13:36
 */
public class Rectangle implements Shape {

    @Override
    public void draw() {
        System.err.println("Shape: Recatangle.");
    }

}

 

 

package cn.com.pep.model.decarator.impl2;
/**
 * 
 * @Title: ShapeDecorator  
 * @Description:  抽象裝飾角色(Decorator),定義一組與抽象構件角色相同的行為,
 *                      通過實現介面來定義,並持有一個具體構件角色的參照,在完成裝飾設計模式增加的功能之後再呼叫原有功能
 * @author wwh 
 * @date 2022-9-1 16:16:42
 */
public abstract class ShapeDecorator implements Shape{
    
    private Shape shape;
    
    public ShapeDecorator(Shape shape) {
        this.shape = shape;
    }

    @Override
    public void draw() {
        shape.draw();
    }

}
package cn.com.pep.model.decarator.impl2;
/**
 * 
 * @Title: RedShapeDecorator  
 * @Description: 具體的裝飾者類,負責給被裝飾者類擴充套件功能; 
 * @author wwh 
 * @date 2022-9-1 16:55:38
 */
public class RedShapeDecorator extends ShapeDecorator{

    public RedShapeDecorator(Shape shape) {
        super(shape);
    }
    
    @Override
    public void draw() {
        super.draw();
        setRedColor();
    }
    
    private void setRedColor() {
        System.err.println("Red Color!");
    }

}
package cn.com.pep.model.decarator.impl2;
/**
 * 
 * @Title: DecoratorPatternDemo  
 * @Description:  測試類
 * @author wwh 
 * @date 2022-9-1 16:56:54
 */
public class DecoratorPatternDemo {
    
    public static void main(String[] args) {
        Shape shape = new Rectangle();
        ShapeDecorator decorator = new RedShapeDecorator(shape);
        decorator.draw();
    }
}  

裝飾設計模式在JDK原始碼中的應用:
  裝飾設計模式在jdk原始碼中最具代表性的應用莫過於I/O體系中的InputStream/OutputStream,Reader/Writer等,現在我們就以InputStream為例進行分析:
  
抽象構件角色(Component):InputStream類,用來定義具體構件的行為;
具體構件角色(ConcreteComponent):FileInputStream、ByteArrayInputStream、ObjectInputStream類,具體被裝飾的類;
抽象裝飾角色(Decorator):FilterInputStream類,通過繼承抽象構建角色類來定義一組與具體構件類相同的行為,再通過聚合的方式來持有被裝飾角色的參照;
具體裝飾角色(ConcreteDecorator):BufferedInputStream、DataInputStream、CheckedInputStream類,具體裝飾者類,負責給被裝飾者增加功能;

寫在後面:
  裝飾設計模式中,裝飾者和被裝飾者都可以獨立擴充套件,沒有耦合關係,被裝飾者無需知道裝飾者的存在,裝飾者也無需關心被裝飾者的具體細節,裝飾設計模式可以動態擴充套件一個類的功能,
是繼承的一種替代方案,但是裝飾的層級過多可能比較複雜。