远隔作用

科技工作者之家 2020-11-17

远隔作用(Action at a distance)是一种程序设计中的反模式,是指程序某一部分的行为会广泛的受到程序其他部分指令的影响,而且要找到影响其他程序的指令很困难,甚至根本无法进行。这个词最早源自物理学中的远距作用,指两个没有相互接触的物体会互相影响对方。量子力学的量子缠结就是远距作用的一个例子,爱因斯坦将此称为“鬼魅似的远距作用”(spooky action-at-a-distance)。

定义远隔作用是指计算机代码执行时会受其他程序代码影响,在程序调试过程,很难找到影响其他代码的代码,甚至无从下手。避免远隔作用的方法是避免使用全域变量,利用可控制的区域变量来调整数据,或是使用有参照透明度的纯函数编程或函数式编程语言。

远隔作用造成的程序错误常是因为程序模块的特定功能在错误的时间动作,或是影响了不应当影响的变量。不过很难找出是哪一个模块造成的影响。一些看似没有问题的动作的副作用使得程式在一个未知的状态中,因此局部的资料也可能不是局部的,有可能受到其他模块的影响。改善远隔作用的方式是定义一个模块可以影响模块的范围。若在软件设计时准确的定义各模块之间的界面,避免使用共享的数据或是全域变量,可以大幅减少远隔作用造成的问题。有些面向对象程式设计设计原则也可以避免远隔作用。得墨忒耳定律提到一个物件只能影响它邻近的物件。若物体之间有必要有远隔作用,必需用消息传递的方式进行。适当的设计可以大幅限制远隔作用的发生,因此也使系统比较容易维护。不良的界面设计会造成不羁的对象,上帝对象或是其他不依照得墨忒耳定律的物件。函数编程语言的好处是减少远隔作用出现的可能性,若是使用纯函数编程语言,甚至根本不会出现远隔作用。

反模式在软件工程中,一个反面模式(anti-pattern或antipattern)指的是在实践中明显出现但又低效或是有待优化的设计模式,是用来解决问题的带有共同性的不良方法。它们已经经过研究并分类,以防止日后重蹈覆辙,并能在研发尚未投产的系统时辨认出来。Andrew Koenig在1995年造了anti-pattern这个词,灵感来自于GoF的《设计模式》一书。而这本书则在软件领域引入了“设计模式”(design pattern)的概念。三年后antipattern因《AntiPatterns》这本书而获得普及,而它的使用也从软件设计领域扩展到了日常的社会互动中。按《AntiPatterns》作者的说法,可以用至少两个关键因素来把反面模式和不良习惯、错误的实践或糟糕的想法区分开来:

行动、过程和结构中的一些重复出现的乍一看是有益的,但最终得不偿失的模式

在实践中证明且可重复的清晰记录的重构方案

很多反面模式只相当于是错误、咆哮、不可解的问题、或是可能可以避免的糟糕的实践,它们的名字通常都是一些用反话构成的词语。有些时候陷阱(pitfalls)或黑色模式(dark patterns)这些不正式的说法会被用来指代各类反复出现的糟糕的解决方法。因此,一些有争议的候选的反面模式不会被正式承认。这个概念很容易推广到工程学以及工程以外需要人们付出努力去争取的领域。尽管在工程学以外很少用到这个术语,但其概念是通用的。

避免方法使用纯函数

在程序设计中,若一个函数符合以下要求,则它可能被认为是纯函数:

此函数在相同的输入值时,需产生相同的输出。函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关。该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。

纯函数的输出可以不用和所有的输入值有关,甚至可以和所有的输入值都无关。但纯函数的输出不能和输入值以外的任何资讯有关。纯函数可以传回多个输出值,但上述的原则需针对所有输出值都要成立。若引数是传引用调用,若有对参数物件的更改,就会影响函数以外物件的内容,因此就不是纯函数。

使用函数式编程

函数式编程(functional programming)或称函数程序设计,又称泛函编程,是一种编程典范,它将计算机运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。比起指令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

全局变量代替局部变量

在程序设计中,全局变量是在所有作用域都可访问的变量,与之对应的是局部变量。局部变量是拥有局部作用域的变量。通常,使用不必要的全局变量被认为是坏习惯,这正是由于全局变量的非局部性:全局变量可能被从任何地方修改(除非位于保护内存中),也可能被任何地方所依赖。于是全局变量便拥有了建立相互依存关系的无限可能,而互相依存关系的建立会使得复杂度增加,参见远隔作用(Action at distance)。然而,在少数情况下是适合使用全局变量的。例如,可以通过全局变量的使用来避免常用变量在一系列函数间的频繁传递。

面向对象设计

面向对象的设计方法与传统的面向数据过程的方法有本质不同。面向对象的程序设计方法注重需求分析和设计反复,回答的是“用何做、为何做”的问题,它使程序员摆脱了具体的数据格式和过程的束缚,可以集中精力研究和设计所要处理的对象。新的对象类可以通过继承已存在的对象类的性质而产生,因此,这样实现的可重用性是自然的和准确的。采用面向对象的方法表示知识,不仅表达的能力强,可以表示相当广泛的知识,能够描述非常复杂的客观事物,而且具有模块性强、结构化程度高、便于分层实现,有利于设计、复用、扩充、修改等一系列优点。

对象是对象式系统中运行时刻的基本成分,它是属性和行为的封装体,其中还包括和其他对象进行通信的设施。对象有三种不同含义:实在对象、问题对象和计算机对象。实在对象是现实世界中存在的实体;问题对象是实在对象在问题域中的抽象,用以根据需要,完成某些行为;计算机对象是问题对象在计算机系统中的表示,它是数据和操作的封装通信单位。因此,对象式语言中的对象是指计算机对象。

类是对一组具有相同数据和相同操作的对象的描述,也就是说类是一组对象的抽象概括。其作用有:一是作为对象的描述机制,刻划一组对象的公共属性和行为;二是作为程序的基本单位,它是支持模块化设计的设施,并且,类上的分类关系是模块划分的规范标准。类有三部分组成:数据、操作和接口。数据刻划对象的状态,操作刻划对象的行为,类中所有数据均为私有,接口使操作对外可见。

消息是对象与对象之间可以传递信息,所传递的信息即为消息。它要求某个对象执行类中所定义的某个操作的规格说明,由三部分组成:接受消息的对象、消息选择子(又称为消息名)、零个或多个变元(实参)1。

本词条内容贡献者为:

王慧维 - 副研究员 - 西南大学

科技工作者之家

科技工作者之家APP是专注科技人才,知识分享与人才交流的服务平台。