软件体系结构的大统一理论

2020-10-28 13:51:13

以Bob叔叔的Clean Architecture为例,将其对应关系与Gary Bernhardt围绕功能核心的薄命令性外壳进行映射,您就会了解如何以较低的成本维护和扩展软件!

这就是布兰登·罗兹先生所做的。我并不是每天都能找到如此清晰的洞察力。

我很荣幸能找到他的演示文稿和幻灯片,解释鲍勃大叔2011年、2012年和2013年的清洁建筑和加里·伯恩哈特的PyCon演讲。

Rhodes先生提供了如此精炼的视图,以至于他可以用3张代码幻灯片向您展示这些关键概念。我将继续总结他所说的话,并补充一点我的见解。

本页上所有Python代码的版权属于Brandon Rhodes先生,图表的版权属于Robert C.Martin(Bob叔叔)。我在(希望)合理使用的情况下使用这些(非营利和教育性的)。

首先,我们需要达成一致,才能相互理解。以下是我将使用的词语:

函数:我使用";函数&34;或";纯函数";来引用一个Python";函数";,该函数只将其参数用于输入,返回结果作为输出,并且不会导致任何其他副作用(如I/O)。纯函数可以在不更改系统状态的情况下被调用任意次-它不应该影响DB、UI、其他函数或类。

这非常类似于数学函数:将您从x带到y,不会发生任何其他事情。

过程、例程或子例程:执行的一段代码,它可能有副作用,也可能没有副作用。这是Python中的";函数";,但可能不是纯函数";。

测试:自动化单元测试。我所说的单元不一定只是一个类,而是一种行为。如果您愿意,请在我上一篇文章的耦合一章中查看更多详细信息。

Import request#list1 from urllib import urlencode def Find_Definition(Word):q=';定义';+word url=';http://api.duckduckgo.com/?';url+=urlencode({';q';:q,#39;format';:';json';})Response=Requests。Get(Url)#I/O数据=响应。Json()#I/O定义=数据[u';定义';]如果定义==u';';:提高ValueError(';这不是一个单词';)返回定义。

在这里,我们有一段代码,它准备一个URL,然后通过网络获取一些数据(I/O),然后验证结果(单词定义)并返回它。

这有点过头了:理想情况下,一个过程应该只做一件事。虽然这个小的过程仍然具有相当的可读性,但它是对一个更发达的系统的隐喻-在那里它可能是任意长的。

当前的下意识反应是将I/O操作隐藏在遥远的地方。以下是提取I/O行后的相同代码:

Def Find_Definition(Word):#清单2q=';定义';+word url=';http://api.duckduckgo.com/?';url+=urlencode({';q';:q,';格式';:';json';})data=call_json_api(Url)定义=数据[u';定义';]如果定义==u';';:raise ValueError(';这不是一个单词';)返回定义def call_json_api(Url):Response=Requests。Get(Url)#I/O数据=响应。Json()#I/O返回数据。

问题是,代码仍然是耦合的-只要您想测试任何东西-即使是URL的构建或结果的解析,都会调用call_json_api。

发现耦合的一个很好的经验法则是:您可以在不像Frankenstein那样模拟或注入依赖项的情况下测试一段代码吗?

在这里,我们可以在不从内部替换call_json_API的情况下测试find_Definition,以避免发出HTTP请求。

Def find_Definition(Word):#list3url=build_url(Word)data=requests。获取(Url)。Json()#I/O return Pluuck_Definition(Data)def build_url(Word):q=';定义';+word url=';http://api.duckduckgo.com/?';url+=urlencode({';q';:q,';Format';:';json';})返回url def Pluuck_Definition(Data):Definition=Data[u';定义';]如果定义==u';';:提高值错误(';那不是一个单词';)返回定义。

这里,顶部的程序(又名。程序的命令性shell)正在处理I/O,其他所有内容都移到纯函数(build_url、pluck_ition)。只需在合成的数据结构上调用纯函数,就可以很容易地测试它们;不需要科学怪人。

这种分离为命令性外壳和功能核心是函数式编程鼓励的想法。

不过,理想情况下,在真实的系统中,您不会测试像这些例程这样小的元素,而是集成更多的系统。请参阅我上一篇文章的耦合章节,以了解其中的权衡。

看看鲍勃叔叔的清洁建筑图表(版权所有罗伯特·C·马丁,又名。鲍勃叔叔):

Bob叔叔的用例和实体(图表的红色和黄色圆圈)映射到我们前面看到的纯函数--清单3中的build_url和pluck_ition。

Bob';叔叔的接口适配器(绿色圆圈)映射到清单3中前面的-find_Definition中的顶级命令性shell,只处理到外部(Web、DB、UI和其他框架)的I/O。

请注意圆圈左侧的箭头,它们向内指向越来越多的抽象部分。这些是过程或函数调用。我们的代码是由外部调用的。

这使我们可以轻松地测试功能核心。理想情况下,生产系统的大部分应该是纯功能的。

如果减少命令性shell并将代码移到功能核心中,则每个测试都可以验证几乎整个(现在可以正常工作的)堆栈,但不能实际执行外部操作。

然后,您可以使用较少的集成测试来测试命令性shell:您只需要检查它是否正确连接到功能核心。

让系统有两个用户-真正的用户和单元测试-并听取这两个用户的意见,可以让您指导您的体系结构,从而最大限度地减少耦合,并构建更灵活的系统。

拥有灵活的系统可以让您快速且廉价地实施新功能和更改现有功能,以保持企业竞争力。

我们非常感谢您的意见。我还没有应用这些洞察力,我可能遗漏了一些东西!