心理学中有一篇相当古老、但又非常重要的论文,题为《魔法数字七(上下浮动二):人类信息处理能力中的一些限制》。这篇文章衡量了大脑处理信息的极限,并给出了一个具体的数字:人脑可以同时容纳五到九个概念。我们当然能够把这个有趣的结论延伸到诸多领域当中,但对软件开发人员而言,下面两项含义最为重要:
总而言之,这是一场以降低概念描述难度为目标的心理战,夺取的是珍贵的心理容纳空间。
正如英国计算机专家 Hoare 所言:软件设计构建有两种方法,一是使其尽可能简单,从而一目了然确定其中不存在缺陷;另一种方法则是使其极为复杂,以至于看不出什么明显的缺陷。
遗憾的是,他还补充道:第一种方法其实要困难得多。
在软件开发当中,添加新的模块、新的类以及越来越多的代码是件很简单的事。这能够解决软件需要面对的更多用例,但更大的代码量所产出的结果却越来越少。正如 Jamie Zawinski 所说:每一个程序都会不断扩展,一直扩展到像人一样会收邮件、看邮件。而那些无法扩展的程序,最终会被能够扩展的程序所取代。
从本质上讲,我们一直在不断扩展程序。我们向其中添加更多模块、更多类以及更多代码,而且对于每一段代码,我们都需要确保其能够与原有代码顺畅协作,从而在这个复杂性呈指数增长的过程中继续保持进一步扩展的空间。之所以会这样作,是因为增量总是更简单的——至少在起步阶段更简单。
在另一方面,越是简单的东西开发起来就越困难。这要求开发人员考虑潜在的模式与行为,并消除一切不必要的元素。但最终,这样的思维方式能够成就更出色的成果,因为我们只需要使用极少的概念与精力,就可以描述并理解程序,确保大家专注于更多宏观目标、而非细枝末节。
实际上,发现这类复杂而臃肿的构造并不困难。我们看看程序手册就能知道个大概,如果其中有专门的章节来描述结构背景之下的众多词汇,基本可以断定程序复杂度已经失控。
下面来看一个例子,即 Abstract Factory(抽象工厂)模式。为了描述其工作原理,我们需要定义很多名称:
之所以存在这么多名称与图类,是因为复杂性比简单性更易于实现。这也迫使程序员采用针对性过强的对象创建方式,而无法选择复用存储在某处的对象等其它可能的解决方案。通过以下方式,我们尝试降低其整体复杂性:
现在,该模式只关注能够返回 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 以 <
这里单是关键字与概念就多达 10 个,超出了最聪明的大脑所能轻松处理的极限。更糟糕的是,由于太过刻板,我们只能使用这项操作创建对象、而无法复用对象。这也是编程为何如此困难的原因之一:我们毫无意义地抬高了它的难度。
今天带大家做一个网页版的一个精美的计算器,使用的计算器可以实现标准计算器的功能,题目:计算器的实现,技术:html+css+js,使用html+css+js实现简易计算器,开启你的计算之旅吧。效果图如下,复制即可使用
C/S(Client/Server) :客户端/服务器结构,其中客户端和服务器端都是独立的计算机;B/S(Browser/Server):浏览器/服务器结构,B/S是特殊的一种C/S结构,是对C/S的改进和变化,B/S是基于应用层http协议的web
多页面应用:每次页面跳转,后台都会返回一个新的HTML文档,就是多页面应用。单页应用:用vue写的项目是单页应用,刷新页面会请求一个HTML文件,切换页面的时候,并不会发起新的请求一个HTML文件,只是页面内容发生了变化
我们在开发多Tab应用时候,常常会遇到多个Tab状态同步的问题。想象如下场景:用户主界面,显示用户购物车内待结算的商品总数。此时,用户可能打开多个Tab
1.flutter开发者网站下载url_launcher插件;在 pubspec.yaml 文件中添加依赖;安装:flutter pub get;导入;_launchURL、_openMapApp为自定义方法名 可以根据自己的场景自定义名称
Flask是一个使用Python编写的轻量级Web应用框架。以管理员身份,打开命令提示符窗口,输入下面命令py -3 -m pip install flask
你是否正处于选择 App 开发技术的两难境地?如今,渐进式 Web 应用程序需求旺盛。知道这是为什么吗 ?下面让我们来详细的梳理一下。移动电话用户的增长促使在线企业重新考虑对移动应用的优化
使用typescript构建Vue应用和使用js一样,都是通过vue-cli去初始化并创建一个vue项目,只不过使用typescript构建的时候要在脚手架问卷操作的时候勾选上typescript选项。使用typescript构建的Vue项目发生了一些变化:
BFC就是块级格式化上下文,是页面盒模型布局中的一种 CSS 渲染模式,相当于一个独立的容器,里面的元素和外部的元素相互不影响。
我们平时在开发部署 Node.js 应用的过程中,对于应用进程启动的耗时很少有人会关注,大多数的应用 5 分钟左右就可以启动完成,这个过程中会涉及到和集团很多系统的交互,这个耗时看起来也没有什么问题。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!