Python 中的面向接口編程

前言

」面向接口編程「Java 的朋友耳朵已經能夠聽出幹繭了吧,固然這個思想在 Java 中很是重要,甚至幾乎全部的編程語言都須要,畢竟程序具備良好的擴展性、維護性誰都不能拒絕。python

最近無心間看到了我剛開始寫 Python 時的部分代碼,當時實現的需求有個很明顯的特色:編程

  • 不一樣對象具備公共的行爲能力,但具體每一個對象的實現方式又各不相同。

說人話就是商戶須要接入平臺,接入的步驟相同,但具體實現不一樣。markdown

做爲一個」資深「 Javaer,需求還沒看完我就洋洋灑灑的把各個實現類寫好了:編程語言

固然最終也順利實現需求,甚至把組裏一個沒寫過 Java 的大哥唬的一愣一愣的,直呼牛逼。ui

不過過後也給我吐槽:spa

  • 你這設計是不錯,可是感受好複雜,跟代碼時要找到真正的業務邏輯(實現類)得繞幾圈。

截止目前 Python 寫多了,我總算是能總結他的感覺:就是不夠 Pythonic翻譯

雖然說 Python 沒有相似 Java 這樣的 Interface 特性,但做爲面向對象的高級語言也是支持繼承的;設計

在這裏咱們也能夠利用繼承的特性來實現面向接口編程:3d

class Car:
    def run(self):
        pass

class Benz(Car):
    def run(self):
        print("benz run")

class BMW(Car):

    def run(self):
        print("bwm run")

def run(car):
    car.run()

if __name__ == "__main__":
    benz = Benz()
    bmw = BMW()

    run(benz)
    run(bmw)
複製代碼

代碼很是簡單,在 Python 中也沒有相似於 Java 中的 extends 關鍵字,只須要在類聲明末尾用括號包含基類便可。code

這樣在每一個子類中就能單獨實現業務邏輯,方便擴展和維護。

類型檢查

因爲 Python 做爲一個動態類型語言,沒法作到 Java 那樣在編譯期間校驗一個類是否徹底實現了某個接口的全部方法。

爲此 Python 提供瞭解決辦法,那就是 abc(Abstract Base Classes) ,當咱們將基類用 abc 聲明時就能近似作到:

import abc
class Car(abc.ABC):
 @abc.abstractmethod
    def run(self):
        pass

class Benz(Car):
    def run(self):
        print("benz run")

class BMW(Car):
    pass

def run(car):
    car.run()

if __name__ == "__main__":
    benz = Benz()
    bmw = BMW()

    run(benz)
    run(bmw)
複製代碼

一旦有類沒有實現方法時,運行期間便會拋出異常:

bmw = BMW()
TypeError: Can't instantiate abstract class BMW with abstract methods run 複製代碼

雖然沒法作到在運行以前(畢竟不須要編譯)進行校驗,但有總比沒有好。

鴨子類型

以上兩種方式看似已經畢竟優雅的實現面向接口編程了,但實際上也不夠 Pythonic

在繼續以前咱們先聊聊接口的本質究竟是什麼?

Java 這類靜態語言中面向接口編程是比較麻煩的,也就是咱們常說的子類向父類轉型,所以須要編寫額外的代碼。

帶來的好處也是顯而易見,只須要父類即可運行。

但咱們也沒必要過於執着於接口,它自己只是一個協議、規範,並不特指 Java 中的 Interface,甚至有些語言壓根沒有這個關鍵字。

動態語言的特性也不須要強制校驗是否實現了方法。

Python 中咱們能夠利用鴨子類型來優雅的實現面向接口編程。

在這以前先了解下鴨子類型,借用維基百科的說法:

  • 「當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就能夠被稱爲鴨子。」

我用大白話翻譯下就是:

即使兩個徹底不想幹的類,若是他們都實現了相同的方法,那就能夠把他們當作同一類型的類來使用。

舉個簡單例子:

class Order:
    def create(self):
        pass

class User:
    def create(self):
        pass

def create(obj):
    obj.create()

if __name__ == "__main__":
    order = Order()
    user = User()
    create(order)
    create(user)
複製代碼

這裏的 orderuser 自己徹底沒有關係,只是他們都有相同方法,又得益於動態語言無法校驗類型的特色,因此徹底能夠在運行的時候認爲他們是同一種類型。

所以基於鴨子類型,以前的代碼咱們能夠稍做簡化:

class Car:
    def run(self):
        pass

class Benz:
    def run(self):
        print("benz run")

class BMW:
    def run(self):
        print("bwm run")

def run(car):
    car.run()

if __name__ == "__main__":
    benz = Benz()
    bmw = BMW()

    run(benz)
    run(bmw)
複製代碼

由於在鴨子類型中咱們在乎的是它的行爲,而不是他們的類型;因此徹底能夠不用繼承即可以實現面向接口編程。

總結

我以爲平時沒有接觸過動態類型語言的朋友,在瞭解完這些以後會發現新大陸,就像是 Python 老手第一次使用 Java 時;雖然以爲語法囉嗦,但也會羨慕它的類型檢查、參數驗證這類特色。

動靜語言之爭這裏不作討論了,各有各的好,鞋好很差穿只有本身知道。

隨便提一下其實不止動態語言具有鴨子類型,有些靜態語言也能玩這個騷操做,感興趣下次再介紹。

相關文章
相關標籤/搜索