APP下载

提高代码可重用性的研究

2016-03-25张晓宇

微型电脑应用 2016年1期
关键词:降低成本软件开发

张晓宇



提高代码可重用性的研究

张晓宇

摘 要:软件公司提高收益的途径有多种多样,对于软件开发人员而言,降低软件开发成本无疑是最简单的一条。提高代码可重用性就是众多降低软件开发成本的方法中最实用的一种方式。在代码可重用性高项目中,当需求来临时,开发人员能够利用系统中已有的元素,付出尽可能小的努力来应对这个变化。对如何提高代码可重用性展开了研究分析。

关键词:软件开发;降低成本;可重用性

0 引言

对于一个企业而言,软件开发的意义在于满足客户需要,然后通过满足客户需要,为自己所在的企业直接或者间接创造收益。根据“收益-成本=利润”这条简单的商业逻辑可以看出:为了获取利润最大化,企业要么提高收益,要么降低成本。提高收益的方法包括好的市场战略、具有竞争力的定价和提高入门门槛等。降低成本的方法包括减少管理成本,不去购买没有必要的设备,租赁更便宜的办公室等。但是,这些都不是软件开发团队所关注的,软件开发团队需要关注的是如何降低软件开发过程中的成本。软件开发过程中的成本包括两部分:内在成本(Essential Cost)[1]和偶发成本(Accident Cost)。内在成本是指解决一个问题的最简单方案所付出的成本。偶发成本则是指为解决一个问题的方案相对于最简单方案多出的那部分成本。

当一个需求来临的时候,如果我们能够尽可能地利用已有的元素,我们就可以付出尽可能小的努力来应对这个需求,这就是重用。但如果一些本来应该可以重用的部分,无法做到重用,则必须从头做起。这些工作量重叠的部分就属于偶发成本。因此我们可以得出这样的结论:在开发人员实现需求时,如果原有系统对于要实现的需求可重用部分越少,那么开发人员为了实现需求需求所要付出的成本就越高。反之,提高原有系统代码的可重用性可以大幅降低软件的开发成本。

1 重用

1.1 重用的误区

重用是整个软件开发社区几十年来一致努力的重要课题。然而很多开发人员会错误地将“修改”和“拷贝粘贴”当作重用。

当要实现一个需求时,一种常见的方式是:在原有函数里面塞入几行新的代码逻辑。这样的动机绝对是为了重用原有流程中已经存在的代码。这样的做法从短期来看最简单。甚至在很多情况下,它也是成本最少的实现方式。但是这样做的不良后果就是:原有函数的逻辑很快变得复杂,进而晦涩难懂。从长期来看,团队需要付出更多的成本来理解与维护它。另外,一个经过修改的软件单位,无论是它自身还是调用它的代码,都必须重新进行验证。如果一个软件单位的修改内容包括接口部分,就会造成所有使用它的代码的修改和更大范围的重新验证,那么将会造成更多的偶发成本。

另外一种常见的误区是:拷贝粘贴。为了不影响原有功能,将代码进行拷贝粘贴,得到另外一份拷贝。然后在拷贝代码上修改其中一部分,得到另外一段代码。两段代码对应于两种不同的需求,同时存在于同一系统。虽然这种动机也是重用,但造成的结果是重复。它的危害甚至比“修改”更大。

由此可见,“修改”和“拷贝粘贴”都不是真正的重用。

1.2 重用的含义

一个已经存在的软件单位,无需任何修改,无需拷贝粘贴就可以用于帮助实现某些需要,这样的方式才是真正的重用。如果一个软件单位必须被修改,才能满足新的需要,即便你只修改了其中的1%,虽然看起来好像复用了其中的99%。我们仍然称对于这个需求不可重用。如果这个软件单位无须修改的部分中,能够与需要修改的部分进行分离,那么无须修改的部分对于这个需求就是可重用的。

1.3 重用的范围

一个可重用的实体,只能在可见的范围内被重用。当定义一个实体的时候,希望它在多大的范围内被重用,决定了它的部署位置,进而影响系统的类设计和结构设计。如果一个可以在更大范围内重用的单位被控制在较小的范围内,那么势必会造成重复。因为这些可重用的单位会在不同的模块中被重复定义。或者一个可重用单元U被模块A定义,其初衷是仅供本模块内部使用,但却没有对U进行访问范围控制。模块A被定义了明确的职责,但由于U的公开,模块A需要提供更多职责。当另一个模块B需要这个功能时候,就可以直接调用。这样B对A就产生了依赖。但是这种依赖很显然是不恰当的。

1.4 可重用性

一个软件单位适用的范围越广泛,那么它被重用的可能性就越高。重用的可能性就称作可重用性。软件系统中没有绝对可重用的东西。只有对于合适的问题,一个软件单位才可以被重用。

2 提高可重用性的方法

