写点什么

多数程序员难以用简单的方式开发应用?

2019 年 8 月 22 日

多数程序员难以用简单的方式开发应用?

心理学中有一篇相当古老、但又非常重要的论文,题为《魔法数字七(上下浮动二):人类信息处理能力中的一些限制》。这篇文章衡量了大脑处理信息的极限,并给出了一个具体的数字:人脑可以同时容纳五到九个概念。我们当然能够把这个有趣的结论延伸到诸多领域当中,但对软件开发人员而言,下面两项含义最为重要:


  • 构造(模型、实现、设计、模式等……)越简单越好,因为其概念更易于描述。

  • 构造良好且组合良好的抽象,应该具有较少的特殊规则与意外情况,同样是因为其概念更易于描述。



总而言之,这是一场以降低概念描述难度为目标的心理战,夺取的是珍贵的心理容纳空间。


简单构造

正如英国计算机专家 Hoare 所言:软件设计构建有两种方法,一是使其尽可能简单,从而一目了然确定其中不存在缺陷;另一种方法则是使其极为复杂,以至于看不出什么明显的缺陷。


遗憾的是,他还补充道:第一种方法其实要困难得多。


在软件开发当中,添加新的模块、新的类以及越来越多的代码是件很简单的事。这能够解决软件需要面对的更多用例,但更大的代码量所产出的结果却越来越少。正如 Jamie Zawinski 所说:每一个程序都会不断扩展,一直扩展到像人一样会收邮件、看邮件。而那些无法扩展的程序,最终会被能够扩展的程序所取代。


从本质上讲,我们一直在不断扩展程序。我们向其中添加更多模块、更多类以及更多代码,而且对于每一段代码,我们都需要确保其能够与原有代码顺畅协作,从而在这个复杂性呈指数增长的过程中继续保持进一步扩展的空间。之所以会这样作,是因为增量总是更简单的——至少在起步阶段更简单。


在另一方面,越是简单的东西开发起来就越困难。这要求开发人员考虑潜在的模式与行为,并消除一切不必要的元素。但最终,这样的思维方式能够成就更出色的成果,因为我们只需要使用极少的概念与精力,就可以描述并理解程序,确保大家专注于更多宏观目标、而非细枝末节。


检测过于复杂的构造

实际上,发现这类复杂而臃肿的构造并不困难。我们看看程序手册就能知道个大概,如果其中有专门的章节来描述结构背景之下的众多词汇,基本可以断定程序复杂度已经失控。


下面来看一个例子,即 Abstract Factory(抽象工厂)模式。为了描述其工作原理,我们需要定义很多名称:


  • AbstractFactory 接口

  • <<creates>>活动

  • Factory1(或者 ConcreteFactory)

  • ProductA 以及 ProductBinterfaces

  • ProductA1 以及 ProductB1classes

  • new 关键字

  • Abstract Factory 类图

  • :Client 以及 factory:Factory1 序列图


之所以存在这么多名称与图类,是因为复杂性比简单性更易于实现。这也迫使程序员采用针对性过强的对象创建方式,而无法选择复用存储在某处的对象等其它可能的解决方案。通过以下方式,我们尝试降低其整体复杂性:


  • 从整体模式当中删除“Factory”名称。

  • 删除 new 关键字以及 <<creates>>活动。


现在,该模式只关注能够返回 ProductA 或者 ProductB 实例的对象。它可以对该类进行实例化、从数据结构中获取预先构建的对象,或者从某处提取该对象——具体方法无穷无尽,但这些都不重要;真正重要的是我们的目标:开始时没有对象,结束时有了对象。现在再来看名称列表,两项操作:


  • 第一项,利用接口 ProductA 获取一个对象。

  • 第二项,利用接口 ProductB 获取一个对象。


这样,我们可以自由地将该操作放在某个类、某个结构或者是某个指针当中。在使用时,我们只需要配合必要的参数对该操作进行调用,它就会为我们提供相应的对象。


在设置中,我们可以将第一项操作设置为任何能够生成 ProductA 的形式,并将第二项操作设置为任何能够生成 ProductB 的形式。没有类图、没有序列图、没有额外的类、没有不必要的字,也没有歧义——因为我们能够对某项操作做的只有调用,将其作为参数进行传递,获取返回结果并加以存储。毕竟函数与操作在本质上就是带有参数的值。我们要如何利用接口 ProductA 获取某个对象?如何才能返回一个 ProductA?答案很简单:使用第一项操作。


构造良好的抽象

我们已经在 Abstract Factory 的修正版中描述了拥有良好构造的抽象结构。


构造良好的抽象


AbstractFactory 模式显然不是什么好抽象,因为它包含一切开发者不关心、也没必要关心的繁琐细节——例如属于抽象、必须创建对象,以及使用 new 关键字等。作为用户,我并不关心这么多细节,但这些细节就在眼前,想躲都躲不开。


