Java 转发类示例
在阅读中,我看到了Java 转发类示例,java,effective-java,Java,Effective Java,在阅读中,我看到了第16项中的以下示例:偏爱组合而不是继承 在下面的InstrumentedSet中,这本书显示我们可以跟踪元素插入的次数(通过InstrumentedSet.addCount变量) 为此,我们可以简单地附加到此类对象的addCount,然后调用ForwardingSet.add(),它调用实际的集合类的add()的实际实现 //可重用转发类 公共类ForwardingSet实现集合{ 私人终场 公共转发集(集s){this.s=s;} public void clear(){s
第16项中的以下示例:偏爱组合而不是继承
在下面的InstrumentedSet
中,这本书显示我们可以跟踪元素插入的次数(通过InstrumentedSet.addCount
变量)
为此,我们可以简单地附加到此类对象的addCount
,然后调用ForwardingSet.add()
,它调用实际的集合
类的add()
的实际实现
//可重用转发类
公共类ForwardingSet实现集合{
私人终场
公共转发集(集s){this.s=s;}
public void clear(){s.clear();}
公共布尔包含(对象o){返回s.contains(o);}
...
}
//包装类-使用组合代替继承
公共类InstrumentedSet扩展了ForwardingSet{
私有int addCount=0
公共指令集(集合s){super(s);}
@重写公共布尔加法(E){
addCount++;
返回super.add(e);
}
...
}
我是否正确地理解到,要使用这种模式/方法,转发*
类必须调用其父类的所有方法(在本例中为集
)?是的,转发集
将其所有调用委托/转发到支持集
您可能想看看流行的番石榴库中的一个工作示例:。[没有足够的代表发表评论,所以我会留下答案]
回答你的问题:
我的理解正确吗?要使用这种模式/方法,Forwarding*类必须调用其父类的所有方法(在本例中是Set)
是-这是一种实现decorator模式的方法(如本书所述)。由于Forwarding*类实现了一个接口(即Set
接口),因此它必须遵守接口约定并实现这些方法
对于您在评论中提出的问题:
如果ForwardingSet最终完成了Set所做的一切,那么声明ForwardingSet有什么意义呢?也就是说,为什么不使用集合成员变量声明InstrumentedSet
这样做允许您有选择地修改要修饰的方法,从而删除大量样板代码。此外,当接口更新时,您只需要在一个位置而不是N个位置(如果您有N个装饰器)进行更新。代码也更加简洁易读,因为我不必实现decorator对象中的每个方法
例如
下面是Forwarding类和2个decorator,这两个类都只在向集合中添加内容时修改行为,而不修改其他内容
如果set接口发生更改,我现在只需要在一个类中更新ForwardingSet
类。如果我没有上那门课,按照你的建议去做,那么我会的
需要在我的每个decorator中实现整个Set接口(可读性不高)
我需要单独更新每个decorator以实现更改(即新方法)
公共抽象类ForwardingSet实现集合{
设置代表;
//设置方法委派。。。
}
/**
*在这个decorator中,我希望在每次插入某个内容时(并且仅在
*插入)
*/
公共类LoggingSet扩展了ForwardingSet{
//构造函数来设置委托
@凌驾
公共空间添加(E){
log.info(“将对象添加到集合…”)
增订(e);
}
}
/**
*在这个decorator中,我增加了一个计数器,就像示例中一样-仅当
*插入
*/
公共类计数集扩展了ForwardingSet{
整数计数=0;
//构造函数来设置委托
@凌驾
公共空间添加(E){
计数++;
增订(e);
}
}
因此,如果Set
类更新,那么就不会有编译时错误提醒我这个添加的方法,对吗?classSet
从不更新。:-)但是你明白了,在Java中,你需要维护两个版本。在Groovy这样的动态语言中,可以构建一个转发代理对象。代理对象在Java中也是可能的,但这不是一种日常的方法。理论上,是的,它可以,这会给Java 8之前的实现中的转发集
带来一点小麻烦。在真实场景中,将通过在抽象集
中引入默认实现方法来添加新方法,但此转发集
是一个例外,它实现了集
,因此无法获取它们。但是,由于Java 8,它将接收默认的接口方法,这些方法将被放入集合
(即新的拆分器
)。因此,您的ForwardingSet
将在Java 8中派生这些新方法。如果ForwardingSet
最终完全执行Set
的所有操作,那么声明ForwardingSet
有什么意义?也就是说,为什么不使用Set
成员变量声明InstrumentedSet
?我个人不认为仅仅为了扩展而声明ForwardingSet
有什么意义。必须始终实现在接口中声明的所有公共方法。这不是一个随意选择的选项。因此,ForwardingSet必须遵守接口合同并实施它们。
// Reusable forwarding class
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) { this.s = s; }
public void clear() { s.clear(); }
public boolean contains(Object o) { return s.contains(o); }
...
}
// Wrapper class - uses composition in place of inheritance
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) { super(s); }
@Override public boolean add(E e) {
addCount++;
return super.add(e);
}
...
}
public abstract class ForwardingSet<E> implements Set<E> {
Set<E> delegate;
// set method delegations ...
}
/**
* in this decorator I want to log every time something is inserted (and ONLY
* inserted)
*/
public class LoggingSet<E> extends ForwardingSet<E> {
// constructor to set delegate
@Override
public void add(E e) {
log.info("Adding object to set...")
super.add(e);
}
}
/**
* in this decorator I increment a counter just like the example - again ONLY when
* inserting
*/
public class CountingSet<E> extends ForwardingSet<E> {
int count = 0;
// constructor to set delegate
@Override
public void add(E e) {
count++;
super.add(e);
}
}