java設計模式之裝飾器模式


裝飾器模式的定義:

  裝飾器模式也叫作包裝器模式,指在不改變原有對象的基礎上,動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾器模式相比生成子類更為靈活,屬於結構性設計模式。

裝飾器模式提供了比繼承更有彈性的替代方案(擴展原有對象的功能)將功能附加到對象上,因此裝飾器模式的核心是擴展功能,使用裝飾器模式可以透明且動態地擴展類地功能。

裝飾器模式地應用場景:

  • 用於擴展一個類地功能,或者給一個類添加職責。
  • 動態地給一個對象添加功能,這些功能可以再動態地撤銷。
  • 需要為一批平行的兄弟類進行改裝或加裝功能。

裝飾器模式的UML類圖:

 

 

 

 由上圖可以看到,裝飾器模式主要包含以下4個角色:

  • 抽象組件(Component):可以是一個接口或者抽象類,充當被裝飾類的原始對象,規定了被裝飾對象的行為。
  • 具體組件(ConcreteComponent):實現/繼承Component的一個具體對象,即被裝飾對象。
  • 抽象裝飾器(Decorator):通用的裝飾concreteComponent的裝飾器,其內部必然有一個屬性指向Component;其實現一般是一個抽象類,主要是為了讓其子類按照其構造形式傳入一個Component,這是強制的通用行為。如果系統中裝飾邏輯單一,則並不需要實現許多的裝飾器,可以直接省略該類,而直接實現一個具體的裝飾器即可。
  • 具體裝飾器(ConcreteDecorator):Decorator的具體實現類,理論上,每個ConcreteDecorator都擴展了Component對象的一種功能。

裝飾器模式的通用寫法:

 
         
package com.liuyi.designmode.structure.decorator;

import javax.swing.*;

public class Client {
public static void main(String[] args){
Component c1 = new ConcreteComponent (); //首先創建需要被裝飾的原始對象(即要被裝飾的對象)
Decorator decoratorA = new ConcreteDecoratorA(c1); //給對象透明的增加功能A並調用
decoratorA .operation();
Decorator decoratorB = new ConcreteDecoratorB(c1); //給對象透明的增加功能B並調用
decoratorB .operation();
Decorator decoratorBandA = new ConcreteDecoratorB(decoratorA);//裝飾器也可以裝飾具體的裝飾對象,此時相當於給對象在增加A的功能基礎上在添加功能B
decoratorBandA.operation();
}

//抽象組件
static interface Component {
/**
* 示例方法
*/
public void operation();
}

//具體組件
static class ConcreteComponent implements Component {
public void operation() {
//相應的功能處理
System.out.println("處理業務邏輯");
}
}

static abstract class Decorator implements Component {
/**
* 持有組件對象
*/
protected Component component;

/**
* 構造方法,傳入組件對象
* @param component 組件對象
*/
public Decorator(Component component) {
this.component = component;
}

public void operation() {
//轉發請求給組件對象,可以在轉發前后執行一些附加動作
component.operation();
}
}

//具體裝飾器A
static class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
private void operationFirst(){ } //在調用父類的operation方法之前需要執行的操作
private void operationLast(){ } //在調用父類的operation方法之后需要執行的操作
public void operation() {
//調用父類的方法,可以在調用前后執行一些附加動作
operationFirst(); //添加的功能
super.operation(); //這里可以選擇性的調用父類的方法,如果不調用則相當於完全改寫了方法,實現了新的功能
operationLast(); //添加的功能
}
}

//具體裝飾器B
static class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
private void operationFirst(){ } //在調用父類的operation方法之前需要執行的操作
private void operationLast(){ } //在調用父類的operation方法之后需要執行的操作
public void operation() {
//調用父類的方法,可以在調用前后執行一些附加動作
operationFirst(); //添加的功能
super.operation(); //這里可以選擇性的調用父類的方法,如果不調用則相當於完全改寫了方法,實現了新的功能
operationLast(); //添加的功能
}
}
}
 

分析裝飾器模式在IO流中的使用:

  java中的IO流主要分為字符流和字節流,這里我們以常用的字符流的讀(Reader)為例來分析。首先我覺得要理解裝飾器模式在IO中的使用,首先我們必須要

明白怎么去區分哪些是裝飾類,哪些是被裝飾類。我們看上面的例子,發現具體的裝飾類ConcreteDecoratorA和ConcreteDecoratorB的構造函數的參數都是頂層接

