【設計模式】【結構型模式】外觀模式

系統會隨著演化變得非常復雜,最終形成大量的(并且有時是令人迷惑的)類和交互,這種情況并不少見。許多情況下,我們并不想將這種復雜性暴露給客戶端。外觀設計模式有助于隱藏內部的復雜性,并通過一個簡化的接口向客戶端暴露必要的部分。本質上,外觀(Facade)是在已有復雜系統之上實現的一個抽象層。
使用外觀模式的最常見的理由是為一個復雜 系統提供單個簡單的入口點。引入外觀之后,客戶端代碼通過簡單地調用一個方法就能使用一個系統。同時,內部系統并不會丟失任何功能,外觀只是封裝了內部系統。
如果你的系統包含多層,外觀模式也能派上用場。你可以為每一層引入一個外觀入口點,并讓所有層級通過它們的外觀相互通信。這提高了層級之間的松耦合性,盡可能保持層及獨立。

實現

假設我們想使用多服務進程方式來實現一個操作系統。多服務進程的操作系統有一個極小的內核,稱為 微內核,它在特權模式下運行。系統的所有其他服務都遵從一種服務架構(驅動程序服務器、進程服務器、文件服務器等)。每個服務進程屬于一個不同的內存地址空間,以用戶模式運行在微內核之上。這種方式的優勢是操作系統更能容錯、更加可靠、更加安全。例如,由于所有的驅動程序都以用戶模式在一個驅動服務進程之上運行,所以某個驅動程序中的一個bug并不能使整個系統崩潰,也無法影響到其他的無父進程。其劣勢是性能開銷和系統編程的復雜性,因為服務進程和微內核之間,還有獨立的服務進程之間,使用消息傳遞方式進行通信。消息傳遞比宏內核所使用的共享內存模式更為復雜。

我們從server接口(這里的接口并不是指Interface,而是指一個不能直接實例化的類。)開始實現,使用一個Enum類型變量來描述一個服務進程的不同狀態,使用abc模塊來禁止對server接口直接進行初始化,并強制子類實現關鍵的boot()和kill()方法。這里假設每個服務進程的啟動、關閉及重啟都相應地需要不同的動作。如果你以前沒有用過abc模塊,請記住以下幾個重要事項。

  • 我們需要使用metaclass關鍵字來繼承ABCMeta
  • 使用@abstractmethod修飾器來聲明Server的所有子類都強制性的應該實現哪些方法
    嘗試移除一個子類方法,看看會發生什么。移除@abstractmethod修飾器之后再試試。
# /usr/bin/python
# coding:utf-8

from abc import ABCMeta, abstractmethod
from enum import Enum

State = Enum('State', 'new running sleeping restart zombine')


class Server(metaclass=ABCMeta):
    @abstractmethod
    def __init__(self):
        pass

    def __str__(self):
        return self.name

    @abstractmethod
    def boot(self):
        pass

    @abstractmethod
    def kill(self, restart=True):
        pass


class FileServer(Server):
    def __init__(self):
        '''初始化文件服務進程要求的操作'''
        self.name = 'FileServer'
        self.state = State.new

    def boot(self):
        '''啟動文件服務進程要求的操作'''
        print('booting the {}'.format(self))
        self.state = State.running

    def kill(self, restart=True):
        '''終止文件服務進程要求的操作'''
        print('Killing {}'.format(self))
        self.state = State.restart if restart else State.zombine

    def create_file(self, user, name, permissions):
        '''檢查訪問權限的有效性和用戶權限等'''
        print('trying to create the file "{}" for user "{}" with permissions {}'.format(name, user, permissions))


class ProcessServer(Server):
    def __init__(self):
        '''初始化進程服務進程要求的操作'''
        self.name = 'ProcessServer'
        self.state = State.new

    def boot(self):
        '''啟動進程服務進程要求的操作'''
        print('booting the {}'.format(self))
        self.state = State.running

    def kill(self, restart=True):
        '''終止進程服務進程要求的操作'''
        self.state = State.restart if restart else State.zombine

    def create_process(self, user, name):
        '''檢查用戶權限和生成PID等'''
        print("trying to create the process '{}' for user '{}'".format(name, user))


class OperatingSystem:
    '''外觀'''

    def __init__(self):
        self.fs = FileServer()
        self.ps = ProcessServer()

    def start(self):
        [i.boot() for i in (self.fs, self.ps)]

    def create_file(self, user, name, permissions):
        return self.fs.create_file(user, name, permissions)

    def create_process(self, user, name):
        return self.ps.create_process(user, name)


def main():
    os = OperatingSystem()
    os.start()
    os.create_file('fool', 'hello', '-rw-r-r')
    os.create_process('bar', 'ls /tmp')


if __name__ == '__main__':
    main()

輸出

booting the FileServer
booting the ProcessServer
trying to create the file "hello" for user "fool" with permissions -rw-r-r
trying to create the process 'ls /tmp' for user 'bar'

Process finished with exit code 0

在客戶端想要一個復雜系統但又不關心系統的復雜性之時,這種模式是為復雜系統提供一個簡單接口的理想方式。一臺計算機是一個外觀,因此當我們使用它時需要做的事情僅是按一個按鈕來啟動它;其余所有硬件復雜性都用戶無感知地交給BIOS、引導家在程序以及其他系統軟件來處理?,F實生活中外觀例子更多,比如,我們所致電的銀行或公司客服部門,還有啟動機動車所使用的鑰匙。
外觀是一種隱藏系統復雜性的優雅方式,因為多數情況下客戶端代碼并不應該關心系統的這些細節。

遺留問題

  • python的抽象子類如何調用父類的屬性以及方法?
  • python的父類init也為抽象方法的時候,如何在子類中進行運用?
  • 存根是什么
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容