通俗 Python 設計模式——介面卡模式

平田發表於2017-01-02

從這篇開始,介紹書中第二種型別的設計模式——結構型模式。結構型模式主要用於處理系統中不同實體間的關係,如 話題與評論 這類場景。今天介紹其中的介面卡模式

介面卡模式,通俗的說就是設計 介面/api,以保證程式符合 開放/封閉 原則,保持新老程式碼間的相容性。

介面卡模式在實際開發中的使用非常頻繁,通常在系統的不同層次間,以及為系統新增擴充套件功能等場景時使用。因為通常情況下,原系統的程式碼要麼無法獲取——如庫等、要麼難以冒險重構——如執行5年以上的老舊系統牽一髮而動全身。在設計中使用介面卡模式,可以保證在不修改原系統程式碼的前提下,實現新需求與原系統的對接。

Python 實現介面卡模式有多種方式,這裡使用書中提供的示例作為介紹。

假設場景:

存在一套舊系統,裡面包含 HumanSynthesizer 類,如下:

class Synthesizer:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'the {} synthesizer'.format(self.name)

    def play(self):
        return 'is playing an electronic song'

class Human:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return '{} the human'.format(self.name)

    def speak(self):
        return 'says hello'

現在新增 Computer 類如下:

class Computer:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'the {} computer'.format(self.name)

    def execute(self):
        return 'executes a program'

並且對於擴充套件系統來說,所有動作函式均使用 Obj.execute() 來執行。即對於呼叫者來說,原系統的 Synthesizer.play()Human.speak() 是不存在的,必須像呼叫 Computer.execute() 一樣使用 Synthesizer.execute()Human.execute() 來呼叫原系統中物件的執行函式。

這就是我們之前提到的場景,無法修改原系統函式,此時新系統就可以採用介面卡模式進行設計。

我們可以建立一個 Adapter 類專門用於統一介面,程式碼如下。

class Adapter:
    def __init__(self, obj, adapted_methods):
        self.obj = obj
        self.__dict__.update(adapted_methods)

    def __str__(self):
        return str(self.obj)

簡單來說,這裡是使用了 Python 的一個特殊語法糖 class.__dict__ 屬性,即類的內部字典。這個特殊的屬性是一個字典,存放了這個類所包含的所以屬性,包括方法。所以這裡將傳入的類進行處理,將需要被介面卡處理的方法新增到內部字典中,生成一個屬於這個新介面卡物件的方法。

接下來,只需要在呼叫時,對原有系統的類進行封裝,即可實現統一使用 execute() 方法執行動作了。程式碼如下.

def main():
    objects = [Computer('Asus')]
    synth = Synthesizer('moog')
    objects.append(Adapter(synth, dict(execute=synth.play)))
    human = Human('Bob')
    objects.append(Adapter(human, dict(execute=human.speak)))

    for i in objects:
        print('{} {}'.format(str(i), i.execute()))

簡單解釋一下,就是在例項化物件後,使用 Adapter 再將物件包裹一次,最終的呼叫其實都是呼叫了 Adapter 類的物件。這裡我們修改一下 main() 函式,在迴圈體新增一條列印物件型別的語句:

def new_main():
    objects = [Computer('Asus')]
    synth = Synthesizer('moog')
    objects.append(Adapter(synth, dict(execute=synth.play)))
    human = Human('Bob')
    objects.append(Adapter(human, dict(execute=human.speak)))

    for i in objects:
        print('{} {}'.format(str(i), i.execute()))
        print('type is {}'.format(type(i)))

執行結果如下:

the Asus computer executes a program
type is <class '__main__.Computer'>
the moog synthesizer is playing an electronic song
type is <class '__main__.Adapter'>
Bob the human says hello
type is <class '__main__.Adapter'>

可以看到,擴充套件系統實現的 Computer 類的物件因為不需要使用介面卡處理,所以被識別為 Computer 類,但是經過 Adapter 處理後的 synthhuman 物件,已經不能被識別為 SynthesizerHuman 類,而是被識別為 Adapter 類了。如此一來,就實現了統一介面的目標。

從上面的例子可以看到,介面卡模式在需要進行增量式開發的系統中是非常有用的,可以在不修改舊系統的前提下,保證程式碼的一致性。在合適的場景下有效的提高了程式碼的健壯性和相容性。


其實還有一種實現方案,在 這裡 可以看到。

相關文章