7

阅读Effective Java,我从Item 16: Favor composition over inheritance.

在下面InstrumentedSet,这本书展示了我们可以跟踪元素被插入的次数(通过InstrumentedSet.addCount变量)。

为此,我们可以简单地附加到这个类对象的addCount,然后调用ForwardingSet.add(),它调用实际Set类的实际实现add()

// 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);
       }
       ...
    }

我是否正确理解,要使用这种模式/方法,Forwarding*类必须调用其所有父类的方法(在这种情况下Set)?

4

2 回答 2

6

是的,您是对的,ForwardingSet将所有调用委托/转发到支持集。

您可能想看一下流行库 Guava 中的一个工作示例:ForwardingMap

于 2013-11-22T18:36:03.270 回答
0

[没有足够的代表发表评论,所以我会留下答案]

要回答您的问题:

我是否正确理解,要使用这种模式/方法,Forwarding* 类必须调用其所有父类的方法(在本例中为 Set)?

是的 - 这是实现装饰器模式的一种方式(如书中所述)。由于 Forwarding* 类实现了一个接口(即Set<E>接口),因此它必须遵守接口契约并实现那些方法。

对于您在评论中的问题:

如果 ForwardingSet 最终完成了 Set 所做的一切,那么声明 ForwardingSet 的意义何在?即为什么不声明一个带有 Set 成员变量的 InstrumentedSet 呢?

这样做可以让您有选择地修改要装饰的方法,从而删除大量样板代码。另外,当界面更新时,你只需要更新一个地方而不是N个地方(如果你有N个装饰器)。代码也更加简洁易读,因为我不必在装饰器对象中实现每个方法。

举个例子...

下面我有 Forwarding 类和 2 个装饰器 - 两者都只在向集合中添加东西时修改行为,没有别的。

如果设置界面发生变化,我现在只需要更新一个类——ForwardingSet类。如果我没有那门课并按照您的建议做,那么我会

  1. 需要在我的每个装饰器中实现整个 Set 接口(这不是很可读)

  2. 我需要单独更新每个装饰器以实现更改(即新方法)

    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);
          }
    }
    
于 2020-12-18T22:37:16.807 回答