相反,我只希望拥有“一项能够利用 ProductA 接口为获取对象的操作。”我只需要进行调用,它就能返回 ProductA 的结果。至于具体获取方式,我不知道、也不在乎。


组合良好的抽象

AbstractFactory 模式不是什么好抽象,因为它没有得到良好组合。看看它用到的两种方法:其一是创建 ProductA;其二是创建 ProductB。为什么要把它们放在同一个 Factory 当中?为什么不能把这两项操作分开,并让希望将二者一同使用的用户将其放进同一个元组中?A 和 B 在同一个类中,绝对不是什么良好的构造方式。我该如何组合?如果其它地方也有需要使用 ProductA 的操作怎么办?那项操作是否需要了解 AbstractFactory 中的错误链?


AbstractFactory myFactory = new Factory1();ProductA myProduct = myFactory.createProductA();AbstractFactory somethingDifferent = new SomethingDifferentFactory();SomethingDifferent result = somethingDifferent.fabricate(myProduct);return result
复制代码


相反,如果拥有“一项能够利用接口 ProductA 为我获取对象的操作”,而另有一项操作要求使用 ProductA 以构造别的结果,那就可以调用第一项操作来获取 ProductA,然后再用它来执行第二项操作。在类型方面:


  • operation : () -> ProductA

  • otherOperation: ProductA -> SomethingDifferent


因此: otherOperation(operation())。完事,就这么简单。


检测组合性是否良好

遗憾的是,很难通过语义检测组合性是否良好,因为开发人员已经习惯了那些没有组合性可言的做法,因此很多人压根不知道所谓组合良好究竟是什么概念。好的组合其实能够轻松感受到,这有点像是乐高积木,每一块都能轻松与其它块匹配起来,共同搭建出规模可观的作品。


这块有个三角形尖头,我们看看哪些积木块有三角形开孔。


当具有良好的组合性时,我们不需要使用适配器(或者不会设置适配器这样的概念,因为连接机制本身非常简单)。当具有良好的组合性时,我们只需要将各部分连接起来即可构建成一个整体,而不必担心这些部分会以意想不到的方式相互作用。


其整体,就是各部分相加得出的精确总和,仅此而已。


总结

我们已经看到了 AbstractFactory 如何破坏“获取 ProductA”这样一个简单的概念:一个 Factory 即为一个 Abstract,其通过 createProductA 方法利用 new 以 <<construct>>一个拥有 ProductA <<interface>>的 ProductA1。


这里单是关键字与概念就多达 10 个,超出了最聪明的大脑所能轻松处理的极限。更糟糕的是,由于太过刻板,我们只能使用这项操作创建对象、而无法复用对象。这也是编程为何如此困难的原因之一:我们毫无意义地抬高了它的难度。


原文链接:


http://www.javiercasas.com/articles/rule-of-seven


2019 年 8 月 22 日 13:5819066

评论 1 条评论

发布
暂无评论
发现更多内容

架构师训练营第六周作业

一剑

用Roslyn做个JIT的AOP

八苦-瞿昙

技术 随笔杂谈 aop 代理 框架

用“实例化需求”,让需求澄清更高效

小隐乐乐

详解区块链应用市场与落地应用现状

CECBC区块链专委会

互联网大厂根本没有题库!了解这些却能让你掌握“隐形题库”

互联网架构师小马

程序员 面试 找工作

LeetCode题解:15. 三数之和,JavaScript双循环+HashMap,详细注释

Lee Chen

LeetCode 前端进阶训练营

架构师训练营 Week 06 总结

Wancho

React与前端开发发展史

pingan8787

第6周课后练习-请简述CAP原理

Dawn

极客大学架构师训练营

对CAP的理解

朱月俊

第六周总结

秦宝齐

作业

记一次Apache的代码导致生产问题

java金融

Java Apache spring BeanUtils

数据结构学习心得

程李文华

架构师训练营第 6 周作业二

不谈

面向对象编程学习

一叶知秋

分布式系统架构学习总结(分布式数据库和NoSQL)

qihuajun

分布式系统架构作业

qihuajun

第六周作业

秦宝齐

学习 极客大学架构师训练营

架构设计篇之中台战略思想与落地

小诚信驿站

架构设计 刘晓成 中台战略 服务化改造

java 后端博客系统文章系统——No5

猿灯塔

Java

你要的《Spring系列源码解读》PDF它来了

z小赵

Java spring

CAP Theorem

dongge

架构师训练营第六周作业

R20114

极客大学架构师训练营

2020-07-11-第六周作业

路易斯李李李

第6周作业

andy

第6周总结

andy

CAP原理

chenzt

区块链扩张路径变局:从技术比拼转向生态落地

CECBC区块链专委会

1. react起始 | 2020年前端再入门系列连载

chaozh

前端 React

Rust所有权,可转可借

袁承兴

rust 指针 函数调用 引用 内存管理

架构师训练营第六周总结

一剑

开源中间件技术学习路线

开源中间件技术学习路线

多数程序员难以用简单的方式开发应用?-InfoQ