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
    类更新,那么就不会有编译时错误提醒我这个添加的方法,对吗?class
    Set
    从不更新。:-)但是你明白了,在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);
          }
    }