从上文论述可知,重用对于降低成本具有重要意义。如何提高软件的可重用性,Larry Constantine和Ed Yourdon在其著作《结构化设计》里给出了答案,即“高内聚,低耦合”。本章节提到的正交策略可以帮助开发人员做到“高内聚,低耦合”。

2.1 内聚

内聚,用来衡量一个单位内部各种元素之间的关联紧密程度。元素之间关联度越高,则其内聚度越高。

一方面,高内聚强调,紧密关联的事物应该放在一起。这会增强一个软件单位的可重用性和可理解性。另一方面,高内聚强调,只有紧密关联的事物才应该被放在一起。把上述两个方面结合起来,就是著名的Unix设计哲学:“Do One Thing,Do it well”。

2.2 耦合

耦合,用来衡量单位之间的关联程度。单位之间的关联度越高,则他们之间的耦合度就越高。耦合产生的原因是依赖。对于任意两个代码元素,当一方依赖于另一方,则两者之间就产生了耦合。耦合带来的问题是,对于耦合双方,当被依赖的一方发生变化时,会导致依赖的一方也跟着变化。这就会导致可重用性降低。例如在图中的系统中,如图1所示:

图1 耦合的变化

由于一个需求的来临,需要修改D。由于D 变化,会引起A变化。又由于B和C都依赖于A,A的变化又会引起B和C的变化。

这样一个需求的发生就会导致整个系统都会发生变化。原有系统对于这个新需求的可重用性就很差。同样的,如果图1中任何一个依赖关系得到减弱,那么D变化时,引起整个系统变化的概率就会降低。那么系统的可重用性也就会得到增强。

高耦合会降低系统重用性的原因在于,一个模块直接或者间接依赖的其他代码元素越多,为了重用它,需要引入的元素就越多,而这些元素会进一步的依赖其他元素,而那些元素中的很多东西可能并不是所需要的。另外,一个系统的耦合越强,那么系统的可理解性就越差。比如,一个函数直接引用了另外一个模块的全局变量,开发人员必须了解,有哪些会模块在什么样的场合下会修改这个全局变量。只有这样,当前函数的行为以及它所可能导致的结果才能被理解。2.3正交策略

一个低内聚的系统,通常是高耦合的系统。高度耦合会使得系统可重用性很低。一块代码的变更会很容易导致软件系统大面积的修改。我们希望的情况恰恰相反,即一块代码的修改不会引起其他代码的修改。这样的状态被称作“正交”。正交这个词来源于数学概念:两个向量的积为零,则认为他们是正交的,即两个向量是垂直的。在一个正交系统里,一段代码沿着一个方向变化,但另外一个方向却不会发生变化。一个系统“高内聚,低耦合”的程度越高,其正交性越强。

为了达到较好的正交性,可以采用以下策略:

①消除重复

②分离关注点

③缩小依赖范围

2.3.1消除重复

重复就意味着:同一件事,需要被反复的机械的执行。它增加了代码量,从而增加了不必要的理解成本和维护成本。重复分为完全重复和部分重复。完全重复是指系统中有两段代码,它们分别实现了一个功能。对于完全重复,直接删除其中一个,保留另外一个即可。但是大多数情况下,重复是以部分重复的形态出现的。两个部分重复的函数可以归纳为三种形式:参数型重复、调用型重复和回调型重复。

参数型重复是指,两个函数的算法相同,只是处理的数据不同。消除这样的重复,只需要将差异的数据参数化即可。

消重前:

void* f1(void* buf, const Packet* packet)

{

strcpy(buf, packet->src_address);

return (void*)((char*)buf + strlen(packet->src_address) + 1);

}

void* f2(void* buf, const Packet* packet)

{

strcpy(buf, packet->dest_address);

return (void*)((char*)buf + strlen(packet->dest_address) + 1);

}

消重后:

void* appendStrToBuf(void* buf, const char* str)

教师面对的教育对象个体因家庭环境、社会背景、智力水平、性格气质等不同,对他们的教育手段和教育方法策略就要有所不同。要因时制宜、因生制宜,就是常说的因材施教。学生在不同的阶段有不同的特点,要在教育过程中有灵活的应变能力。要了解每个教育对象的个性特征,做到有针对性,有的放矢。

{

strcpy(buf, str);

return (void*)((char*)buf + strlen(str) + 1);

}

void* f1(void* buf, const Packet* packet)

{

return appendStrToBuf(buf, packet->src_address);

}

void* f2(void* buf, const Packet* packet)

{

return appendStrToBuf(buf, packet->dest_address);

}

调用型重复是指,如果两个函数重复部分完全相同,可以将重复的部分提取出来,然后原函数分别对它进行调用。

消重前:

void* f1(void* buf, const Packet* packet){}

void* f2(void* buf, const Packet* packet){}

消重后:

void F(){}

void* f1(void* buf, const Packet* packet){ F();}

void* f2(void* buf, const Packet* packet){F();}

