程序員面試必備:設計模式——裝飾器模式

程序員面試必備:設計模式——裝飾器模式

前言

《設計模式自習室》系列,顧名思義,本系列文章帶你溫習常見的設計模式。主要內容有:

  • 該模式的介紹,包括: 引子、意圖(大白話解釋) 類圖、時序圖(理論規範)
  • 該模式的代碼示例:熟悉該模式的代碼長什麼樣子
  • 該模式的優缺點:模式不是萬金油,不可以濫用模式
  • 該模式的實際使用案例:瞭解它在哪些重要的源碼中被使用

該系列會逐步更新於我的博客和公眾號(博客見文章底部),也希望各位觀眾老爺能夠關注我的個人公眾號:後端技術漫談,不會錯過精彩好看的文章。

系列文章回顧

  • 【設計模式自習室】開篇:為什麼我們要用設計模式?
  • 【設計模式自習室】建造者模式
  • 【設計模式自習室】原型模式
  • 【設計模式自習室】透徹理解單例模式
  • 【設計模式自習室】理解工廠模式的三種形式
  • 【設計模式自習室】適配器模式

結構型——裝飾器模式 Decorator Patter

引子

當你需要統計一個方法的運行時間時,你會怎麼做?是不是下面這種方法呢,用兩個時間語句統計出耗時?

<code>public static void main(String[] args){
    long start = System.currentTimeMillis();
    algo(); // 執行代碼塊
    long end = System.currentTimeMillis();
    System.out.println(end - start);
}
/<code>

如果使用了裝飾器模式,你可以將統計時間的方法獨立出來,變成方法上面的@Timer:

