在阅读 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。但到了动态类型语言,这个就再也不是一个问题,所以就再也不有与之对应的模式。