您的位置:  首頁 > 技術雜談 > 正文

百度工程師教你玩轉設計模式(裝飾器模式)

2022-12-23 12:00 https://my.oschina.net/u/4939618/blog/5616157 百度Geek說 次閱讀 條評論

作者 | 北極星小組

想要寫好代碼,設計模式(Design Pattern)是必不可少的基本功,設計模式是對面向對象設計(Object Oriented Design)中反復出現的一類問題的一種解決方案,本篇介紹裝飾器模式(Decorator Pattern)。

在我們日常的開發過程中,一個最常見的場景就是在已有的基礎上新增功能,常規的做法有以下幾種:

  • 修改已有的類:違背開閉原則。

  • 增加新的子類:每次都得新增大量對應的類,隨著功能的增加,子類越來越膨脹。

在此場景下,裝飾器模式就可以體現出它的優勢了,它允許在不修改原有對象的前提下,靈活的擴展已有類的功能。下面是裝飾器模式的一個通用的類圖:

圖片 △UML

其中的各個類的作用如下:

  • 抽象組件(Component): 可以是接口或者抽象類,它定義了具體類以及裝飾器所擁有的方法。

  • 具體組件(ComponentA, ComponentB):具體的組件,實現或者繼承自抽象組件??梢岳斫獬缮鲜鰣鼍爸幸汛嬖诘念?。

  • 抽象裝飾器(Decorator): 通常為抽象類,持有一個被裝飾的對象,定義了具體裝飾器的方法。此類非必須也可以沒有,具體裝飾器也可直接繼承或者實現抽象組件。

  • 具體裝飾器(DecoratorX, DecoratorY): 具體的裝飾器,繼承自抽象裝飾器(也可直接繼承自抽象組件),擴展了抽象組件的某些功能。

下面,將通過3個具體的案例的講解裝飾器的使用方式,方便大家進一步的理解。

一、裝飾器在任務處理場景的應用

在實際的開發中,我們經常需要定義不同的類來處理各種不同的任務。假設一個這樣的場景,我們的系統有多個具體的類,用來處理不同類型的任務?,F在需要添加一個功能,就是在處理完任務后發出一條消息。針對這個場景,使用裝飾器模式的實現思路如下:

  • 抽象組件(TaskProcessor):處理任務的抽象類(亦可通過接口實現),定義一個通用的任務處理方法process()。

  • 具體組件(TaskProcessorA, TaskProcessorB): 負責實現具體的任務處理邏輯

  • 抽象裝飾器(TaskProcessDecorator):持有一個任務處理對象實例

  • 具體裝飾器(AfterTaskProcessDecorator):實現具體的任務處理完成后的消息通知擴展能力

具體的代碼如下:

package com.baidu.demo;
public class Decorator {
    // 抽象組件
    static abstract class TaskProcessor {
        abstract void process();
    }
    // 具體組件
    static class TaskProcessorA extends TaskProcessor {
        @Override
        void process() {
            System.out.println("TaskProcessorA處理完成");
        }
    }
    // 具體組件
    static class TaskProcessorB extends TaskProcessor {
        @Override
        void process() {
            System.out.println("TaskProcessorB處理完成");
        }
    }
    // 抽象裝飾器
    static abstract class TaskProcessDecorator extends TaskProcessor {
        protected TaskProcessor processor;
        public TaskProcessDecorator(TaskProcessor processor) {
            this.processor = processor;
        }
        abstract void process();
    }
    // 具體裝飾器
    static class AfterTaskProcessDecorator extends TaskProcessDecorator {
        public AfterTaskProcessDecorator(TaskProcessor processor) {
            super(processor);
        }

        @Override
        void process() {
            processor.process();
            afterProcess();
        }

        void afterProcess() {
            System.out.println("任務處理完畢,發送消息...");
        }
    }

    public static void main(String[] args) {
        // 擴展之前
        System.out.println("==========before==========");
        TaskProcessor processorA = new TaskProcessorA();
        processorA.process();
        TaskProcessor processorB = new TaskProcessorB();
        processorB.process();

        // 裝飾器擴展之后:TaskProcessorA TaskProcessorB并未做任何修改,即可實現功能的擴展
        System.out.println("==========after==========");
        TaskProcessor decoratorA = new AfterTaskProcessDecorator(processorA);
        decoratorA.process();
        TaskProcessor decoratorB = new AfterTaskProcessDecorator(processorB);
        decoratorB.process();
    }
}

