`
fangang
  • 浏览: 877106 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
311c4c32-b171-3767-b974-d26acf661fb2
谈谈用例模型的那些事儿
浏览量:38656
767c50c5-189c-3525-a93f-5884d146ee78
一次迭代式开发的研究
浏览量:68817
03a3e133-6080-3bc8-a960-9d915ed9eabc
我们应当怎样做需求分析
浏览量:409979
753f3c56-c831-3add-ba41-b3b70d6d913f
重构,是这样干的
浏览量:91604
社区版块
存档分类
最新评论

代码复用应该这样做(3)

阅读更多
但假如被合并的代码所在的类具有某种并列关系,甚至是同一个父类下的多个子类,或者同一接口的多个实现类,则我们可以采用继承的方式解决代码复用的问题。

具体做法是这样的,第一步还是整理原有的代码,通过比较,将需要重构的多份原代码中相同的与不同的代码整理出来。在整理过程中,可以将不同的代码,保留在各自的原程序中,而将相同的代码抽取出来成为独立的函数。这些函数就是我们后面需要抽象、合并、复用的代码。

下一步呢,就是运用“抽取父类(Extract Superclass)”的重构手法,从多个要复用的类中抽取出一个共同的父类。父类中包含的方法应当是经过比较后相同的部分,而将不同的部分保留在原有的类中。

举一个例子吧:在一个开票业务中,开票被分为正常开票与非正常开票。不论是哪种开票,它们都需要保存,因此它们都有save()这个方法。但是,正常开票与非正常开票在保存时,有相同的操作,却也存在着差异。在我们重构前,“保存”操作在正常开票与非正常开票业务类中各自实现了一遍。显然,这个方法在两个类中存在着大量重复的代码。

随后我们开始整理与分析。我们发现在整个保存的过程中,保存前的校验是存在着差异的,但都需要校验;保存前的数据处理有相同的部分,也是不同的部分;最后执行保存,以及保存后的返回处理是相同的。经过这样的分析,我们分别将原有的正常开票业务类与非正常开票业务类中的保存操作分为了三段:valid(), prepare()与save()。

最后,我们运用“抽取父类”重构方法,抽取出“开票业务类”这个父类,将valid()设为抽象方法,将prepare()中共同的代码放在父类中,将save()在父类中实现,将valid()和prepare()中不同的部分,分别在正常开票与非正常开票业务类中各自实现(如图):


这里“开票业务类”是个抽象类,不能被实例化。它的valid()方法是个抽象方法,什么都不写,让各个子类各自实现去。方法prepare()在父类中实现了公共的部分,但各个子类在实现其不同的部分时,应当调用父类,就像这样:

/* 
* @param vo the value of Object
*/
public void prepare(Fp vo) throws IOException {
	super(vo); //调用父类中的公用代码
	xxxxxxxx;  //编写子类中各种不同的部分
}

最后,save()在两种开票业务中的代码完全相同,因此仅仅在父类中编写,子类不再各自实现。

这样设计带来的巨大好处是大大提高了程序的可维护性:如果代码共同的部分变更了,则去修改父类;如果代码各自的部分变更了,则去修改子类。如果子类中某些代码因需求的变更变为了共同的操作,则提升到父类中;相反,父类中某些共同的操作,因需求的变更而不相同时,则降级到子类中分别实现去。

除此之外,还有一种办法是将不同的部分用一个接口与其多个实现来解决。当实际的应用软件系统比较复杂时,使用继承比较容易出现“继承泛滥”的问题。因此,一个可行的办法是将继承转换为组合,具体方法是这样做的:

当我们完成了对代码的比较以后,将代码不同的部分封装到一个统一接口下的多个实现类中。然后将代码相同的部分合并成一个业务类,为各个客户程序所使用。举个例子吧,在ERP软件中有一个功能就是将各种不同的单据生成财务凭证。不同的单据生成的财务凭证是不一样的,如应付单应当将其对方科目作为凭证的借方科目,将应付科目作为贷方科目;付款单应当将其付款科目作为借方科目,将其结算方式对应的结算方式科目作为贷方科目……但不论哪种单据,尽管科目与规则不同,但都是由一到多个借方分录和一到多个贷方分录组成。原程序是分别为各种不同的单据制作凭证生成类,如“应付单凭证生成类”、“付款单凭证生成类”等等,因此出现了大量重复代码。随后我们开始重构。通过分析发现,各种不同单据在整个过程中主要是生成分录的规则不同,同时合并分录的策略也不相同,但分录生成与合并分录之间是一种排列组合关系,即任何一种单据都可能有三种合并方式。不同属性的排列组合关系最容易造成“继承的泛滥”,如一个“应付单凭证生成类”,要分解成三个不同合并方式的类,“付款单凭证生成类”同样要分解为三个。除此之外,读取单据、保存凭证的过程则是相同的。因此我们进行了如下设计:


这样的设计,当来了一个业务,要求用多张应付单,按照凭证类型合并的方式生成凭证,则使用“应付单实现类”与“按凭证类型合并”;当另一个用付款单不合并生成凭证时,则使用“付款单实现类”与“不合并凭证”,功能得到实现。

采用该方法重构代码时,有效地解决了复杂环境下造成“继承泛滥”的问题,同时提高了系统可维护性。试想,如果要增加一种新的单据,我们则仅仅写一个它的“分录生成”实现类就可以了,其它什么都不用管,多么简便呀。原来的一大堆诸如“应付单凭证生成类”、“付款单凭证生成类”等等,现在一个简单的“凭证生成业务类”统统都搞定了,程序多么清晰呀。

最后,在复用代码的过程中,还有一种情况经常出现并且比较讨厌,那就是要重构的代码,被相同的部分与不同的部分分割成了好多的碎片。遇到这种情况,采用继承结合模板模式的方法,是最有效的了。模板模式(Template Method)是GoF设计模式中的一个,如果你为一个算法定义了一系列步骤,并且允许子类来实现其中的一个或多个步骤,你就可以使用这个模式。该方法将把分离得支离破碎的程序过程,分解成数个方法定义在模板模式的父类中(即每个方法就是一个步骤),并且在父类中定义了这些方法的执行顺序。之后,将代码中相同的部分写在父类中,将不同的部分分别在子类中实现。

比如,我经常要创建工厂类,虽然每个工厂类都各有各自的不同,但所有工厂类的初始化总是一样的:采用各种方式(相对路径、绝对路径、jar包或zip包中的路径,等等)寻找XML配置文件、读取XML文件、解析XML文件中的内容、根据内容创建产品、将产品放入到工厂中、为客户程序搜索工厂中的产品。这其中第1、2、5步对于任何工厂都是相同的,而第3、4、6步各个工厂却不尽相同。这时,创建一个AbstractFactoryTemplate的模板类,让各个工厂去继承它,则每个工厂类只需实现各自的第3、4、6步,工厂类就快速实现了(如图)。


提高代码复用率的方法林林总总、不胜枚举,并且每种方法都有各自的适用场景。因此,对开发人员提出了很高的技术要求。我们只有在实际工作中多思考、勤练习、仔细体会,才能熟练掌握各项技能,切实提高程序的代码质量。
(续)

相关文档
遗留系统:IT攻城狮永远的痛
需求变更是罪恶之源吗?
系统重构是个什么玩意儿
我们应当改变我们的设计习惯
小步快跑是这样玩的(上)
小步快跑是这样玩的(下)
代码复用应该这样做(1)
代码复用应该这样做(2)
代码复用应该这样做(3)
做好代码复用不简单(1)

特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!
  • 大小: 23.9 KB
  • 大小: 45 KB
  • 大小: 22 KB
分享到:
评论
1 楼 hq2999 2014-01-15  
不错不错

相关推荐

    CMMI 代码复用报告 模板

    **CMMI 代码复用报告模板详解** 代码复用是软件开发中的一项关键实践,旨在提高效率,减少错误,并促进代码的可维护性。CMMI(Capability Maturity Model Integration,能力成熟度模型集成)是衡量软件开发过程质量...

    delphi中几种代码的复用及其比较

    在Delphi编程环境中,代码复用是提高效率和软件质量的关键策略。代码复用不仅可以简化编码,减少工作量,还能促进团队协作,提高代码的可维护性和一致性。本文主要探讨了在只使用Delphi作为开发工具的情况下,如何...

    运用LabVIEW实现机器人中的代码复用.zip

    本资料包“运用LabVIEW实现机器人中的代码复用.zip”主要关注如何利用LabVIEW提高开发效率,通过代码复用来缩短项目周期。 首先,代码复用是软件工程中的一个核心概念,旨在减少重复工作,提升开发速度和代码质量。...

    C++可复用代码——命令行控制模块

    在C++编程中,代码复用是提高效率和减少错误的关键。本项目“C++可复用代码——命令行控制模块”提供了一个基础框架,帮助开发者轻松构建自定义的命令行Shell程序。这个框架旨在使程序员能够专注于实现特定功能,而...

    第5章 函数和代码复用.pdf

    ### 第5章 函数和代码复用 #### 知识点概述 本章节主要围绕着函数的概念、定义、使用以及代码复用的方式展开讨论。针对Delphi环境下的Python语言程序设计,深入剖析了如何利用函数实现更高效、灵活的编程实践。 #...

    python函数及代码复用学习教案.pptx

    python函数及代码复用学习教案.pptx

    基于上下文的智能化代码复用推荐.pdf

    《基于上下文的智能化代码复用推荐》这篇文章探讨了如何利用现代技术和大数据分析来提高软件开发中的代码复用效率和质量。代码复用是开发者常用的一种高效开发手段,包括复用相似功能模块、代码片段以及API等不同...

    基于上下文的智能化代码复用推荐.docx

    《基于上下文的智能化代码复用推荐》\n\n代码复用是软件开发中的关键实践,旨在提升开发效率和代码质量。随着互联网的发展,代码大数据的积累为智能化代码复用推荐提供了新的可能性。本文主要探讨了两个核心方向:...

    c++设计模式简单使用对比-简单体会代码复用

    这种机制使得代码复用变得简单,同时也促进了类的分层结构,有助于创建更复杂的系统。例如: ```cpp class Worker { public: void work() { // 工人的一般工作 } }; class AdvancedWorker : public Worker { ...

    代码复用避免篇1

    在编程领域,代码复用是提高效率和保持代码可维护性的重要原则。然而,不当的代码复用可能会引入问题。本文将探讨四种常见的代码复用模式,并分析它们的优缺点。 首先,我们来看“默认模式”的代码复用,即通过父类...

    运行时代码随机化防御代码复用攻击.pdf

    【运行时代码随机化防御代码复用攻击】 代码复用攻击是一种高级的恶意攻击手段,它利用了程序中已存在的可执行代码片段(称为gadgets)来构造新的恶意指令序列,绕过传统安全机制。随着攻击技术的发展,代码复用...

    jsp页面代码复用组件工具:easylayout

    "jsp页面代码复用组件工具:easylayout" 指的是一种基于JavaServer Pages (JSP) 技术的代码复用工具,名为 "easylayout"。这个工具主要目的是为了提高开发效率,通过封装布局和组件,减少开发者在编写JSP页面时重复...

    查理复用下的led数码管.rar

    3. **时序管理**:查理复用的关键在于精确的时序控制,确保在正确的时间点切换引脚状态。这部分代码可能包含定时器配置和中断服务程序,用于控制LED亮灭的周期。 4. **错误处理**:由于查理复用涉及到复杂的引脚...

    频分复用的matlab代码

    以下是一个简单的MATLAB频分复用代码示例: ```matlab % 定义信号参数 x = 1544:1558; % 时间轴,范围从1544到1558,步长为1 bandwidth = 0.1; % 子信道带宽 % 生成三个独立的信号 signal1 = exp(-((x-1550)/...

    21丨理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?1

    DRY(Don't Repeat ...总的来说,理解并正确应用DRY原则,以及掌握如何提高代码复用性,是提高软件质量、降低维护成本的关键。在实际开发中,我们需要时刻注意识别和消除代码的冗余,使我们的代码更加简洁、高效。

    Android自定义view实现代码复用

    本教程将通过一个具体的示例——HZLoginView,深入讲解如何在Android中创建自定义View并实现代码复用及自定义属性。 首先,理解自定义View的基本步骤: 1. 创建一个新的Java类:通常,我们继承自Android的基础View...

    面向Linux的内核级代码复用攻击检测技术.pdf

    近年来,代码复用攻击已经成为安全领域的重要研究课题,而内核级代码复用攻击利用内核自身代码绕过传统的防御机制,使得这类攻击更具隐蔽性和危害性。现有的大多数检测方法主要集中在应用层的代码复用攻击,对内核级...

    IO多路复用之poll——完整代码

    三种IO多路复用机制: 一:select 二:poll 三:epoll 以上三种IO多路复用的完整代码,皆可以在我的资源列表中获取下载: 资源列表:http://download.csdn.net/user/qiulanzhu

    深入理解JavaScript系列(45):代码复用模式(避免篇)详解

    任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量避免...

Global site tag (gtag.js) - Google Analytics