<code>public class Foo {

    @Timer
    public void algo1() {
        ArrayList l = new ArrayList<>();
        for (int i = 0; i /<code>

這樣你就可以在不插入任何語句在原方法的情況下,統計原方法的執行時間,就像在原方法外面披了一層皮一樣,這就是裝飾器!

PS:如果你瞭解Python,那麼你可能會經常使用到裝飾器。在Java中,使用起來有一些複雜,但也是完全能夠通過註解做到的,它可以同樣應用在權限控制、日誌、緩存等方面,方便的對代碼進行解耦,讓通用的功能“切入”原先的代碼,使得開發時可以更專注於業務邏輯。

定義

裝飾模式動態地給一個對象增加一些額外的職責(Responsibility),就增加對象功能來說,裝飾模式比生成子類實現更為靈活。

其別名也可以稱為包裝器(Wrapper),與適配器模式的別名相同,但它們適用於不同的場合。根據翻譯的不同,裝飾模式也有人稱之為“油漆工模式”,它是一種對象結構型模式。

類圖

如果看不懂UML類圖,可以先粗略瀏覽下該圖,想深入瞭解的話,可以繼續谷歌,深入學習:

程序員面試必備:設計模式——裝飾器模式

裝飾模式包含如下角色:

  • Component: 抽象構件
  • ConcreteComponent: 具體構件
  • Decorator: 抽象裝飾類
  • ConcreteDecorator: 具體裝飾類
程序員面試必備:設計模式——裝飾器模式

A7ADEE73C43CE99E4C2C10189AFA3)


時序圖

時序圖(Sequence Diagram)是顯示對象之間交互的圖,這些對象是按時間順序排列的。時序圖中顯示的是參與交互的對象及其對象之間消息交互的順序。

我們可以大致瀏覽下時序圖,如果感興趣的小夥伴可以去深究一下:

程序員面試必備:設計模式——裝飾器模式

代碼實現

還記得引子中舉得例子嗎?

我們現在Python下看看如何實現這個統計方法執行時間的函數

首先,我們定義一個函數,如下:

<code>def exe_time(func):
    def new_func(*args, **args2):
        t0 = time.time()
        print "@%s, {%s} start" % (time.strftime("%X", time.localtime()), func.__name__)
        back = func(*args, **args2)
        print "@%s, {%s} end" % (time.strftime("%X", time.localtime()), func.__name__)
        print "@%.3fs taken for {%s}" % (time.time() - t0, func.__name__)
        return back
    return new_func
/<code>

接下來,我們就可以在需要計時的函數前一行引用它作為裝飾,比如:

<code>@exe_time
def foo():
    for i in xrange(10000000):
        pass
/<code>

輸出效果如下:

<code>@13:12:27, {foo} start
@13:12:27, {foo} end
@0.203s taken for {foo}
/<code>

在Java中,沒有Python這種天秀的語法糖,我們需要用註解實現,已經有網友寫的例子,這裡就不班門弄斧了。可以參考下文鏈接:

https://www.jianshu.com/p/d2dfb70fd671

使用場景舉例

Spring

spring中用到的包裝器模式在類名上有兩種表現:一種是類名中含有Wrapper,另一種是類名中含有Decorator。基本上都是動態地給一個對象添加一些額外的職責。

JDK源碼中的使用

  • java.io.BufferedInputStream(InputStream)
<code>InputStream inputStream = new InputStream() {
@Override
public int read() throws IOException {
return 0;
}
};
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
//增強了原本InputStream的功能
/<code>
  • java.io.DataInputStream(InputStream)
  • java.io.BufferedOutputStream(OutputStream)
  • java.util.zip.ZipOutputStream(OutputStream)
  • java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap

優缺點

優點

  • 裝飾模式與繼承本質其其實都是要擴展對象的功能,但是裝飾模式可以提供比繼承更多的靈活性。
  • 可以使用多個具體裝飾類來裝飾同一對象,得到功能更為強大的對象。
  • 具體構件類與具體裝飾類可以獨立變化,用戶可以根據需要增加新的具體構件類和具體裝飾類,在使用時再對其進行組合,原有代碼無須改變,符合“開閉原則”

缺點

  • 這種比繼承更加靈活機動的特性,也同時意味著裝飾模式比繼承更加易於出錯,排錯也很困難,對於多次裝飾的對象,調試時尋找錯誤可能需要逐級排查,較為煩瑣。

補充:裝飾模式與代理模式的區別

兩者都是對類的方法進行擴展,但裝飾器模式強調的是增強自身,在被裝飾之後你能夠在被增強的類上
使用增強後的功能。增強後你還是你,只不過能力更強了而已;而代理模式則強調要讓別人幫你去做一
些本身與你業務沒有太多關係的職責(記錄日誌、設置緩存)。代理模式是為了實現對象的控制,因為
被代理的對象往往難以直接獲得或者是其內部不想暴露出來。

參考

  • https://www.jianshu.com/p/d2dfb70fd671
  • https://oldj.net/blog/2010/05/22/decorator-get-execution-time
  • https://github.com/CyC2018/CS-Notes/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20%E8%A3%85%E9%A5%B0.md
  • https://blog.csdn.net/seaReal1/article/details/80252281
  • 《HEAD FIRST 設計模式》
  • 《劍指offer》

關注我

我是一名後端開發工程師。

主要關注後端開發,數據安全,爬蟲,物聯網,邊緣計算等方向,歡迎交流。

各大平臺都可以找到我

  • 微信公眾號:後端技術漫談
  • Github:@qqxx6661
  • CSDN:@後端技術漫談
  • 知乎:@後端技術漫談
  • 簡書:@後端技術漫談
  • 掘金:@後端技術漫談

原創博客主要內容

  • Java面試知識點複習全手冊
  • 設計模式/數據結構 自習室
  • Leetcode/劍指offer 算法題解析
  • SpringBoot/SpringCloud菜鳥入門實戰系列
  • 爬蟲相關技術文章
  • 後端開發相關技術文章
  • 逸聞趣事/好書分享/個人興趣

個人公眾號:後端技術漫談

程序員面試必備:設計模式——裝飾器模式

公眾號:後端技術漫談.jpg

如果文章對你有幫助,不妨收藏,投幣,轉發,在看起來~


分享到:


相關文章: