代码复用的规则
java技术 — 作者 raycicolop @ 12:23
代码复用是绝大多数程序员所期望的,也是OO的目标之一。总结我多年的编码经验,为了使代码能够最大程度上复用,应该特别注意以下几个方面。
对接口编程
"对接口编程"是面向对象设计(OOD)的第一个基本原则。它的含义是:使用接口和同类型的组件通讯,即,对于所有完成相同功能的组件,应该抽象出一个接口,它们都实现该接口。具体到JAVA中,可以是接口(interface),或者是抽象类(abstract class),所有完成相同功能的组件都实现该接口,或者从该抽象类继承。我们的客户代码只应该和该接口通讯,这样,当我们需要用其它组件完成任务时,只需要替换该接口的实现,而我们代码的其它部分不需要改变!
当现有的组件不能满足要求时,我们可以创建新的组件,实现该接口,或者,直接对现有的组件进行扩展,由子类去完成扩展的功能。
优先使用对象组合,而不是类继承
"优先使用对象组合,而不是类继承"是面向对象设计的第二个原则。并不是说继承不重要,而是因为每个学习OOP的人都知道OO的基本特性之一就是继承,以至于继承已经被滥用了,而对象组合技术往往被忽视了。下面分析继承和组合的优缺点:
类继承允许你根据其他类的实现来定义一个类的实现。这种通过生成子类的复用通常被称为白箱复用(white-box reuse)。术语"白箱"是相对可视性而言:在继承方式中,父类的内部细节对子类可见。
对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组合对象来获得。对象组合要求对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为被组合的对象的内部细节是不可见的。对象只以"黑箱"的形式出现。
继承和组合各有优缺点。类继承是在编译时刻静态定义的,且可直接使用,类继承可以较方便地改变父类的实现。但是类继承也有一些不足之处。首先,因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。更糟的是,父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。
对象组合是通过获得对其他对象的引用而在运行时刻动态定义的。由于组合要求对象具有良好定义的接口,而且,对象只能通过接口访问,所以我们并不破坏封装性;只要类型一致,运行时刻还可以用一个对象来替代另一个对象;更进一步,因为对象的实现是基于接口写的,所以实现上存在较少的依赖关系。
优先使用对象组合有助于你保持每个类被封装,并且只集中完成单个任务。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物(这正是滥用继承的后果)。另一方面,基于对象组合的设计会有更多的对象(但只有较少的类),且系统的行为将依赖于对象间的关系而不是被定义在某个类中。
注意:理想情况下,我们不用为获得复用而去创建新的组件,只需要使用对象组合技术,通过组装已有的组件就能获得需要的功能。但是事实很少如此,因为可用的组件集合并不丰富。使用继承的复用使得创建新的组件要比组装已有的组件来得容易。这样,继承和对象组合常一起使用。然而,正如前面所说,千万不要滥用继承而忽视了对象组合技术。
相关的设计模式有: Bridge、Composite、Decorator、Observer、Strategy等。
下面的例子演示了这个规则,它的前提是:我们对同一个数据结构,需要以任意的格式输出。
第一个例子,我们使用基于继承的框架,可以看到,它很难维护和扩展。
abstract class AbstractExampleDocument
{
// skip some code ...
public void output(Example structure)
{
if( null != structure )
{
this.format( structure );
}
}
protected void format(Example structure);
}
第二个例子,我们使用基于对象组合技术的框架,每个对象的任务都清楚的分离开来,我们可以替换、扩展格式类,而不用考虑其它的任何事情。
class DefaultExampleDocument
{
// skip some code ...
public void output(Example structure)
{
ExampleFormatter formatter =
(ExampleFormatter) manager.lookup(Roles.FORMATTER);
if( null != structure )
{
formatter.format(structure);
}
}
}
这里,用到了类似于"抽象工厂"的组件创建模式,它将组件的创建过程交给manager来完成;ExampleFormatter是所有格式的抽象父类;
将可变的部分和不可变的部分分离
"将可变的部分和不可变的部分分离"是面向对象设计的第三个原则。如果使用继承的复用技术,我们可以在抽象基类中定义好不可变的部分,而由其子类去具体实现可变的部分,不可变的部分不需要重复定义,而且便于维护。如果使用对象组合的复用技术,我们可以定义好不可变的部分,而可变的部分可以由不同的组件实现,根据需要,在运行时动态配置。这样,我们就有更多的时间关注可变的部分。
对于对象组合技术而言,每个组件只完成相对较小的功能,相互之间耦合比较松散,复用率较高,通过组合,就能获得新的功能。
减少方法的长度
通常,我们的方法应该只有尽量少的几行,太长的方法会难以理解,而且,如果方法太长,则应该重新设计。对此,可以总结为以下原则:
☆ 三十秒原则:如果另一个程序员无法在三十秒之内了解你的函数做了什么(What),如何做(How)以及为什么要这样做(Why),那就说明你的代码是难以维护的,必须得到提高;
☆ 一屏原则:如果一个函数的代码长度超过一个屏幕,那么或许这个函数太长了,应该拆分成更小的子函数;
☆ 一行代码尽量简短,并且保证一行代码只做一件事:那种看似技巧性的冗长代码只会增加代码维护的难度。
消除case / if语句
要尽量避免在代码中出现判断语句,来测试一个对象是否某个特定类的实例。通常,如果你需要这么做,那么,重新设计可能会有所帮助。我在工作中遇到这样的一个问题:我们在使用JAVA做XML解析时,对每个标签映射了一个JAVA类,采用SAX(简单的XML接口API:Simple API for XML)模型。结果,代码中反复出现了大量的判断语句,来测试当前的标签类型。为此,我们重新设计了DTD(文档类型定义:Document Type Definition),为每个标签增加了一个固定的属性:classname,而且重新设计了每个标签映射的JAVA类的接口,统一了每个对象的操作:
addElement(Element aElement); //增加子元素
addAttribute(String attName, String attValue); //增加属性;
则彻底消除了所有的测试当前的标签类型的判断语句。每个对象通过 Class.forName(aElement.attributes.getAttribute("classname")).newInstence(); 动态创建,
减少参数个数
有大量参数需要传递的方法,通常很难阅读。我们可以将所有参数封装到一个对象中来完成对象的传递,这也有利于错误跟踪。
许多程序员因为,太多层的对象包装对系统效率有影响。是的,但是,和它带来的好处相比,我们宁愿做包装。毕竟,"封装"也是OO的基本特性之一,而且,"每个对象完成尽量少(而且简单)的功能",也是OO的一个基本原则。
类层次的最高层应该是抽象类
在许多情况下,提供一个抽象基类有利做特性化扩展。由于在抽象基类中,大部分的功能和行为已经定义好,使我们更容易理解接口设计者的意图是什么。
由于JAVA不允许"多继承",从一个抽象基类继承,就无法再从其它基类继承了。所以,提供一个抽象接口(interface)是个好主意,一个类可以实现多个接口,从而模拟实现了"多继承",为类的设计提供了更大的灵活性。
尽量减少对变量的直接访问
对数据的封装原则应该规范化,不要把一个类的属性暴露给其它类,而是应该通过访问方法去保护他们,这有利于避免产生波纹效应。如果某个属性的名字改变,你只需要修改它的访问方法,而不是修改所有相关的代码。
子类应该特性化,完成特殊功能
如果一个子类只是使一个组件变成组件管理器,而不是实现接口功能,或者,重载某个功能,那么,就应该使用一个外部的容器类,而不是创建一个子类。
建议:类层次结构图,不要太深;
例如:下面的接口定义了组件的功能:发送消息;类Transceiver实现了该接口;而其子类Pool只是管理多个Transceiver对象,而没有提供自己的接口实现。建议使用组合方式,而不是继承!
public interface ITransceiver{
public abstract send(String msg);
}
public class Transceiver implements ITransceiver {
public send(String msg){
System.out.println(msg);
}
}
//使用继承方式的实现
public class Pool extends Transceiver{
private List pool = new Vector();
public void add(Transceiver aTransceiver){
pool.add(aTransceiver);
}
public Transceiver get(int index){
pool.get(index);
}
}
//使用组合方式的实现
public class Pool {
private List pool = new Vector();
public void add(Transceiver aTransceiver){
pool.add(aTransceiver);
}
public Transceiver get(int index){
pool.get(index);
}
}
拆分过大的类
如果一个类有太多的方法(超过50个),那么它可能要做的工作太多,我们应该试着将它的功能拆分到不同的类中,类似于规则四。
作用截然不同的对象应该拆分
在构建的过程中,你有时会遇到这样的问题:对同样的数据,有不同的视图。某些属性描述的是数据结构怎样生成,而某些属性描述的是数据结构本身。最好将这两个视图拆分到不同的类中,从类名上就可以区分出不同视图的作用。
类的域、方法也应该有同样的考虑!
尽量减少对参数的隐含传递
两个方法处理类内部同一个数据(域),并不意味着它们就是对该数据(域)做处理。许多时候,该数据(域)应该作为方法的参输入数,而不是直接存取,在工具类的设计中尤其应该注意。例如:
public class Test{
private List pool = new Vector();
public void testAdd(String str){
pool.add(str);
}
public Object testGet(int index){
pool.get(index);
}
}
两个方法都对List对象pool做了操作,但是,实际上,我们可能只是想对List接口的不同实现Vector、ArrayList等做存取测试。所以,代码应该这样写:
public class Test{
private List pool = new Vector();
public void testAdd(List pool, String str){
pool.add(str);
}
public Object testGet(List pool, int index){
pool.get(index);
}
}
分享到:
相关推荐
分类: SQL Server ...INCLUDEPICTURE \d "http://blog.itpub.net/attachment/201408/11/29500582_1407739354rKwn.png" \* MERGEFORMATINET 7 填入所要创建域的完整的域名 INCLUDEPICTURE \d ...
- **OracleLife** (http://www.eygle.com/yangtingkun, http://yangtingkun.itpub.net/) - 由Yang Tingkun运营的个人博客,分享了许多Oracle技术经验。 - **老熊的Oracle+Linux技术部落** ...
- **网址**: [http://www.linuxforum.net/forum/postlist.php?Cat=&Board=oracle](http://www.linuxforum.net/forum/postlist.php?Cat=&Board=oracle) - **功能**: 技术文章、问题解答、经验分享等。 - **...
10. **ITPUB** (http://www.itpub.net/):中国最专业的IT技术社区,聚集了众多技术专家,提供高质量的技术交流。 11. **VCKBase** (http://www.vckbase.com/):全方位的技术百科全书,同时提供在线培训服务。 12. ...
http://ms.itpub.net/thread-765553-1-1.html 3. 运行PowerDesigner 12程序,点击Tools -> License Parameters -> Standalone Seat - Local License,导入PowerDesigner 12压缩包(即第一步中下载的安装压缩包)中...
1. **ITPub博客(http://publish.itpub.net/lists/11554/0/11554_1.shtml)**:汇集了多位资深开发者的个人博客,涉及Android开发的多个方面。 2. **西里汉林博客(http://www.cnblogs.com/xirihanlin/)**:作者...
- **ITPUB**(http://www.itpub.net/):一个知名的IT技术社区,其中Oracle板块非常活跃,包含了大量实用的技术问答和讨论。 - **CSDN Oracle专区**(http://expert.csdn.net/expert/forum.c=&whichpage=1):作为...
5. ITPUB SQL Server论坛(http://www.itpub.net/forum-40-1.html):IT专家聚集地,提供专业、深入的技术交流,对于进阶学习者有很大帮助。 6. CSDN邹建的SQL Server博客(http://blog.csdn.net/zjcxc/):CSDN是...
Oracle数据库表结构导出...http://www.itpub.net/viewthread.php?tid=1003383 http://blog.csdn.net/yzsind/archive/2008/06/09/2527371.aspx http://www.oracle.com.cn/viewthread.php?tid=135059 ...
4. 菊花论坛(http://www.sharecenter.net/):面向网络工程师。该社区特色在于它提供了大量的网络工程类文章和讨论,涵盖 CISCO 工程等方面的内容。 5. IT 售前论坛(http://www.sysvs.com/bbs):面向 IT 售前及...
4. **ITPUB(http://www.itpub.net/thread-829756-1-1.html)** - **知识点**:ITPUB是一个技术交流平台,用户可以在这里提问、解答关于软件测试的问题,获取实践经验。 5. **测试联盟...
解决aix 6.1打了sp2补丁导致oracle...http://www.itpub.net/viewthread.php?tid=1362201&extra=&page=1 这个额外的补丁文件,不知为何无法从IBM网站成功下载,网上找到好久才ITPUB上找到这个资源,感谢原来的上传人。
2. ITPUB(http://www.itpub.net/):这个论坛以其在数据库技术领域的深度讨论而知名,尤其是Oracle技术。它吸引了大量的IT技术爱好者和专家,是学习和交流技术的好地方。 3. 51CTO(http://www.51cto.com/):作为...
14. **CSDN** (http://www.csdn.net/ & http://www.csdn.com/) - **功能简介**:中国最大的开发者技术社区之一,包含丰富的技术文章、博客和论坛。 - **应用场景**:学习新技术、参与技术讨论。 15. **马迪欧** ...
11. **ITPUB(<http://www.itpub.net/>)** - ITPUB是IT领域的一个综合性社区,其中也包含了软件测试相关内容。 - 特色:内容覆盖广泛,除了软件测试外,还涉及数据库、开发等其他IT领域。 12. **CSDN 测试频道...
- **网址**:http://space.itpub.net/17203031/ - **特点**: - 主要提供Oracle AWR报告解析等相关技术文章。 - 该网站可能更偏向于Oracle性能调优方面的内容。 ### 二、Oracle数据文件块格式介绍 - **链接**:...
这是一个免费的Oracle表结构整理软件,是一个牛人写的,可以导出Excel和word两种格式,本... <br>软件原发地: http://www.itpub.net/viewthread.php?tid=332660<br>作者BLOG: http://blog.csdn.net/yzsind
技术支持网站很多,一般如果在学习过程中遇到问题,可以到下列网站寻求支持:http://www.itpub.net/http://www.linuxforum.net/http://www.csdn.com/http://tech.ccidnet.com/pub/column/c1108.html最重要的,别忘记...
帖子评论请到:http://www.itpub.net/810100.html 打包下载请到:http://www.5ienet.com 怒 何故 RMAN路 困难多处 刻苦勤学读 希望早日克服 参考资料数十部 不厌其烦尝试几度 刻苦终见回报幕 一丝恍然飘入 测试再...