// 輸出結果如下
==========before==========
TaskProcessorA處理完成
TaskProcessorB處理完成
==========after==========
TaskProcessorA處理完成
任務處理完畢,發送消息...
TaskProcessorB處理完成
任務處理完畢,發送消息...

二、裝飾器在文件IO場景的應用

裝飾器模式,一個典型的應用就是文件IO操作,最基礎的類實現字節流讀取類,使用裝飾器模式可以封裝文件字節流讀取類,然后可以繼續封裝可緩存的文件字節流讀取類,在項目中按需使用。具體實現如下:

  • InputStream:具體組件,實現讀取字節流。

  • FileInputStream:具體裝飾器,作為InputStream的子類,擴展文件操作。

  • BufferedInputStream:具體裝飾器,作為FileInputStream的子類,擴展緩存操作。

具體代碼如下:

//具體組件,實現讀取字節流
public abstract class InputStream {
    public int read(byte b[], int off, int len) {}
}

//具體裝飾器,作為InputStream的子類,擴展文件操作
public class FileInputStream extends InputStream {
    protected InputStream in;
    
    public FileInputStream(String name) {
        InputStream in = ... //此處省略,通過文件名創建對象
        this.in = in;
    }
    
    public int read(byte b[], int off, int len) {
        return this.in.read(b, off, len);
    }
}

//具體裝飾器,作為FileInputStream的子類,擴展緩存操作
public class BufferedInputStream extends FileInputStream {
    protected FileInputStream in;
    protected byte[] buffer;
    
    public BufferedInputStream(FileInputStream in) {
        this.in = in;
    }
    
    public int read(byte b[], int off, int len) {
        if (this.buffer == null || this.buffer.length == 0) {
            this.in.read(this.buffer, 0, in.lenght());
        }
        
        System.arraycopy(this.buffer, off, b, 0, len);
        ...
    }
}

public static void main(String[] args) {
    FileInputStream fs = new FileInputStream('./test.log');
    BufferedInputStream bs = new BufferedInputStream(fs);
    
    byte[] b;
    bs.read(b, 0, 1);
}

三、裝飾器在日志系統場景的應用

在日志系統中,一般常用日志的級別分別為 DEBUG(調試)、INFO(運行信息)、WARN(警告)、ERROR(錯誤),一旦發生錯誤級別的日志后,則需要觸發報警通知相關人員及時進行跟進,報警方式一般有:郵件、短信、如流等,通常我們會根據業務場景以組合的方式進行報警通知,使用裝飾器模式則能很好實現組合報警這一功能。

  • 抽象組件:Log接口抽象

  • 具體組件:Slf4j 具體日志類的實現

  • 抽象裝飾器:LogDecorator 日志裝飾器的基類

  • 具體裝飾器:MailLogDecorator、SMSLogDecorator、InfoFlowLogDecorator具體裝飾類

/**
 * 日志接口
 */
public interface Log {
    void debug(String message);
    void info(String message);
    void warn(String message);
    void error(String message);
}

/**
 * Slf4j 日志
 */
public class Slf4jLog implements Log {

    //日志記錄對象
    private final Logger log = LoggerFactory.getLogger("system_log");

    @Override
    public void debug(String message) {
        if (log.isDebugEnabled()) {
             log.debug(message);
        }
    }

    @Override
    public void info(String message) {
        if (log.isInfoEnabled()) {
              log.info(message);
        }
    }

    @Override
    public void warn(String message) {
        if (log.isWarnEnabled()) {
            log.warn(message);
        }
    }

    @Override
    public void error(String message) {
        if (log.isErrorEnabled()) {
            log.error(message);
        }
    }
}

/**
 * 日志裝飾器
 */
public class LogDecorator implements Log {
    protected Log log;

    public LogDecorator(Log log) {
        this.log = log;
    }

    @Override
    public void debug(String message) {
        log.debug(message);
    }

    @Override
    public void info(String message) {
        log.info(message);
    }

    @Override
    public void warn(String message) {
        log.warn(message);
    }

