」面向接口編程「
寫 Java
的朋友耳朵已經能夠聽出幹繭了吧,固然這個思想在 Java
中很是重要,甚至幾乎全部的編程語言都須要,畢竟程序具備良好的擴展性、維護性誰都不能拒絕。python
最近無心間看到了我剛開始寫 Python
時的部分代碼,當時實現的需求有個很明顯的特色:編程
說人話就是商戶須要接入平臺,接入的步驟相同,但具體實現不一樣。編程語言
做爲一個」資深「 Javaer
,需求還沒看完我就洋洋灑灑的把各個實現類寫好了:翻譯
固然最終也順利實現需求,甚至把組裏一個沒寫過 Java
的大哥唬的一愣一愣的,直呼牛逼。設計
不過過後也給我吐槽:code
截止目前 Python
寫多了,我總算是能總結他的感覺:就是不夠 Pythonic
。對象
雖然說 Python
沒有相似 Java
這樣的 Interface
特性,但做爲面向對象的高級語言也是支持繼承的;繼承
在這裏咱們也能夠利用繼承的特性來實現面向接口編程:接口
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
關鍵字,只須要在類聲明末尾用括號包含基類便可。it
這樣在每一個子類中就能單獨實現業務邏輯,方便擴展和維護。
因爲 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)
這裏的 order
和 user
自己徹底沒有關係,只是他們都有相同方法,又得益於動態語言無法校驗類型的特色,因此徹底能夠在運行的時候認爲他們是同一種類型。
所以基於鴨子類型,以前的代碼咱們能夠稍做簡化:
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
時;雖然以爲語法囉嗦,但也會羨慕它的類型檢查、參數驗證這類特色。
動靜語言之爭這裏不作討論了,各有各的好,鞋好很差穿只有本身知道。
隨便提一下其實不止動態語言具有鴨子類型,有些靜態語言也能玩這個騷操做,感興趣下次再介紹。