Chapeter 4 Classes and Interfaces
To summarize, inheritance is powerful, but it is problematic because it violates encapsulation.
Inheriting from ordinary concrete classes across package boundaries is dangerous.
Item 16: Favor composition over inheritance
-
1.Unlike method invocation, inheritance violates encapsulation
- it is safe to extend a class if merely adding new methods and refraining from overriding existing methods.
- [Best Solution]composition-and-forwarding: the class itself and a reusable forwarding class
- [Design Pattern] composition, give your new class a private field that references an instance of the existing class.
- [Design pattern] Decorator pattern, The InstrumentedSet class is known as a wrapper class because each InstrumentedSet instance contains (“wraps”) another Set instance
- [Comparison]
- [1] inheritance-based class works only for a single concrete class and requires a separate constructor for each supported constructor in the superclass
- [2] the wrapper class can be used to instrument any Set implementation and will work in conjunction with any preexisting constructor
- [Disadvantages ?] wrapper classes are not suited for use in callback frameworks
-
a wrapped object doesn’t know of its wrapper, it passes a reference to itself ( this ) and callbacks elude the wrapper.
// Sometimes the combination of composition and forwarding is loosely referred to asdelegation. // Technically it’s not delegation unless the wrapper object passes itself to the wrapped object =============composition-and-forwarding: robust & flexible============ // 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); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } } // 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 addAll(Collection<? extends E> c) { return s.addAll(c);} ... } // The design of the InstrumentedSet class is enabled by the existence of the Set interface =============composition-and-forwarding: robust & flexible============
-
-
2.Is every B really an A?
- If you cannot truthfully answer yes to this question, B should not extend A.
- If the answer is no, it is often the case that B should contain a private instance of A and expose a smaller and simpler API