    @Override
    public void error(String message) {
        log.error(message);
    }
}

/**
 * 郵件日志裝飾器
 */
public class MailLogDecorator extends LogDecorator {
    public MailLogDecorator(Log log) {
        super(log);
    }

    @Override
    public void warn(String message) {
        log.warn(message);
        mail(message);
    }

    @Override
    public void error(String message) {
        log.error(message);
        mail(message);
    }
    
    public void mail(String message) {
        //模擬郵件發送
        log.info("郵件已發送,信息:" + message);
    }
}

/**
 * 短信日志裝飾器
 */
public class SMSLogDecorator extends LogDecorator {
    public SMSLogDecorator(Log log) {
        super(log);
    }
    
    @Override
    public void error(String message) {
        log.error(message);
        send(message);
    }

    public void send(String message) {
        //模擬短信發送
        log.info("短信已發送,信息:" + message);
    }
}

/**
 * 如流日志裝飾器
 */
public class InfoflowLogDecorator extends LogDecorator {
    public InfoflowLogDecorator(Log log) {
        super(log);
    }

    @Override
    public void warn(String message) {
        log.warn(message);
        send(message);
    }
    
    @Override
    public void error(String message) {
        log.error(message);
        send(message);
    }

    
    public void send(String message) {
        //模擬如流發送
        log.info("如流消息已發送,信息:" + message);
    }
}

/**
 * 日志測試類
 */
public class LogTest {

    /**
     * 測試日志裝飾器
     */
    @Test
    public void testLogDecorator() {
        Log log = new SMSLogDecorator(new InfoFlowLogDecorator(new MailLogDecorator(new Slf4jLog())));
        log.debug("系統調試開啟");
        log.info("系統正常運行");
        log.warn("數據為空警告");
        log.error("db 連接錯誤");
    }
}
===========output=========
15:16:56.564 [main] DEBUG system_log - 系統調試開啟
15:16:56.566 [main] INFO  system_log - 系統正常運行
15:16:56.566 [main] WARN  system_log - 數據為空警告
15:16:56.566 [main] INFO  system_log - 郵件已發送,信息:數據為空警告
15:16:56.566 [main] INFO  system_log - 如流消息已發送,信息:數據為空警告
15:16:56.566 [main] ERROR system_log - db 連接錯誤
15:16:56.566 [main] INFO  system_log - 郵件已發送,信息:db 連接錯誤
15:16:56.566 [main] INFO  system_log - 如流消息已發送,信息:db 連接錯誤
15:16:56.566 [main] INFO  system_log - 短信已發送,信息:db 連接錯誤

Process finished with exit code 0

四、總結

如上幾個案例,裝飾器的最大作用就是在不修改原有類的基礎上擴展已有的功能,它符合開閉原則,而且實現也比較靈活。

---------- END ----------

推薦閱讀【技術加油站】系列:

百度工程師教你玩轉設計模式(工廠模式)

百度工程師教你玩轉設計模式(適配器模式)

百度工程師教你玩轉設計模式(單例模式)

圖片

展開閱讀全文
  • 0
    感動
  • 0
    路過
  • 0
    高興
  • 0
    難過
  • 0
    搞笑
  • 0
    無聊
  • 0
    憤怒
  • 0
    同情
熱度排行
友情鏈接
18禁高潮出水呻吟娇喘mp3,日本熟妇乱人伦A片免费高清,成人午夜精品无码区,狠狠色噜噜色狠狠狠综合久久,麻豆一区二区99久久久久,年轻的妈妈4,少妇被又大又粗又爽毛片,护士张开腿让我爽了一夜,男男互攻互受h啪肉np文,你好神枪手电视剧免费观看啊,97人妻一区二区精品免费,久久久婷婷五月亚洲97号色,freegaysexvideos男男中国,国产精品国产三级国av麻豆,国产精品又黄又爽又色无遮挡网站,亚洲av无码一区二区三区网站,亚洲国产精品久久久久蜜桃,国产真人无码作爱视频免费,国产成人精品亚洲一区二区三区,亚洲欧洲日产最新,老司机带带我精彩免费,国产成人久久精品激情,日本最新av免费一区二区三区,边摸边吃奶又黄又激烈视频
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>