7

Reading Effective Java, I saw the following example from Item 16: Favor composition over inheritance.

In the below InstrumentedSet, the book shows we can keep track of how many times an element was inserted (via the InstrumentedSet.addCount variable).

To do this, we can simply append to this class object's addCount, and then call ForwardingSet.add(), which calls the actual Set class's actual implementation of 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);
       }
       ...
    }

Am I correct in understanding that, to use this pattern/approach, it's necessary for the Forwarding* class to call all of its parent class's methods (in this case Set)?

Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384
  • You must always implement all public methods which are declared in an Interface. It is not an option to choose at will. So ForwardingSet will have to keep to the interface contract and implement them. – IgorGanapolsky Feb 24 '14 at 17:35

2 Answers2

7

Yes, you are correct, ForwardingSet delegates/forwards all of it's calls to a backing set.

You might want to have a look at a working example in a popular library Guava: ForwardingMap.

Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
  • 1
    So, if the `Set` class updates, then there's no compile-time error that would alert me to this added method, right? – Kevin Meredith Nov 22 '13 at 18:38
  • 1
    Class `Set` never updates. :-) But you get the point - in Java you would need to maintain both versions. In dynamic languages like Groovy one could build a forwarding proxy object. – Andrey Chaschev Nov 22 '13 at 18:41
  • 1
    Proxying objects is also possible in Java, but it's not a daily approach. – Andrey Chaschev Nov 22 '13 at 18:43
  • 1
    But, `Set` **could** update since it's an interface (http://docs.oracle.com/javase/7/docs/api/java/util/Set.html), yeah? In which case, `ForwardingSet` would **not** issue a compile-time warning. – Kevin Meredith Nov 22 '13 at 19:10
  • 2
    In theory, yes - it can, and this would bring a small trouble to a `ForwardingSet` for pre-Java 8 implementation. In a real-world scenario new methods will be added by introducing a default implementing method in an `AbstractSet`, but this `ForwardingSet` is an exception and it implements `Set`, so it would not get them. Since Java 8 however it will receive the default interface methods which would be put into a `Set` (i.e. the new `spliterator`). So your `ForwardingSet` would derive these new methods in Java 8. – Andrey Chaschev Nov 22 '13 at 19:34
  • FYI, in Intellij you can implement `ForwardingSet` in a few clicks: `Alt+Insert` on `ForwardingSet`, choose 'Delegate Methods...', choose the backing set field, select all methods - and that's it, so it's really not a big deal. :-) – Andrey Chaschev Nov 22 '13 at 19:39
  • You are only at risk of getting compile-time errors from Interface implementations if you update your Java version SDK in the IDE (i.e. IntelliJ). Otherwise, you don't have to worry about it, I think. – IgorGanapolsky Feb 24 '14 at 17:41
  • 2
    What is the point of declaring a `ForwardingSet`, if it ends up doing exactly everything that `Set` does? I.e. why not declare a `InstrumentedSet` with a `Set` member variable instead? I personally don't see the point of declaring `ForwardingSet` just for the sake of extending it. – thiagowfx Dec 20 '17 at 20:25
  • 1
    And what will you do if you want your ForwardingSet to have all Set’s functionality in general plus some of the methods overrided (like adding counter in example above)? You will need to write forwarding methods in this exact class. But if you have two different components which should use forwarding, will you duplicate the forwarding in both of them? I think no, so that is why it is taken out into separate Forwarding class. – user1048261 Apr 15 '20 at 07:37
0

[Not enough rep to comment so I will leave an answer]

To answer your question:

Am I correct in understanding that, to use this pattern/approach, it's necessary for the Forwarding* class to call all of its parent class's methods (in this case Set)?

Yes - this is a way of implementing the decorator pattern (as the book describes). Since the Forwarding* class implements an interface (i.e. the Set<E> interface) it must therefore abide by the interface contract and implement those methods.

For your question in the comments:

What is the point of declaring a ForwardingSet, if it ends up doing exactly everything that Set does? I.e. why not declare a InstrumentedSet with a Set member variable instead?

Doing so allows you to selectively modify which method you want to decorate and thus removes a lot of boilerplate code. Also, when the interface updates, you only need to update in one place instead of N places (if you have N decorators). The code is also more concise and readable since I don't have to implement every method in the decorator object.

As an example...

Below I have the Forwarding class and 2 decorators - both that only modify behavior when adding something to the set and nothing else..

If the set interface changes, I now only need to update in one class - the ForwardingSet class. If I didn't have that class and did as you suggest, then I would

  1. Need to implement the entire Set interface in each of my decorators (which is not very readable)

  2. I would need to update each decorator individually to implement the change (i.e. a new method)

    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);
          }
    }
    
Fermi-4
  • 645
  • 6
  • 10