天天看點

擴充系統功能——裝飾模式(一)

      盡管目前房價依舊很高,但還是阻止不了大家對新房的渴望和買房的熱情。如果大家買的是毛坯房,無疑還有一項艱巨的任務要面對,那就是裝修。對新房進行裝修并沒有改變房屋用于居住的本質,但它可以讓房子變得更漂亮、更溫馨、更實用、更能滿足居家的需求。在軟體設計中,我們也有一種類似新房裝修的技術可以對已有對象(新房)的功能進行擴充(裝修),以獲得更加符合使用者需求的對象,使得對象具有更加強大的功能。這種技術對應于一種被稱之為裝飾模式的設計模式,本章将介紹用于擴充系統功能的裝飾模式。

12.1 圖形界面構件庫的設計

      Sunny軟體公司基于面向對象技術開發了一套圖形界面構件庫VisualComponent,該構件庫提供了大量基本構件,如窗體、文本框、清單框等,由于在使用該構件庫時,使用者經常要求定制一些特效顯示效果,如帶滾動條的窗體、帶黑色邊框的文本框、既帶滾動條又帶黑色邊框的清單框等等,是以經常需要對該構件庫進行擴充以增強其功能,如圖12-1所示:
擴充系統功能——裝飾模式(一)

圖12-1  帶滾動條的窗體示意圖

      如何提高圖形界面構件庫性的可擴充性并降低其維護成本是Sunny公司開發人員必須面對的一個問題。

      Sunny軟體公司的開發人員針對上述要求,提出了一個基于繼承複用的初始設計方案,其基本結構如圖12-2所示:

擴充系統功能——裝飾模式(一)

圖12-2 圖形界面構件庫初始設計方案

      圖12-2中,在抽象類Component中聲明了抽象方法display(),其子類Window、TextBox等實作了display()方法,可以顯示最簡單的控件,再通過它們的子類來對功能進行擴充,例如,在Window的子類ScrollBarWindow、BlackBorderWindow中對Window中的display()方法進行擴充,分别實作帶滾動條和帶黑色邊框的窗體。仔細分析該設計方案,我們不難發現存在如下幾個問題:

        (1) 系統擴充麻煩,在某些程式設計語言中無法實作。如果使用者需要一個既帶滾動條又帶黑色邊框的窗體,在圖12-2中通過增加了一個新的類ScrollBarAndBlackBorderWindow來實作,該類既作為ScrollBarWindow的子類,又作為BlackBorderWindow的子類;但現在很多面向對象程式設計語言,如Java、C#等都不支援多重類繼承,是以在這些語言中無法通過繼承來實作對來自多個父類的方法的重用。此外,如果還需要擴充一項功能,例如增加一個透明窗體類TransparentWindow,它是Window類的子類,可以将一個窗體設定為透明窗體,現在需要一個同時擁有三項功能(帶滾動條、帶黑色邊框、透明)的窗體,必須再增加一個類作為三個窗體類的子類,這同樣在Java等語言中無法實作。系統在擴充時非常麻煩,有時候甚至無法實作。

       (2)代碼重複。從圖12-2中我們可以看出,不隻是窗體需要設定滾動條,文本框、清單框等都需要設定滾動條,是以在ScrollBarWindow、ScrollBarTextBox和ScrollBarListBox等類中都包含用于增加滾動條的方法setScrollBar(),該方法的具體實作過程基本相同,代碼重複,不利于對系統進行修改和維護。

        (3) 系統龐大,類的數目非常多。如果增加新的控件或者新的擴充功能系統都需要增加大量的具體類,這将導緻系統變得非常龐大。在圖12-2中,3種基本控件和2種擴充方式需要定義9個具體類;如果再增加一個基本控件還需要增加3個具體類;增加一種擴充方式則需要增加更多的類,如果存在3種擴充方式,對于每一個控件而言,需要增加7個具體類,因為這3種擴充方式存在7種組合關系(大家自己分析為什麼需要7個類?

擴充系統功能——裝飾模式(一)

)。

      總之,圖12-2不是一個好的設計方案,怎麼辦?如何讓系統中的類可以進行擴充但是又不會導緻類數目的急劇增加?不用着急,讓我們先來分析為什麼這個設計方案會存在如此多的問題。根本原因在于複用機制的不合理,圖12-2采用了繼承複用,例如在ScrollBarWindow中需要複用Window類中定義的display()方法,同時又增加新的方法setScrollBar(),ScrollBarTextBox和ScrollBarListBox都必須做類似的處理,在複用父類的方法後再增加新的方法來擴充功能。根據“合成複用原則”,在實作功能複用時,我們要多用關聯,少用繼承,是以我們可以換個角度來考慮,将setScrollBar()方法抽取出來,封裝在一個獨立的類中,在這個類中定義一個Component類型的對象,通過調用Component的display()方法來顯示最基本的構件,同時再通過setScrollBar()方法對基本構件的功能進行增強。由于Window、ListBox和TextBox都是Component的子類,根據“裡氏代換原則”,程式在運作時,我們隻要向這個獨立的類中注入具體的Component子類的對象即可實作功能的擴充。這個獨立的類一般稱為裝飾器(Decorator)或裝飾類,顧名思義,它的作用就是對原有對象進行裝飾,通過裝飾來擴充原有對象的功能。

      裝飾類的引入将大大簡化本系統的設計,它也是裝飾模式的核心,下面讓我們正式進入裝飾模式的學習。

繼續閱讀