软件复用(SoftWare Reuse)是将已有软件的各种有关知识用于建立新的软件,以缩减软件开发和维护的花费。软件复用是提高软件生产力和质量的一种重要技术。早期的软件复用主要是代码级复用,被复用的知识专指程序,后来扩大到包括领域知识、开发经验、设计决定、体系结构、需求、设计、代码和文档等一切有关方面。
复用级别
代码的复用
包括目标代码和源代码的复用。其中目标代码的复用级别最低,历史也最久,当前大部分编程语言的运行支持系统都提供了连接(Link)、绑定(Binding)等功能来支持这种复用。源代码的复用级别略高于目标代码的复用,程序员在编程时把一些想复用的代码段复制到自己的程序中,但这样往往会产生一些新旧代码不匹配的错误。想大规模的实现源程序的复用只有依靠含有大量可复用构件的构件库。如”对象链接及嵌入”(OLE)技术,既支持在源程序级定义构件并用以构造新的系统,又使这些构件在目标代码的级别上仍然是一些独立的可复用构件,能够在运行时被灵活的得新组合为各种不同的应用。
设计的复用
设计结果比源程序的抽象级别更高,因此它的复用受实现环境的影响较少,从而使可复用构件被复用的机会更多,并且所需的修改更少。这种复用有三种途径,第一种途径是从现有系统的设计结果中提取一些可复用的设计构件,并把这些构件应用于新系统的设计;第二种途径是把一个现有系统的全部设计文档在新的软硬件平台上重新实现,也就是把一个设计运用于多个具体的实现;第三种途径是独立于任何具体的应用,有计划地开发一些可复用的设计构件。
分析的复用
这是比设计结果更高级别的复用,可复用的分析构件是针对问题域的某些事物或某些问题的抽象程度更高的解法,受设计技术及实现条件的影响很少,所以可复用的机会更大。复用的途径也有三种,即从现有系统的分析结果中提取可复用构件用于新系统的分析;用一份完整的分析文档作输入产生针对不同软硬件平台和其它实现条件的多项设计;独立于具体应用,专门开发一些可复用的分析构件。
测试信息的复用
主要包括测试用例的复用和测试过程信息的复用。前者是把一个软件的测试用例在新的软件测试中使用,或者在软件作出修改时在新的一轮测试中使用。后者是在测试过程中通过软件工具自动地记录测试的过程信息,包括测试员的每一个操作、输入参数、测试用例及运行环境等一切信息。这种复用的级别,不便和分析、设计、编程的复用级别作准确的比较,因为被复用的不是同一事物的不同抽象层次,而是另一种信息,但从这些信息的形态看,大体处于与程序代码相当的级别。
由于软件生产过程主要是正向过程,即大部分软件的生产过程是使软件产品从抽象级别较高的形态向抽象级别较低的形态演化,所以较高级别的复用容易带动较低级别的复用,因而复用的级别越高,可得到的回报也越大,因此分析结果和设计结果在当前很受重视。用户可购买生产商的分析件和设计件,自己设计或编程,掌握系统的剪裁、扩充、维护、演化等活动。
类的方面
设计可复用的类——LSP
行为子结构
协变与逆变
Liskov替换原则(LSP)
一、组合(has-a)
二、继承(is-a)
(1)初始化子类
如果父类的构造方法是带参数的,而且没有无参数的构造方法,那么在子类的构造方法中必须显式地调用父类的构造方法。
如果父类的构造方法是无参数的,那么在子类中写不写都可以,不写的话会隐式地调用。
三、代理
四、组合与继承结合
(1)确保正确的清理
你并不清楚垃圾收集器合适将会调用,或者它是否被调用。因此你想要某个类清理一些东西,就必须显示地编写一个特殊的方法来做这件事,并确保客户端程序员知晓他们必须调用这个方法。finally
(2)名称屏蔽
重载机制。@override:防止不想重载时意外重载
五、protected关键字
六、final关键字
(1)数据
基本类型,不变的是值。引用类型,不变的是引用,值是可以修改的。
(2)修饰类
不能被继承
final类中的所有的方法都隐式指定为是final的。
(3)方法
1.不能被重写
2.效率,在Java的早期,如果将一个方法指明为final,就是同意编译器将针对该方法的所有调用都转为内嵌调用(不推荐使用、jvm会自己优化)
有final: 没有虚函数表,直接调用,最快
无final: 普通虚函数,那么正常速度,中间
有final子类:因为没有虚函数表,需要转换成父类,较慢
无final子类: 因为虚函数,从虚函数表找父类的getName,最慢
当然虚函数表的描述是有一定限制的,比如HotSpot VM确实是有为Java类生成vtable的,但是
其他jvm虚拟机不一定就有,但是处理虚函数的方法基本一致,我们可以统一理解
补:在最近的Java版本中,虚拟机(特别是hotspot技术)可以探测到这些情况,并优化去掉这些效率反而降低的额外的内嵌调用,因此不再需要使用final方法来进行优化了。
final与private
类中所有的private方法都隐式地指定为是final。由于无法取用private方法,所以也就无法重写。可以对private方法添加final修饰符,但是没有意义。
混淆:如果你试图重写一个private方法(隐式final),编译器不会报错。
API复用
API是程序员最重要的资产和“荣耀”,吸引外部用户,提高声誉。
建议:始终以开发API的标准面对任何开发任务;面向“复用”编程而不是面向“应用”编程。
难度:要有足够良好的设计,一旦发布就无法再自由改变。
编写一个API需要考虑以下方面:
API应该做一件事,且做得很好
API应该尽可能小,但不能太小
Implementation不应该影响API
记录文档很重要
考虑性能后果
API必须与平台和平共存
类的设计:尽量减少可变性,遵循LSP原则
方法的设计:不要让客户做任何模块可以做的事情,及时报错
API(接口)重用(多态)
说到接口重用,先说说多态,什么是多态,很简单,就是孩子从父辈那里继承的DNA,到了孩子这一代出现了不同的分支方向,有的可能喜欢文学,有的可能喜欢数理化,这个是多态的一个比较感性的描述
至于多态的代码实例我就不在这里列举了,下面会有更深入的例子,其实java实现多态的方法,就是函数方法的重写(在这里区别下函数的重载)和基类与派生类的类型转换
基类与派生类的转换:
1)派生类对象可以向基类对象赋值。
2)派生类对象可以代替基类对象向基类对象的引用进行赋值或者初始化。
3)如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象。
4)派生类对象的地址可以赋给基类对象的指针变量。//很重要
上述转换的核心思想是,基类对象永远只能访问父类的成员,哪怕是派生类赋值还是引用,都限定在基类中。
框架复用
框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。
为了增加代码的复用性,可以使用委派和继承机制。同时,在使用这两种机制增加代码复用的过程中,我们也相应地在不同的类之间增加了关系(委派或继承关系)。而对于一个项目而言,各个不同类之间的依赖关系就可以看做为一个框架。一个大规模的项目可能由许多不同的框架组合而成。
框架与设计模式:
框架、设计模式这两个概念总容易被混淆,其实它们之间还是有区别的。构件通常是代码重用,而设计模式是设计重用,框架则介于两者之间,部分代码重用,部分设计重用,有时分析也可重用。在软件生产中有三种级别的重用:内部重用,即在同一应用中能公共使用的抽象块;代码重用,即将通用模块组合成库或工具集,以便在多个应用和领域都能使用;应用框架的重用,即为专用领域提供通用的或现成的基础结构,以获得最高级别的重用性。
框架与设计模式虽然相似,但却有着根本的不同。设计模式是对在某种环境中反复出现的问题以及解决该问题的方案的描述,它比框架更抽象;框架可以用代码表示,也能直接执行或复用,而对模式而言只有实例才能用代码表示;设计模式是比框架更小的元素,一个框架中往往含有一个或多个设计模式,框架总是针对某一特定应用领域,但同一模式却可适用于各种应用。可以说,框架是软件,而设计模式是软件的知识。
框架分为白盒框架和黑盒框架。
白盒框架:
白盒框架是基于面向对象的继承机制。之所以说是白盒框架,是因为在这种框架中,父类的方法对子类而言是可见的。子类可以通过继承或重写父类的方法来实现更具体的方法。
虽然层次结构比较清晰,但是这种方式也有其局限性,父类中的方法子类一定拥有,要么继承,要么重写,不可能存在子类中不存在的方法而在父类中存在。
通过子类化和重写方法进行扩展(使用继承);
通用设计模式:模板方法;
子类具有主要方法但对框架进行控制。
允许扩展每一个非私有方法
需要理解父类的实现
一次只进行一次扩展
通常被认为是开发者框架
黑盒框架:
黑盒框架时基于委派的组合方式,是不同对象之间的组合。之所以是黑盒,是因为不用去管对象中的方法是如何实现的,只需关心对象上拥有的方法。
这种方式较白盒框架更为灵活,因为可以在运行时动态地传入不同对象,实现不同对象间的动态组合;而继承机制在静态编译时就已经确定好。
通过实现插件接口进行扩展(使用组合/委派);
常用设计模式:Strategy, Observer ;
插件加载机制加载插件并对框架进行控制。
允许在接口中对public方法扩展
只需要理解接口
通常提供更多的模块
通常被认为是终端用户框架,平台