口的實例對象。我們先通過JDK API文檔查看Reader接口有哪些實現類,然后我們根據這個規則去判斷哪些實現類是裝飾類,哪些實現類是被裝飾類。

 

 我們先來查看BufferedReader的構造方法,發現傳入的參數是頂層抽象的實例對象,所以可以判定BufferedReader是一個裝飾類,並且它不是抽象的,所以這里直接忽略

了抽象裝飾器,直接實現了具體的裝飾器。當然Reader也有抽象的裝飾器,比如FilterReader,感興趣的同學可以自己去看API,但是它的具體的裝飾器實現很少用,當然

如果需要自定義Reader裝飾器,可以去繼承這個抽象裝飾器實現。

我們再來看InputStreamReader的構造方法,發現傳入的參數不是頂層抽象的實例對象,所以可以判斷InputStreamReader是一個被裝飾的類。

 這里只列舉這兩個常用的被裝飾類和裝飾的類,目的只是為了讓大家理解裝飾器模式在IO流中是怎么運用的,來看看具體的使用,我們讀取項目路徑下的

名字為xiaoniu.txt文件里面的內容,然后打印出來,先來看不使用裝飾類即用InputStreamReader的情況下怎么使用:

package com.liuyi.designmode.structure.decorator;

import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;

/**
 * @ClassName IoTest
 * @description:
 * @author:liuyi
 * @Date:2020/11/14 23:01
 */
public class IOTest {
    public static void main(String[] args) throws Exception {
        //讀取當前項目下的xiaoniu.txt
        FileInputStream fileInputStream = new FileInputStream("xiaoniu.txt");
        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
        //設置一次讀取1024個字節
        char[] chars = new char[1024];
        int index;
        while ((index=inputStreamReader.read(chars))!=-1){
            System.out.println(String.valueOf(chars));
        }
        fileInputStream.close();
        inputStreamReader.close();
    }
}

先不着急使用裝飾類,我們發現使用的時候還要傳入一個InputStream的實例對象,通過查看JDK文檔,我們發現InputStreamReader還有一個子類FileReader,

看看FileReader怎么使用:

package com.liuyi.designmode.structure.decorator;

import java.io.FileReader;

/**
 * @ClassName IoTest
 * @description:
 * @author:liuyi
 * @Date:2020/11/14 23:01
 */
public class IOTest {
    public static void main(String[] args) throws Exception {
        //讀取當前項目下的xiaoniu.txt
        FileReader fileReader = new FileReader("xiaoniu.txt");
        //設置一次讀取1024個字節
        char[] chars = new char[1024];
        int index;
        while ((index=fileReader.read(chars))!=-1){
            System.out.println(String.valueOf(chars));
        }
        fileReader.close();
    }
}

FileReader也起到增強的作用,只不過是通過子類的方式實現的,只是這種方式不符合開閉原則,如果需要添加功能必須要修改子類,還要在接口添加抽象方法。

我們通常會讀取文件里面的內容的時候,需要一行一行的讀取,如果只用被裝飾類的方法是很難實現的,這個時候我們的裝飾器BufferedReader就閃亮登場了。

package com.liuyi.designmode.structure.decorator;

import java.io.BufferedReader;
import java.io.FileReader;

/**
 * @ClassName IoTest
 * @description:
 * @author:liuyi
 * @Date:2020/11/14 23:01
 */
public class IOTest {
    public static void main(String[] args) throws Exception {
        //讀取當前項目下的xiaoniu.txt
        FileReader fileReader = new FileReader("xiaoniu.txt");
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        //設置一次讀取1024個字節
        String readLine = bufferedReader.readLine();
        System.out.println(readLine);
    }
}

裝飾器模式與代理模式的區別:

  從代理模式UML類圖和通用代碼實現來看,代理模式與裝飾器模式幾乎一模一樣。從代碼實現來看,代理模式確實與裝飾器模式也是一樣的,但是這兩種設計模式

所面向的功能擴展面是不一樣的。

  裝飾器模式強調自身的功能擴展,着重類功能的變化,比如添加方法。而代理模式強調對代理過程的控制,主要是對已有的方法進行功能增強,比如spring中通過

AOP實現客戶端請求日志的記錄。

裝飾器模式的優點:

  • 裝飾器是繼承的有力補充,比繼承靈活,在不改變原對象的情況下,動態地給一個對象擴展功能,即插即用。
  • 通過使用不同裝飾類及這些裝飾類的排列組合,可以實現不同效果。
  • 裝飾器模式完全遵守開閉原則。

裝飾器模式的缺點:

  • 會出現更多的代碼、更多的類,增加程序的復雜性。
  • 動態裝飾在多層裝飾時會更復雜。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM