写点什么

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

  • 2019-08-22
  • 本文字数:2825 字

    阅读完需:约 9 分钟

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

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


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

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



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

简单构造

正如英国计算机专家 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-08-22 13:5819444

评论 1 条评论

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

IDaaS系统方舟一账通ArkID内置OIDC认证插件配置流程

龙归科技

Idaas OIDC ArkID

阿里云一站式专家测试服务,护航APP线上质量,发版无忧

移动研发平台EMAS

阿里云 移动测试 限时活动

基于Requests与mitmproxy打造迷你接口测试框架

霍格沃兹测试开发学社

大话JMeter2|正确get参数传递和HTTP如何正确使用

霍格沃兹测试开发学社

大话JMeter4|不同的并发数可以自动化做压测吗?

霍格沃兹测试开发学社

软件测试 | 测试开发 | 测试人生 | 从外行到外包,从手工测试到知名互联大厂测开 这个90后小姐姐是怎么腾飞的?

测吧(北京)科技有限公司

如何做好性能压测(一):压测环境的设计和搭建

霍格沃兹测试开发学社

实战 | UI 自动化测试框架设计与 PageObject 改造

霍格沃兹测试开发学社

谈安全测试的重要性

京东科技开发者

漏洞 软件系统 安全测试 网络安全渗透测试

软件测试 | 测试开发 | MySQL锁机制总结

测吧(北京)科技有限公司

软件测试 | 测试开发 | 测试人生 | 从跨专业手工测试转岗外包,再到 Python 测试开发,跳槽涨薪 85%

测吧(北京)科技有限公司

Python 测试开发

天翼云打造国云安全品牌 铸牢企业云上安全防线

Geek_2d6073

实战 | JMeter 典型电商场景(下单/支付)的性能压测

霍格沃兹测试开发学社

C#/VB.NET: 为Excel表格添加超链接

Geek_249eec

C# Excel VB.NET 超链接

软件测试 | 测试开发 | 测试人生 | 双非学历,从外包到某大厂只用了1年时间,在2线城市年薪近30万,我柠檬了......

测吧(北京)科技有限公司

面试 测试 软件测试和开发

后端Web开发框架(Java)

霍格沃兹测试开发学社

大话测试数据(一)

霍格沃兹测试开发学社

实战 | 基于JMeter 完成典型电商场景(首页浏览)的性能压测

霍格沃兹测试开发学社

史上最全 Appium 自动化测试从基础到框架实战精华学习笔记(一)

霍格沃兹测试开发学社

最佳实践|用腾讯云AI图像搜索打造属于自己的拍立淘

牵着蜗牛去散步

腾讯 图像搜索 腾讯云AI 小程序商城 AI技术实践

软件测试 | 测试开发 | 项目倒排,跟工期不足say byebye~

测吧(北京)科技有限公司

测试

Python基础(二) | Python的基本数据类型

timerring

Python 9月月更

如何用Sonic云真机打王者

霍格沃兹测试开发学社

知识图谱在智能运维中的应用

穿过生命散发芬芳

知识图谱 9月月更

如何利用 xUnit 框架对测试用例进行维护?

霍格沃兹测试开发学社

学习Docker就应该掌握的dockerfile语法与指令

霍格沃兹测试开发学社

融云 2022 社交泛娱乐出海嘉年华,邀你一起「超浪」!

融云 RongCloud

IT 程序猿 社交娱乐

基于 Spring Boot 的 RESTful API 设计与实现

霍格沃兹测试开发学社

只需搞定Docker,环境问题再也不是测开路上的『坑』

霍格沃兹测试开发学社

易观千帆《银行APP用户体验分析白皮书》重磅首发,助力银行打造获客新增长点

易观分析

金融 银行

同样是断言,为何 Hamcrest 如此优秀?

霍格沃兹测试开发学社

多数程序员难以用简单的方式开发应用?_语言 & 开发_Javier Casas_InfoQ精选文章