
<code>”面向接口程式設計“</code>寫 <code>Java</code> 的朋友耳朵已經可以聽出幹繭了吧,當然這個思想在 <code>Java</code> 中非常重要,甚至幾乎所有的程式設計語言都需要,畢竟程式具有良好的擴充性、維護性誰都不能拒絕。
最近無意間看到了我剛開始寫 <code>Python</code> 時的部分代碼,當時實作的需求有個很明顯的特點:
不同對象具有公共的行為能力,但具體每個對象的實作方式又各不相同。
說人話就是商戶需要接入平台,接入的步驟相同,但具體實作不同。
作為一個”資深“ <code>Javaer</code>,需求還沒看完我就洋洋灑灑的把各個實作類寫好了:
當然最終也順利實作需求,甚至把組裡一個沒寫過 <code>Java</code> 的大哥唬的一愣一愣的,直呼牛逼。
不過事後也給我吐槽:
你這設計是不錯,但是感覺好複雜,跟代碼時要找到真正的業務邏輯(實作類)得繞幾圈。
截止目前 <code>Python</code> 寫多了,我總算是能總結他的感受:就是不夠 <code>Pythonic</code>。
雖說 <code>Python</code> 沒有類似 <code>Java</code> 這樣的 <code>Interface</code> 特性,但作為面向對象的進階語言也是支援繼承的;
在這裡我們也可以利用繼承的特性來實作面向接口程式設計:
代碼非常簡單,在 <code>Python</code> 中也沒有類似于 <code>Java</code> 中的 <code>extends</code> 關鍵字,隻需要在類聲明末尾用括号包含基類即可。
這樣在每個子類中就能單獨實作業務邏輯,友善擴充和維護。
由于 <code>Python</code> 作為一個動态類型語言,無法做到 <code>Java</code> 那樣在編譯期間校驗一個類是否完全實作了某個接口的所有方法。
為此 <code>Python</code> 提供了解決辦法,那就是 <code>abc(Abstract Base Classes)</code> ,當我們将基類用 <code>abc</code> 聲明時就能近似做到:
一旦有類沒有實作方法時,運作期間便會抛出異常:
雖然無法做到在運作之前(畢竟不需要編譯)進行校驗,但有總比沒有好。
以上兩種方式看似已經畢竟優雅的實作面向接口程式設計了,但實際上也不夠 <code>Pythonic</code>。
在繼續之前我們先聊聊<code>接口</code>的本質到底是什麼?
在 <code>Java</code> 這類靜态語言中面向接口程式設計是比較麻煩的,也就是我們常說的子類向父類轉型,是以需要編寫額外的代碼。
帶來的好處也是顯而易見,隻需要父類便可運作。
但我們也不必過于執着于接口,它本身隻是一個協定、規範,并不特指 <code>Java</code> 中的 <code>Interface</code>,甚至有些語言壓根沒有這個關鍵字。
動态語言的特性也不需要強制校驗是否實作了方法。
在 <code>Python</code> 中我們可以利用鴨子類型來優雅的實作面向接口程式設計。
在這之前先了解下鴨子類型,借用維基百科的說法:
“當看到一隻鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。”
我用大白話翻譯下就是:
即便兩個完全不想幹的類,如果他們都實作了相同的方法,那就可以把他們當做同一類型的類來使用。
舉個簡單例子:
這裡的 <code>order</code> 和 <code>user</code> 本身完全沒有關系,隻是他們都有相同方法,又得益于動态語言沒法校驗類型的特點,是以完全可以在運作的時候認為他們是同一種類型。
是以基于鴨子類型,之前的代碼我們可以稍作簡化:
因為在鴨子類型中我們在意的是它的行為,而不是他們的類型;是以完全可以不用繼承便可以實作面向接口程式設計。
我覺得平時沒有接觸過動态類型語言的朋友,在了解完這些之後會發現新大陸,就像是 <code>Python</code> 老手第一次使用 <code>Java</code> 時;雖然覺得文法啰嗦,但也會羨慕它的類型檢查、參數驗證這類特點。
動靜語言之争這裡不做讨論了,各有各的好,鞋好不好穿隻有自己知道。
随便提一下其實不止動态語言具備鴨子類型,有些靜态語言也能玩這個騷操作,感興趣下次再介紹。
作者:
crossoverJie
出處:
https://crossoverjie.top
歡迎關注部落客公衆号與我交流。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出,
如有問題, 可郵件(crossoverJie#gmail.com)咨詢。