继承固然非常强大,但我们不能滥用继承,如使用不当,则会引起诸多问题.只有当子类和超类之间确实存在"is-a"关系的时候,使用继承才是恰当的.即便如此,如果子类和超类在不同的包中,并且超类不是为了继承而设计的,那么继承将会是脆弱的.此时,我们应该考虑用包装类,一复合和转发机制来代替继承.
让我们来看个具体的例子: 我们假设程序用了个HashSet, 我们想统计一个HashSet对象创建以来总共加了多少个元素,为了实现这个功能,我们首先考虑继承,然后覆盖原有的实现加个计数:
package com.effective.item16;
import java.util.Collection;
import java.util.HashSet;
public class InstrumentedHashSet<E> extends HashSet<E> {
private static final long serialVersionUID = 1L;
private int count = 0;
InstrumentedHashSet() {
super();
}
@Override
public boolean add(E e){
count++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c){
count += c.size();
return super.addAll(c);
}
public int getCount(){
return count;
}
}
结果发现但我们调用如下代码时,计数本应为3,真实结果为6. 什么原因呢? 原来父类HashSet里面addAll方法调用了add方法.这种内部细节,我们作为api的使用者很难知道,除非查看源码. 继而又有个严重的问题,如果HashSet的具体实现改变了,那么又有可能引起其他的问题,很难察觉.
让我们换种思路,采用复合代替继承.
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
public class WrapperSet<E> implements Set<E> {
private int count = 0;
private Set<E> s;
private WrapperSet(Set<E> s) {
super();
this.s = s;
}
...
@Override
public boolean add(E e) {
count++;
return s.add(e);
}
@Override
public boolean remove(Object o) {
return s.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return c.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
if(c != null)
count += c.size();
return s.addAll(c);
}
...
}
Set接口保存了HashSet的功能特性,看似麻烦了些,但程序变得健壮了,这种Wrapper设计也带来了灵活性,他可以包装任何的Set实现, 同时又很好的扩展了计数器.
关于包装类,大家可以结合Decorator模式和Proxy模式做更深入的研究.
分享到:
相关推荐
对于接口和包装类的详细解析,用法及其注意事项!深层了解接口和包装类的知识!谢谢
行业分类-外包设计-用于包装微流体装置的接口件的介绍分析.rar
面向对象程序设计中,可以将一个类的定义 放在另一个类的内部,这就是内部类(有的地方叫嵌套类), 包含内部类的类也被称为外部类(有的地方也叫宿主类...但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
8个包装类实现的接口:Serializable ,Comparable 3、构造方法 前面6个包装类、Boolean 1、本身常量值的一个构造方法 (value) 2、带String 参数一个构造方法(String s) Character Character(char ...
C++ 封装的python类,简化了python接口调用,可快速上手并使用,接口可调用python脚本以及文件,也可直接调用python的函数接口,并获得返回值
---包装数据结构以供应用程序使用----而不在数据结构算法本身,接口的示例和实现都以literate程序的方式给出,换句话说就是源代码及其解释是按照最适合理解代码的顺序交织出现的。 下面我将我想要给大家讲的内容分...
有调用ffmpeg读取媒体文件和存媒体文件的功能,有方便的显示yuv或者rgb的类、有简单播放音频pcm数据的类,有拉伸图片的类。至少在使用ffmpeg接口上有一定参考价值,然后就是MinGW编译出的ffmpeg库至少是能用的,虽然...
接口中我使用了缓存,以及在返回数据时候用了响应实体包装了, 没有直接返回数据库映射实体类。 怀疑问题点: 响应实体包装实体类中的字段有误,全部写成了小写字母? 缓存在出入库的时候会自动转化大小写。 问题...
st_asio_wrapper是一组类,功能是对boost.asio的包装(调试环境:boost-1.50.0),目的是简化boost.asio开发; 其特点是效率高、跨平台、完全异步,当然这是从boost.asio继承而来; 自动重连,数据透明传输,自动...
包装类 String 类 字符串的特性 String 类的常见方法 StringBuffer 类 Math 类 Arrays 类 System 类 日期类包括1代2代3代 集合 集合的框架体系 Collection 接口和常用方法 Collection 接口实现类的特点 List 接口和...
通过这些源代码可学习接口、声明类以实现Comparable接口、创建自定义接口、将基本数据类型值处理为对象、将字符串转换为数值的方法、BigInteger和BigDecimal类、对一个对象数组排序、基本类型和包装类之间的自动转换...
该包装器以类似于 Mathematica 的 OpenCLLink 的方式提供了 MATLAB 和 OpenCL 之间的接口。 控制环境中的一切,来回复制数据,以直观的方式启动线程,所有这些都组织在一个类中,并通过简单的函数接口完成。 编译...
该包提供了一个示例,说明如何通过 mex 接口安全地将 C++ 类包装在 MATLAB 类中,而不会出现内存泄漏,同时在 MATLAB 中实现类似于底层 C++ 接口的接口。 下载后调用: >> 运行示例在 MATLAB 中查看示例实现。 查看...
JAVA相关基础知识:面向对象的基本特征,集合类比较,简单数类型与包装类,异常捕捉,重载,重写
实验性 - 在 Porgress 中工作... Objective-c 包装类将处理 Web 服务协议,并提供易于使用的功能来创建、删除、附加和上传 ascii 文件、图像从移动设备到 Hadoop。 还将有一些类支持 WebHCat 接口,以将数据添加到 hiv
这里对oracle的开发接口oci进行了简单包装,类似于ADO,一共包括2个对象COciConnection和COciRecordset,用起来还不错,方便大家使用. 这几天使用发现了个BUG,需要把OCIStmtExecute函数的最后一个参数从OCI_DEFAULT...
前两天,因为项目需要,写的解析XML文件类,使用还算方便,注释详尽。另外还要安装 MSXML.MSI,添加#import "msxml4.dll
您所要做的就是将包装器脚本复制到 Octave 搜索路径中的某个位置并调整 Java 类路径,以便找到 StringTemplate jar 和所有模板文件。 现在,运行模板扩展是一个简单的Octave命令,如下所示:txt = st4Render...
不仅外部函数被包装,结构也被包装,暴露仅托管类和函数给 C# 用户。 已在Visual Studio 2017和Matlab(Coder)2018a组合下进行了测试。 wrap_dll_csharp(filename_prj,filename_cs,namespace_cs,classname_cs...
封装好的ADO类,可以避免COM接口的调用麻烦。转载请保留版权