
Inobject-oriented programming,composition over inheritance (sometimescomposition with forwarding orcomposite reuse) is a commondesign pattern that tries to achievecode reuse without requiringinheritance. Instead of having two child classes inherit functionality from a common parent, composition simulates inheritance by having the children include a copy of a "pseudo-parent" class as a field, which implements the functionality common to the two classes. Then, methods that would have been called on the child are instead called on the pseudo-parent, a technique calledmethod forwarding.[2]
Composition is generally used in languages where inheritance is unavailable or has an implementation that is considered inflexible, inconvenient, or inadequate (e.g. because a language lacksmultiple inheritance). However, many problems that are easily solved with inheritance are difficult to solve using only composition; as a result, inheritance and object composition typically work hand-in-hand, as discussed in the bookDesign Patterns (1994).[3]
An example inC++ follows:
importstd;usingstd::unique_ptr;usingstd::vector;classGameObject{public:virtualvoidupdate(){// no-op}virtualvoiddraw()const{// no-op}virtualvoidcollide(vector<GameObject>objects){// no-op}};classVisible:publicGameObject{private:unique_ptr<Model>model;public:virtualvoiddraw()constoverride{// code to draw a model at the position of this object}};classSolid:publicGameObject{public:virtualvoidcollide(vector<GameObject>objects)override{// code to check for and react to collisions with other objects}};classMovable:publicGameObject{public:virtualvoidupdate()override{// code to update the position of this object}};
Then, suppose we also have these concrete classes:
Player - which isSolid,Movable andVisibleCloud - which isMovable andVisible, but notSolidBuilding - which isSolid andVisible, but notMovableTrap - which isSolid, but neitherVisible norMovableNote that multiple inheritance is dangerous if not implemented carefully because it can lead to thediamond problem. One solution to this is to create classes such asVisibleAndSolid,VisibleAndMovable,VisibleAndSolidAndMovable, etc. for every needed combination; however, this leads to a large amount of repetitive code. C++ usesvirtual inheritance to solve the diamond problem of multiple inheritance.
The C++ examples in this section demonstrate the principle of using composition and interfaces to achieve code reuse and polymorphism. Due to the C++ language not having a dedicated keyword to declare interfaces, the following C++ example uses inheritance from a pureabstract base class. For most purposes, this is functionally equivalent to the interfaces provided in other languages, such as Java[4]: 87 and C#.[5]: 144
Introduce an abstract class namedVisibilityDelegate, with the subclassesNotVisible andVisible, which provides a means of drawing an object:
classVisibilityDelegate{public:virtualvoiddraw()const=0;};classNotVisible:publicVisibilityDelegate{public:virtualvoiddraw()constoverride{// no-op}};classVisible:publicVisibilityDelegate{public:virtualvoiddraw()constoverride{// code to draw a model at the position of this object}};
Introduce an abstract class namedUpdateDelegate, with the subclassesNotMovable andMovable, which provides a means of moving an object:
classUpdateDelegate{public:virtualvoidupdate()=0;};classNotMovable:publicUpdateDelegate{public:virtualvoidupdate()override{// no-op}};classMovable:publicUpdateDelegate{public:virtualvoidupdate()override{// code to update the position of this object}};
Introduce an abstract class namedCollisionDelegate, with the subclassesNotSolid andSolid, which provides a means of colliding with an object:
importstd;usingstd::vector;classCollisionDelegate{public:virtualvoidcollide(vector<GameObject>objects)=0;};classNotSolid:publicCollisionDelegate{public:virtualvoidcollide(vector<GameObject>objects)override{// no-op}};classSolid:publicCollisionDelegate{public:virtualvoidcollide(vector<GameObject>objects)override{// code to check for and react to collisions with other objects}};
Finally, introduce a class namedGameObject with members to control its visibility (using aVisibilityDelegate), movability (using anUpdateDelegate), and solidity (using aCollisionDelegate). This class has methods which delegate to its members, e.g.update() simply calls a method on theUpdateDelegate:
importstd;usingstd::unique_ptr;usingstd::vector;classGameObject{private:unique_ptr<VisibilityDelegate>visibilityDelegate;unique_ptr<UpdateDelegate>updateDelegate;unique_ptr<CollisionDelegate>collisionDelegate;public:GameObject(VisibilityDelegate*v,UpdateDelegate*u,CollisionDelegate*c):visibilityDelegate{std::make_unique<VisibilityDelegate>(v)},updateDelegate{std::make_unique<UpdateDelegate>(u)},collisionDelegate{std::make_unique<CollisionDelegate>(c)}{}voidupdate(){updateDelegate->update();}voiddraw()const{visibilityDelegate->draw();}voidcollide(vector<GameObject>objects){collisionDelegate->collide(objects);}};
Then, concrete classes would look like:
classPlayer:publicGameObject{public:Player():GameObject(newVisible(),newMovable(),newSolid()){}// ...};classSmoke:publicGameObject{public:Smoke():GameObject(newVisible(),newMovable(),newNotSolid()){}// ...};
To favor composition over inheritance is a design principle that gives the design higher flexibility. It is more natural to build business-domain classes out of various components than trying to find commonality between them and creating a family tree. For example, an accelerator pedal and a steering wheel share very few commontraits, yet both are vital components in a car. What they can do and how they can be used to benefit the car are easily defined. Composition also provides a more stable business domain in the long term as it is less prone to the quirks of the family members. In other words, it is better to compose what an object can do (has-a) than extend what it is (is-a).[1]
Initial design is simplified by identifying system object behaviors in separate interfaces instead of creating a hierarchical relationship to distribute behaviors among business-domain classes via inheritance. This approach more easily accommodates future requirements changes that would otherwise require a complete restructuring of business-domain classes in the inheritance model. Additionally, it avoids problems often associated with relatively minor changes to an inheritance-based model that includes several generations of classes.Composition relation is more flexible as it may be changed on runtime, while sub-typing relations are static and need recompilation in many languages.
Some languages, notablyGo[6] andRust,[7] use type composition exclusively.
One common drawback of using composition instead of inheritance is that methods being provided by individual components may have to be implemented in the derived type, even if they are onlyforwarding methods (this is true in most programming languages, but not all; see§ Avoiding drawbacks). In contrast, inheritance does not require all of the base class's methods to be re-implemented within the derived class. Rather, the derived class only needs to implement (override) the methods having different behavior than the base class methods. This can require significantly less programming effort if the base class contains many methods providing default behavior and only a few of them need to be overridden within the derived class.
Thus, the pattern of "composition over inheritance" should not be interpreted as a literal mantra or law, but rather a selectively applicable suggestion to be used where appropriate.
For example, in the C# code below, the variables and methods of theEmployee base class are inherited by theHourlyEmployee andSalariedEmployee derived subclasses. Only thePay() method needs to be implemented (specialized) by each derived subclass. The other methods are implemented by the base class itself, and are shared by all of its derived subclasses; they do not need to be re-implemented (overridden) or even mentioned in the subclass definitions.
// Base classpublicabstractclassEmployee{// PropertiesprotectedstringName{get;set;}protectedintID{get;set;}protecteddecimalPayRate{get;set;}protectedintHoursWorked{get;}// Get pay for the current pay periodpublicabstractdecimalPay();}// Derived subclasspublicclassHourlyEmployee:Employee{// Get pay for the current pay periodpublicoverridedecimalPay(){// Time worked is in hoursreturnHoursWorked*PayRate;}}// Derived subclasspublicclassSalariedEmployee:Employee{// Get pay for the current pay periodpublicoverridedecimalPay(){// Pay rate is annual salary instead of hourly ratereturnHoursWorked*PayRate/2087;}}
This drawback can be avoided by usingtraits,mixins, (type)embedding, orprotocol extensions.
Some languages provide specific means to mitigate this:
virtual methods (for specifying avirtual function) to have default implementations.[11]@Delegate annotation on the field, instead of copying and maintaining the names and types of all the methods from the delegated field.[15]handles trait to facilitate method forwarding.[21]A 2013 study of 93 open source Java programs (of varying size) found that:
While there is not huge opportunity to replace inheritance with composition (...), the opportunity is significant (median of 2% of uses [of inheritance] are only internal reuse, and a further 22% are only external or internal reuse).Our results suggest there is no need for concern regarding abuse of inheritance (at least in open-source Java software), but they do highlight the question regarding use of composition versus inheritance. If there are significant costs associated with using inheritance when composition could be used, then our results suggest there is some cause for concern.
— Temperoet al., "What programmers do with inheritance in Java"[24]