在阅读 clean architecture的过程中,会发现作者经常提到recompile redeploy,这些术语看起来都跟静态类型语言有关,比如Java、C++、C#。而在我经常使用的python语言中,是不存在这些概念的。于是,在阅读的时候就会有一个疑惑,《clean architecture》中提到的各种原则,比如SOLID,是否对动态类型语言 -- 如python -- 同样适用?

SOLID是面向对象设计的指导原则,更具适用性的应该是各种设计模式,GOF经典的Design Patterns: Elements of Reusable Object-Oriented Software 也是用C++来举例的,那么这些经典设计模式有多少是适用于动态语言如python的呢?本文记录对这些问题浅薄的思考,如果有认知错误的地方,还请大家不吝指教。

本文地址:https://www.cnblogs.com/xybaby/p/11782293.html

SOLID

SOLID是模块(module)设计的指导原则,有以下五个原则组成

  • SRP(Single responsibility principle):单一职责原则,一个module只有一个原因修改
  • OCP(Open/closed principle):开放-关闭原则,开放扩展,关闭修改
  • LSP(Liskov substitution principle):里氏替换原则,子类型必须能够替换它们的基类型
  • ISP(Interface segregation principle):接口隔离原则,你所依赖的必须是真正使用到的
  • DIP(Dependency inversion principle):依赖倒置原则,依赖接口而不是实现(高层不需要知道底层的实现)

ISP

首先来看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

DIP(依赖倒置原则)是SOLID的核心,OCP其实就依赖于DIP。也可以说,DIP是“clean architecture”的核心。

“clean architecture”由两部分组成:

  • well-isolated components
  • dependency rule

什么是”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.

从静态类型语言到动态类型语言,其实是省略了很多东西

  • 省略了虚函数,如template method模式
  • 省略了虚基类、接口,如DIP、strategy模式

python中的依赖与依赖倒置

在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》中讲设计模式大致分为三类

  • Creational: ways and means of object instantiation
  • Structural: mutual composition of classes or objects (the Facade DP is Structural)
  • Behavioral: how classes or objects interactand distribute responsibilities among them

由于在动态类型语言中,类(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。但到了动态类型语言,这个就不再是一个问题,因此就不再有与之对应的模式。

references

SOLID原则、设计模式适用于Python语言吗的更多相关文章

  1. 设计模式之SOLID原则

    介绍 设计模式中的SOLID原则,分别是单一原则.开闭原则.里氏替换原则.接口隔离原则.依赖倒置原则.前辈们总结出来的,遵循五大原则可以使程序解决紧耦合,更加健壮. SRP 单一责任原则 OCP 开放 ...

  2. 实践GoF的23种设计模式:SOLID原则(上)

    摘要:本文以我们日常开发中经常碰到的一些技术/问题/场景作为切入点,示范如何运用设计模式来完成相关的实现. 本文分享自华为云社区<实践GoF的23种设计模式:SOLID原则(上)>,作者: ...

  3. 【学习笔记】PYTHON语言程序设计(北理工 嵩天)

    1 Python基本语法元素 1.1 程序设计基本方法 计算机发展历史上最重要的预测法则     摩尔定律:单位面积集成电路上可容纳晶体管数量约2年翻倍 cpu/gpu.内存.硬盘.电子产品价格等都遵 ...

  4. 面向对象的SOLID原则白话篇

    面向对象的SOLID原则 简介 缩写 全称 中文 S The Single Responsibility Principle 单一责任原则 O The Open Closed Principle 开放 ...

  5. google的python语言规范

    Python语言规范   Lint Tip 对你的代码运行pylint 定义: pylint是一个在Python源代码中查找bug的工具. 对于C和C++这样的不那么动态的(译者注: 原文是less ...

  6. 设计模式及Python实现

    设计模式是什么? Christopher Alexander:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心.这样你就能一次又一次地使用该方案而不必做重复劳动.” 设计 ...

  7. Python 语言规范

    Python 语言规范 pychecker  对你的代码运行pychecker 定义: pychecker 是一个在Python 源代码中查找bug 的工具. 对于C 和C++这样的不那 么动态的( ...

  8. 浅谈设计模式及python实现

    设计模式及Python实现   设计模式是什么? Christopher Alexander:“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心.这样你就能一次又一次地使用 ...

  9. Python 语言规范(Google)

    Python语言规范 Lint tip 对你的代码运行pylint 定义: pylint是一个在Python源代码中查找bug的工具. 对于C和C++这样的不那么动态的(译者注: 原文是less dy ...

随机推荐

  1. Spring boot - 梳理 - 根本上说,Spring Boot项目只不过是一个普通的Spring项目,只是使用了Spring Boot的起步依赖和自动配置

    根本上说,Spring Boot项目只不过是一个普通的Spring项目,只是使用了Spring Boot的起步依赖和自动配置

  2. Spring Boot 梳理 - 4个核心

    Spring Boot 魔法的核心:自动配置.起步依赖.命令行界面.Actuator 自动配置: 不用手动配置JdbcTemplate的Bean 不用手动配置DataSource的Bean Sprin ...

  3. 【ADO.NET--MVC】初学MVC(MVC入门)(1)

    最近一直在学MVC,本来今天想开始做项目了,但是一下手才发现还有好多好多都不懂,虽然想照搬别人的模板,但是还是觉得很虚,这也不懂哪也不懂.看来学习一门技术断不是那么简单,只要随便套套模板,看看别人代码 ...

  4. Web前端开发的应用和前景——web 1.0到web 3.0

    Web前端开发的应用和前景--web 1.0到web 3.0 Web1.0:(只读时代) 以静态.单向阅读为主,网站内信息可以直接和其他网站信息进行交互,能通过第三方信息平台同时对多家网站信息进行整合 ...

  5. .Net Core 商城微服务项目系列(八):购物车

    最近加班有点多,一周五天,四天加班到11点+,心很累.原因是我当前在的这个组比较特殊,相当于业务的架构组,要为其它的开发组提供服务和监控.所以最近更新的也少,不过这个元旦三天假应该会更新三篇. 这篇是 ...

  6. Java基础学习笔记(五) - 常用的API

    API介绍 概念:API 即应用编程程序接口.Java API是JDK中提供给我们使用的类说明文档,这些类将底层的代码实现封装.无需关心这些类是如何实现,只需要学习如何使用. 使用:通过API找到需要 ...

  7. FFmpeg(二) 解封装相关函数理解

    一.解封装基本流程 ①av_register_All()////初始化解封装,注册解析和封装的格式. ②avformat_netword_init()//初始化网络,解析rtsp协议 ③avforma ...

  8. spring5 源码深度解析----- @Transactional注解的声明式事物介绍(100%理解事务)

    面的几个章节已经分析了spring基于@AspectJ的源码,那么接下来我们分析一下Aop的另一个重要功能,事物管理. 事务的介绍 1.数据库事物特性 原子性多个数据库操作是不可分割的,只有所有的操作 ...

  9. 软件开发工具(第12章: CVS的安装及使用)

    一.CVS介绍 并发版本系统(Concurrent Versions System——CVS)是一个常用 的代码版本控制系统. 使用CVS可以对代码进行集中管理,记录代码所有的更改历史,提供 协作开发 ...

  10. IoC 之装载 BeanDefinitions 总结

    最近时间重新对spring源码进行了解析,以便后续自己能够更好的阅读spring源码,想要一起深入探讨请加我QQ:1051980588 ClassPathResource resource = new ...