- Inheritance (White-box reuse)
- Composition (Black-box reuse)
- Parametrized Types or Generics
While inheritance makes is easy for extending/reusing behavior, sure has some serious flaws which makes it the last choice for reuse.
- Class inheritance is defined statically at compile time. This means you cannot change the implementations inherited from the parent at run-time.
- Inheritance also breaks encapsulation since the subclass is exposed to the details of the parent’s class.
- There is tight coupling between super class’ implementation and subclass’ implementation. Any changes in the parent, forces the child class to also change.
Some of these drawbacks can be avoided by using Interface based inheritance instead of Class based inheritance.
Usually we want to favor object composition over class inheritance. Object composition overcomes all these problems by talking to composed class via their public interface. Favoring composition also helps in keeping each class encapsulated and focused on one task thus encouraging Single Responsibility principle.
Parameterized types or Generics is also another interesting way to reuse behavior/code. For example, to declare a list of String objects, you supply the String type as a parameter. The language implementation will create a customized version of the List class template for each type of element.