软件柔性化

来源:互联网 发布:淘宝买到烂水果怎么办 编辑:程序博客网 时间:2024/04/29 17:20

    软件的开发是一项系统工程,涉及到人力、资金成本、资源、协作等各个方面的因素,这些因素必须有机组合在一起,而不是被一些所谓的条条框框所套住。明确目标,合理进行策划和实施以及团队的密切配合,这些都是一个软件产品成功开发的基础。

<br></br>

      然而,软件开发毕竟是一项复杂的工作,很多原因都会导致软件失败或者疲于奔命,如:官僚主义、不明确的目标、团队的不畅沟通,资源的不合理配置,以及其他诸多因素,这些因素中,设计摆在非常重要的位置,在很大程度上决定软件的复杂度,很多时候我们为了追求某种概念,去设计一个非常灵活的架构,而在实际项目运用中只解决了一个非常小的问题。曾经见过一个架构,充分运用了Windows消息机制,十多个类对象去解决几个按钮状态的功能,结果得不偿失,设计和代码的晦涩以及和想要达到的功能矛盾,导致开发人员不能很好的去理解软件,修正和扩展功能变得难以控制,促使开发陷入了焦油坑。从另一个方面来说,优秀的设计能够很好地解决软件的复杂度。

       在软件开发过程中,我们经常有这样的体会随着软件开发的深入,设计时未考虑到的问题逐渐暴露、新的需求不断添加原有设计思路的天生缺陷等,都可以导致很多模块对象被重新设计和重新组织编码,新的领域对象和原有的领域对象需要重新集成。如果遇到设计元素都是整块的,无法重新组合的,又或是设计元素分解很细、理解和跟踪很难的,多余的抽象和间接层次结构的,还有各个元素紧密联系、牵一发而动全身的,这时,我们要重构和迭代将非常困难,举步为艰。好的设计应该是简单、结构清晰易于理解的,每个元素只反映自己的领域范畴,各个元素非常容易组织在一起形成新的应用,并且其功能是可以预测的。自然适应这些变化的能力,取决于软件的柔性化。

       软件的柔性化不是那么容易获得,也没有公式可套,需要经过多次的重构迭代和精化,不断地对业务领域进行沉淀和挖掘,除此之外,可以通过以下几个规则可以使软件具备柔性。

一.释意接口

作为一名开发人员,经常会用别人开发好的代码,很多时候会深入代码内部进行阅读,倒不是学习别人的代码写得如何巧妙,而是因为很难理解代码所表达的真正含义,有时甚至自己写的代码,过一段时间也会不理解它的意图。这样,封装的大部分价值就已经丧失了。我们必须使我们的设计和代码清晰地反映有意义的领域逻辑,将隐含的概念显式地反映出来,另外必须为我们的类、方法和参数等起一个能够解释其设计意图的名称,名称要保证能够准确的表达其效果和目的,没有二义性。为了便于团队成员的交流,尽量使用能够反映领域的通用语言,站在使用人员的角度去思考。

我们来比较一下两个调漆的领域类:

首先变量的命名让人头脑过载,看了之后绝对要阅读代码才能知道它具体代表什么,Paint的本意是将两种涂料混合起来得到体积更大的混合颜色的涂料。而paint方法可以是往墙壁上刷涂料,也可以混合另外一种涂料刷墙壁等,所表达的含义很不清晰,让人猜测。必须通过阅读代码才能释意。

二.无副作用函数

我这次在重构计价2007UI的过程中,碰到的很多都是具有毒副作用的函数,它们往往是一个函数调用其他函数,而其他函数又调用另外的操作,函数之间存在着不可预知的任意深度的嵌套,而且这些函数又被很多的模块所使用,还有很多函数使用重载,我们很难预测调用一个操作究竟会产生什么样的后果,也很难想象在第二层和第三层上的操作会产生什么样的影响,被深深的套牢着。如果开发人员被迫去理解那些内部实现,任何接口、函数方法都不会有用处。如果不能安全地对抽象的结果进行预测,我们必须限制这种上限,制定规则约束,避免不可控制。要使我们的操作方法不产生副作用首先要将命令和查询严格的隔离到不同的操作中去,将逻辑(计算方法)和状态修改分开,不至于发生概念混淆。操作分为两大类:命令和查询,命令即为系统发生改变的操作,查询即为获取信息。我们要确保命令尽可能的简单,同时不返回任何领域数据,避免在外部发生不可预知的修改。其次为了避免对已有的领域对象发生无谓的修改,我们常常用一个值对象来代表返回的结果或者传递的参数。值对象通常是一个临时的产物,要用的时候临时创建,不用的时候马上删除,其生命周期受到严格的控制,而且值对象具有不变性,是非常安全的做法。