回调型重复是指,如果两个函数重复部分完全相同。可以将重复的部分提取为函数F,将差异的部分修改成与原型相同的两个函数S1和S2,然后通过F分别对S1和S2进行调用。

消重前:

void* f1(void* buf, const Packet* packet){}

void* f2(void* buf, const Packet* packet){}

消重后:

void s1(){}

void s2(){}

void F(void (*s)()){s();}

void f1(){F(s1);}

void f2(){F(s2);}

2.3.2分离关注点

消除重复是提高可用性的被动型策略。即在重复出现之后,不影响实现需求的前提下,尽可能地消除它。而消除重复,既让整个系统更加干净,又提高了系统内部元素的可重用性。但是,如果系统在没有重复的情况下,我们需要通过主动出击,在早期就让系统更加干净。分离关注点就是其中一种方法。

分离关注点的好处很多,首先,是为了管理复杂度。当把一个复杂的事情分离成一个个的关注点时,每个关注点都更简单,因此更容易理解。其次,是为了可重用性。在一个大型的网络系统中,某个网元承担了某个职责后,其他网元就不需要各自完成相应的职责,而是重用此网元的功能实现。分离关注点要分到什么程度,Uncle Bob在《敏捷软件开发:原则,设计与模式》里给出了答案,即符合单一职责原则。单一职责原则是指每个职责高度内聚,不同职责松散耦合,从而让各个职责尽可能独立地变化,这样就能做到分离关注点的目的。

2.3.3缩小依赖范围

所谓耦合,是指双方或者多方通过某种方式产生了依赖关系。缩小依赖范围就是指缩小它们之间的依赖关系。当谈起一个某块依赖于另一个模块时,并非意味着一个模块依赖对方所有的功能和代码。这种依赖往往是局部的。尽管其依赖关系可能非常复杂和混乱。但如果仔细梳理,我们就会得出结论,在被依赖的模块中,某个元素一旦发生变化,就会导致依赖方的变化,那么这些元素就被称作依赖点。依赖点就是一个知识点,一个模块了解另外一个模块的知识点越多,则它对另外一个模块的依赖点就越多。任意一个依赖点的变化都会导致依赖方的变化。所以两个模块之间存在的依赖点越多,则依赖方变化的概率就越高,这会导致糟糕的正交性。所以必须尽可能减少依赖点的数量,这就是所谓的缩小依赖范围。

3 总结

本文对如何提高代码可重用性展开研究分析,提出了正交策略。正交策略可以有效提高软件系统的内聚度,降低耦合度,进而提高软件的可重用性。可重用性高的软件系统可以在需求变更的长期演进过程中为企业节省成本,提高企业收益。

参考文献

[1] JoshuaKerievsky.重构到模式[M].Addison-Wesley Professional,2005.

[2] Paul M.Duvall, Steve Matyas. Andrew Glover-Contin -uous integration improving software quality and redu -cing risk[M].Addison-Wesley Professional,2007(7).

[3] 张海藩.软件工程[M].北京:清华大学出版社,2003.

[4] Kent Beck,Cynthia Andres.解析极限编程:拥抱变化[M].北京:机械工业出版社,2011,20-26.

[5] Mani Chandy K., Event-Driven,Applications: Costs, Benefits and Design Approaches[J].California Institute of

[6] Scott Meyers,Effective C++:55 Specific Ways to Improve Your Programs and Designs, 3rd Edition[M].Pearson Education Inc.,2005.

[7] Jimmy Nilsson.领域驱动设计与模式实战[M].北京:人民邮电出版社,2009,11.

[8] 杨芙清.软件工程技术发展思索[J].软件学报,2005,1.

[9] 张凯.软件缺陷混沌分形描述与软件质量进化度量的研究[D].武汉:武汉理工大学,2005

The Research of Improving Code Reusability

Zhang Xiaoyu
(Zhongxing Telecommunication Equipment Corporation, Shanghai 201203, China)

Abstract:Many ways to improve the profit in software company. Reducing cost is the easiest way for software developers. Improving code resuability is a kind of way to cut off cost. Developer can use the existing elements of current system with high reusability by the least cost when need comes. This paper focus on how to improve code reusability.

Key words:Software Developing; Cost Reducing; Reusability

收稿日期:(2015.05.04)

作者简介:张晓宇(1987-),男,阜阳,中兴通讯股份有限公司,通讯软件开发工程师,研究方向:通讯软件开发与应用,上海,201203

文章编号:1007-757X(2016)01-0048-03

中图分类号:TP311

文献标志码:A

猜你喜欢

降低成本软件开发
定制铺丝新工艺降低成本提高综合性能
如何有效提升建筑工程施工技术管理水平
煤炭企业降低成本有效措施
煤炭企业降低成本有效措施
信息安全环境下的计算机软件开发研究
核电厂定期试验管理软件开发
FPGA软件开发过程中编码规范的作用
航天器设计如何降低成本
以全面预算为核心提高管理质量