python設計模式-外觀模式

上一篇《python設計模式-適配器模式》介紹瞭如何將一個類的接口轉換成另一個符合期望的接口。這一篇將要介紹需要一個爲了簡化接口而改變接口的新模式-外觀模式(Facade-Pattern)。

問題

問題:如果你組裝了一套家庭影院,內含播放器、投影機、自動屏幕、立體聲音響、爆米花機等。如何設計一個遙控器,可以簡單的操作這個系統中的各個組件呢?

首先來看一下最笨方式觀賞電影的步驟:

  1. 打開爆米花機
  2. 開始爆米花
  3. 將燈光調暗
  4. 放下屏幕
  5. 打開投影儀
  6. 將投影機的輸入切換到播放器
  7. 將投影及設置在寬屏模式
  8. 打開功放
  9. 將功放的輸入設置爲播放器
  10. 將攻防設置爲環繞立體聲
  11. 將攻防音量調到適中
  12. 打開播放器
  13. 播放電影

寫成類和方法的調用大概是以下的樣子:


# 打開爆米花機,開始爆米花
poper.on()
poper.pop()
# 燈光調暗
lights.dim(10)

# 放下屏幕
screen.down()

# 打開投影儀,設置爲寬屏模式
projector.on()
projector.setInput(dvd)
projector.wideScreenMode()

# 打開功放 設置爲DVD 調整成環繞立體聲模式,音量調到5
amp.on()
amp.setDvd(dvd)
amp.setSurroundSound()
amp.setVolume(5)

# 打開dvd 播放器
dvd.on()
dvd.play(movie)

可以看到代碼中涉及到6個不同的類,而且電影看完後還需要回退,一切都要再反着重來一遍。怎樣簡化一下操作呢?
現在,外觀模式就可以大展身手了。

使用外觀模式,可以通過實現一個提供更合理的接口的外觀類,將子系統變得更容易使用。當然,原來的接口還在。

解決方法

先來看一下外觀模式如何運作

外觀模式類圖

  1. 這裏爲家庭影院系統創建了一個新的外觀類HomeTheaterFacade,這個類暴露出來幾個簡單的方法,比如watchMovieendMovie
  2. 這個外觀類將家庭影院的多個組件看作一個子系統,通過調用這個子系統來實現watchMovie方法。
  3. 外觀只提供了一個更直接的操作方式,並沒有將原來的子系統隔離,子系統的功能還可以使用

注意:

  1. 可以有多個外觀
  2. 外觀提供簡化的接口,但不隔離子系統
  3. 外觀將實現從子系統中解耦,比如:現在有個子系統的組件需要升級換代,只需要把外觀代碼做相應的修改就可以實現
  4. 外觀和適配器都可以包裝多個類,但是外觀的意圖時簡化接口的調用,而適配器的意圖是將接口轉換成不同的接口

示例


class HomeTheaterFacade(object):

    #先聲明需要用的子組件
    amp = Amplifier()
    tuner = Tuner()
    dvd = DvdPlayer()
    cd = CdPlayer()
    projector = Projector()
    lights = TheaterLights()
    screen = Screen()
    popper = PopcornPopper()
    
    def watchMovie(self, movie):
        # watchMovie 將之前需要手動處理的任務批量處理
        print("Get ready to watch a movie...")
        # 打開爆米花機,開始爆米花
        self.poper.on()
        self.poper.pop()
        # 燈光調暗
        self.lights.dim(10)

        # 放下屏幕
        self.screen.down()

        # 打開投影儀,設置爲寬屏模式
        self.projector.on()
        self.projector.setInput(dvd)
        self.projector.wideScreenMode()

        # 打開功放 設置爲DVD 調整成環繞立體聲模式,音量調到5
        self.amp.on()
        self.amp.setDvd(dvd)
        self.amp.setSurroundSound()
        self.amp.setVolume(5)

        # 打開dvd 播放器
        self.dvd.on()
        self.dvd.play(movie)
    
    def endMovie(self):
        # endMovie 負責關閉一切,由子系統中的組件完成
        print("Shutting movie theater down...")
        self.popper.off()
        self.lights.on()
        self.screen.up()
        self.projector.off()
        self.amp.off()
        self.dvd.stop()
        self.dvd.eject()
        self.dvd.off()
代碼使用
def main():
    home_theater = HomeTheaterFacade() # 實例化外觀
    home_theater.watchMovice() # 使用簡化方法開啓 關閉電影ß
    home_theater.endMovice()

定義

定義:外觀模式提供了一個統一的接口,用來訪問子系統中的一羣接口。外觀定義了一個高層接口,讓子系統更容易使用。

外觀模式類圖

從類圖也可以瞭解到,外觀模式的主要意圖是提供一個更簡單易用的接口。

最少知識原則(least Knowledge)

最少知識原則的意思是減少對象之間的交互,只和幾個特定的對象交互。

這個原則是希望在設計中,不要耦合太多的類,以免修改系統時,會影響到其它部分。

比如:如果想從DVD播放器獲取音響的音量,可以在Dvd播放器中加入一個方法,用來像音響請求當前音量,而不是先返回音響對象,再從音響對象返回音量。

# 不好的實踐
def get_volume():
    tuner = dvd.tuner()
    return tuner.get_volume
    

# 好的實踐
def get_volume():
    # 這裏要給dvd 對象加一個get_volume方法
    return dvd.get_volume
缺點:雖然這個原則減少了對象之間的依賴,但是也會導致更多的包裝被製造出來(比如上邊例子中,就需要給dvd 加一個 get_volume方法),這也可能會導致系統更復雜。

再回顧一下外觀模式的例子,會發現外觀模式符合最少知識原則,客戶端只有HomeTheaterFacade這一個交互對象。它的存在讓系統調用變的更簡單,並且如果需要子系統有模塊需要升級,只需要修改HomeTheaterFacade這個類就可以完成升級。


本文例子來自《Head First 設計模式》。

最後,感謝女朋友支持和包容,比❤️

也可以在公號輸入以下關鍵字獲取歷史文章:公號&小程序 | 設計模式 | 併發&協程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章