三.边界轮廓

高内聚、低关联是设计的两个基本原则,无论设计规模大小,这两个原则对于概念和编码具有同样的重要性。有时希望找到一个合适的粒度将一个功能模块细分开来,有时又要把功能模块集中起来以便封装复杂性。如果将模型和设计中的元素封装在一个整体构造中,就会造成功能上的重复,因为其外部接口无法提供客户可能关心的所有信息,我们很难理解这些元素的含义,因为各种不同的概念都混在一起了,而将类和方法细分可能会导致不必要的复杂化,强迫开发人员去理解那些小碎片是如何配合起来的,概念可能完全丢失,半个铀原子就不再是铀了。粒度的大小不是很重要的,重要的是粒度是否符合具体的业务领域,这才是定义边界轮廓的精髓所在。

业务领域中一定存在着特征和规则,不然建模就毫无意义了,如果我们的模型能够与领域发生共振,产生共鸣,说明模型已经贴合业务领域,我们的模型也就越能适应业务的发展。如果模型使我们产生怀疑或者哪怕有一点别扭,则提醒我们需要对业务进一步深化。

同样,任何领域中都有一些不感兴趣的细节,不同的应用关注的细节是不一样的,如同调漆应用来说,只关心将不同的颜料混合成新的颜料,而不关心单个具体的颜色如何处理的情况,这是涂料设计人员的事。所以对于调漆应用来说,没有必要将颜料分解为红颜料或者蓝颜料,这些都是没有必要的。我们的目标是得到简单的接口,将设计元素(操作、接口、类和聚合)聚合在一起,将领域中的一些重要部分用领域语言清晰的将轮廓描述出来,避开不相关的细节,使业务领域尽早的与我们的模型贴切稳定起来。

       高内聚固然是我们想追求的,但是其带来的影响可能存在过多的交叉依赖,而交叉依赖很容易累积起来,使模型和设计难以理解。如要理解一个类,就必须理解其相关联的类,交叉依赖就像一张蜘蛛网,交织在一起,理解起来非常费劲。不仅类的引用可以形成交叉依赖,返回值,参数,变元等都可以形成交叉依赖。而原始类型和一些基本的类库,由于是系统语言自带的,程序开发人员都熟知的,很显然不会形成交叉依赖。

       模块和聚合都是用来限制交叉依赖网的,一个具有高内聚的子领域分裂到一个模块中时,一批对象都与系统的其他部分解耦了。同一个模块中的类依赖要比依赖一个外部的类要好,因为这样更能反映模块内部本质的概念。每个依赖都是值得怀疑的,除非它代表了概念之间某种根本的东西。将隐含概念显式出来,精心选择模型和设计可以使依赖减少,最好是得到一个没有任何依赖的孤立类。

       另外操作封闭也能够减少交叉依赖,所谓操作封闭,就是集合的任何两个元素组合起来,其结果依然在这个集合之中。如112,实数11组合,其结果依然是实数2,又例如通过一个Employee对象返回他的主管依然是一个Employee对象。这种将操作的返回值和变量、实现者定义成相同的类型,无疑消除了其他概念的依赖。

       低关联是对象设计的基本原则,只要有可能就应该这样做。消除不必要的依赖,减少交叉依赖带来的概念过载、理解维护困难的缺陷。

       柔性开发可以有效应付软件的可变性和复杂性。为持续开发和维护带来效率,而不致使我们的开发陷入泥潭,从头再来。但要使软件获得柔性化,并非是件容易的事情,只有持续不断的迭代和重构,才能使我们的设计模型更切合业务领域,使我们的模型更稳定。

 
原创粉丝点击