在閱讀 clean architecture的過程當中,會發現做者常常提到recompile
redeploy
,這些術語看起來都跟靜態類型語言有關,好比Java、C++、C#。而在我常用的python語言中,是不存在這些概念的。因而,在閱讀的時候就會有一個疑惑,《clean architecture》中提到的各類原則,好比SOLID,是否對動態類型語言 -- 如python -- 一樣適用?html
SOLID是面向對象設計的指導原則,更具適用性的應該是各類設計模式,GOF經典的Design Patterns: Elements of Reusable Object-Oriented Software 也是用C++來舉例的,那麼這些經典設計模式有多少是適用於動態語言如python的呢?本文記錄對這些問題淺薄的思考,若是有認知錯誤的地方,還請你們不吝指教。python
本文地址:http://www.javashuo.com/article/p-vzkrjsdi-eb.htmlc++
SOLID是模塊(module)設計的指導原則,有如下五個原則組成程序員
首先來看ISP,接口隔離原則,《clean architecture》的做者認可這是一個語言相關的原則數據庫
This fact could lead you to conclude that the ISP is a language issue, rather than an architecture issue.編程
爲何呢, ISP主要是爲了解決「胖接口」致使的沒必要要的 recompilation and redeployment, 以下所示:
設計模式
Use1對op1
的使用致使OPS的修改,致使User2 User3也要從新編譯。而在動態語言中是不存在從新編譯這樣的問題的:架構
In dynamically typed languages like Ruby and Python, such declarations don’t exist in source code. Instead, they are inferred at runtime. Thus there are no source code dependencies to force recompilation and redeployment編程語言
DIP(依賴倒置原則)是SOLID的核心,OCP其實就依賴於DIP。也能夠說,DIP是「clean architecture」的核心。函數
「clean architecture」由兩部分組成:
什麼是」Dependency rule"呢?讓低層的detail去依賴高層的policy。好比,業務邏輯(business rule)就相比數據存儲(database)出於更高層,雖然邏輯上是業務邏輯要使用數據庫,但爲了可維護性、可擴展性,架構設計上得讓database去依賴business rule,以下所示
從上圖能夠看出,爲了達到這個目的,在靜態語言中,會聲明一個接口,調用的雙方都依賴這個接口。如上圖中的database interface
,讓business rule和database都去依賴這個接口,而這個database interface和business rule在一個component,這就實現了讓低層的database去依賴高層的business rule。
在靜態類型語言(如Java、C++)中,其實就是利用運行時多態這個特性,使得能夠在運行時 -- 而不是編譯時 -- 改變軟件的行爲,固然爲了達到這個目的,須要預先聲明一個虛基類 或者接口(Java Interface)。
而在python中,原本就是運行的時候去求值,並且由於ducking type,因此無需事先聲明接口或者強迫繼承接口
Dependency structures in these languages(dynamic typed languages) are much simpler because dependency inversion does not require either the declaration or the inheritance of interfaces.
從靜態類型語言到動態類型語言,實際上是省略了不少東西
在python中,怎麼算依賴,怎麼算依賴倒置?
'''my.py''' import other class My(object): def f(self): other.act()
這一段代碼中經過import
讓module my
依賴於module other
,
'''my.py''' class My(object): def __init__(self, actor): self._actor = actor def f(self): self._actor.act()
那麼在這裏,my和other有依賴關係嗎?沒有的,這裏壓根就沒有出現過other。因爲動態類型加上ducking type,根本無需顯式的接口定義,只要遵循相關的協議(契約)便可。而這個契約,沒辦法經過代碼強行約束,調用者須要什麼樣的接口,被調用者應該具有什麼樣的行爲,都只能經過文檔(或者單元測試)來描述。
爲了表達契約,上述代碼應該加上docstring
'''my.py''' class My(object): def __init__(self, actor): '''Param: actor,該對象須要具有接收0個參數的act方法 ''' self._actor = actor def f(self): self._actor.act()
python中大量使用相似的協議,如context management
, iterator protocol
。雖然很方便,同時也對程序員有更高要求,由於至少得有靠譜的docstring。若是須要強加約束,那是是能夠考慮使用abc的。
首先聲明的是,在本文中提到的設計模式,通常指Design Patterns: Elements of Reusable Object-Oriented Software 這本書中所描述的經典設計模式。
很早以前看過一種說法,「++設計模式是對靜態語言缺陷的彌補」++,當時沒經思考就全盤接受了,竊認爲這就是真理。最近才真正思考這個問題,發現這種說法存在偏見與不全面。
首先拋出一個問題:設計模式是語言相關嗎(language-specific)?是某種類型的編程語言須要設計模式,而另一些編程語言就不須要?或者說,不一樣的編程語言須要的設計模式是不同的?
什麼是設計模式呢,《Design Patterns》中描述爲,針對軟件設計中某一類特定問題的簡單且優美的解決方案。
Describes simple and elegant solutions to specific problems in object-oriented software design
也就是說,設計模式是解決某類特定問題的套路,或者說方法論。套路是針對某個問題,通過理論或實踐驗證的、行之有效的方法與步驟。沒有方法論也能解決問題,可能就須要去大量的嘗試、試錯,獲得一種解決辦法(大機率也不是最優解),這個求解的過程耗時且低效。所以能夠說,方法論(模式)加速了問題求解的過程。
好比,程序員天天都很大量的事情要作:要開會、要寫代碼、要處理bug、要本身充電。如何安排呢?可能本身思考這個問題就得焦頭爛額,可是已經有成熟的方法論 --艾森豪威爾矩陣-- 可供使用了啊。
咱們常說,站在巨人的肩膀上,套路、方法論就是巨人的肩膀。
設計模式一樣如此。
《Design Patterns》這本書,寫於1994年,做者提到寫這本數的目標,就是將這些行之有效的經驗記錄下來。前面提到,設計模式是針對一類問題的解決方案,那麼在介紹一種設計模式的時候,就必定會涉及到如下內容(包括但不限於):
固然,首先得給這個模式取一個恰如其分的名字,命名的重要性不容質疑。至少保證程序員之間在溝通的時候所表達的是同一個問題,無論這個溝通是peer to peer,仍是經過代碼。名字(術語、定義)也就減輕了溝通的成本。
在《Design Patterns》寫成的兩年後,即1996年,Peter Norvig就作了一個分享 「Design Patterns in Dynamic Programming」, 指出因爲動態語言存在更少的語言層面的限制,GOF中的大多數設計模式在Lisp或者Dylan有更簡單的實現,有的甚至簡單到根本無需注意
16 of 23 patterns have qualitatively simpler implementation in Lisp or Dylan than in C++ for at least some uses of each pattern
16 of 23 patterns are either invisible or simpler
那麼哪些模式變得「invisible」,哪些是「simpler」了呢?
《Design Patterns》中講設計模式大體分爲三類
因爲在動態類型語言中,類(class, type)和方法(function)都是一等公民,所以Creational patterns
在動態類型語言,如Python中就變得「invisible」。
因爲動態類型、ducking type,一些Creational patterns
如「Observer」,「Visitor」就變得「simpler」。這裏要強調的是,變得更簡單,並不意味這個這個模式就沒有存在的意義了,好比觀察者模式,或者訂閱-發佈,表明了鬆耦合的設計原則,在各個層級的設計中都是須要的。
對於這種體現更高原則、思想的設計模式,咱們應該用模式去幫助思考和溝通,而不要拘泥於樣板代碼、特定語言實現。StackExchange上的這個排比句很恰當:
- I might say that I have a visitor pattern, but in any language with first class functions it will be just a function taking a function. Instead of factory class I usually have just a factory function. - I might say I have an interface, but then it's just a couple of methods marked with comments, because there wouldn't be any other implementation (of course in python an interface is always just comments, because it's duck-typed). - I still speak of the code as using the pattern, because it's a useful way to think about it, but don't actually type in all the stuff until I really need it.
那麼回到問題,設計模式是語言相關嗎(language-specific)?
個人回答是,部分設計模式是語言相關的,部分設計模式不是語言相關的,具體到某一個特定的模式還多是變化的。
爲何呢,嚴謹一點,咱們只能說設計模式是問題相關的 -- 是關乎某個問題的。核心在於,這個問題在什麼狀況下確實是一個問題。並且,隨着發展,一個老問題會消亡,新問題會出現。
具體到編程語言,則應該關心的是一個問題是否是語言相關的。在靜態類型語言,如C++中,對象都有類型,類型決定了其行爲,那麼爲了運行時多態,就得有一個虛基類,同時還要作到OCP,這就須要各式各樣的Creational Patterns。但到了動態類型語言,這個就再也不是一個問題,所以就再也不有與之對應的模式。