An implementation of composition over inheritance typically begins with the creation of various interfaces representing the behaviors that the system must exhibit. Interfaces can facilitate polymorphic behavior. Classes implementing the identified interfaces are built and added to business domain classes as needed. Thus, system behaviors are realized without inheritance.
In fact, business domain classes may all be base classes without any inheritance at all. Alternative implementation of system behaviors is accomplished by providing another class that implements the desired behavior interface. A class that contains a reference to an interface can support implementations of the interface—a choice that can be delayed until runtime.
An example in C++ follows:
Then, suppose we also have these concrete classes:
Note that multiple inheritance is dangerous if not implemented carefully because it can lead to the diamond problem. One solution to this is to create classes such as VisibleAndSolid, VisibleAndMovable, VisibleAndSolidAndMovable, etc. for every needed combination; however, this leads to a large amount of repetitive code. C++ uses virtual 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 pure abstract base class. For most purposes, this is functionally equivalent to the interfaces provided in other languages, such as Java3: 87 and C#.4: 144
Introduce an abstract class named VisibilityDelegate, with the subclasses NotVisible and Visible, which provides a means of drawing an object:
Introduce an abstract class named UpdateDelegate, with the subclasses NotMovable and Movable, which provides a means of moving an object:
Introduce an abstract class named CollisionDelegate, with the subclasses NotSolid and Solid, which provides a means of colliding with an object:
Finally, introduce a class named Object with members to control its visibility (using a VisibilityDelegate), movability (using an UpdateDelegate), and solidity (using a CollisionDelegate). This class has methods which delegate to its members, e.g. update() simply calls a method on the UpdateDelegate:
Then, concrete classes would look like:
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 common traits, 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).5
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, notably Go6 and Rust,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 only forwarding 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.
For example, in the C# code below, the variables and methods of the Employee base class are inherited by the HourlyEmployee and SalariedEmployee derived subclasses. Only the Pay() 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.
This drawback can be avoided by using traits, mixins, (type) embedding, or protocol extensions.
Some languages provide specific means to mitigate this:
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.— Tempero et al., "What programmers do with inheritance in Java"25
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.
Knoernschild, Kirk (2002). Java Design - Objects, UML, and Process: 1.1.5 Composite Reuse Principle (CRP). Addison-Wesley Inc. ISBN 9780201750447. Retrieved 2012-05-29. 9780201750447 ↩
Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. p. 20. ISBN 0-201-63361-2. OCLC 31171684. 0-201-63361-2 ↩
Bloch, Joshua (2018). "Effective Java: Programming Language Guide" (third ed.). Addison-Wesley. ISBN 978-0134685991. 978-0134685991 ↩
Price, Mark J. (2022). C# 8.0 and .NET Core 3.0 – Modern Cross-Platform Development: Build Applications with C#, .NET Core, Entity Framework Core, ASP.NET Core, and ML.NET Using Visual Studio Code. Packt. ISBN 978-1-098-12195-2. 978-1-098-12195-2 ↩
Freeman, Eric; Robson, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Head First Design Patterns. O'Reilly. p. 23. ISBN 978-0-596-00712-6. 978-0-596-00712-6 ↩
Pike, Rob (2012-06-25). "Less is exponentially more". Retrieved 2016-10-01. https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html ↩
"Characteristics of Object-Oriented Languages - The Rust Programming Language". doc.rust-lang.org. Retrieved 2022-10-10. https://doc.rust-lang.org/stable/book/ch17-01-what-is-oo.html#inheritance-as-a-type-system-and-as-code-sharing ↩
"What's new in C# 8.0". Microsoft Docs. Microsoft. Retrieved 2019-02-20. https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#default-interface-methods ↩
Skeet, Jon (23 March 2019). C# in Depth. Manning. ISBN 978-1617294532. 978-1617294532 ↩
Albahari, Joseph (2022). C# 10 in a Nutshell. O'Reilly. ISBN 978-1-098-12195-2. 978-1-098-12195-2 ↩
"Alias This". D Language Reference. Retrieved 2019-06-15. https://dlang.org/spec/class.html#alias-this ↩
"(Type) Embedding". The Go Programming Language Documentation. Retrieved 2019-05-10. https://golang.org/doc/effective_go.html#embedding ↩
https://projectlombok.org [bare URL] https://projectlombok.org ↩
"@Delegate". Project Lombok. Retrieved 2018-07-11. https://projectlombok.org/features/experimental/Delegate ↩
"MikeInnes/Lazy.jl". GitHub. https://github.com/MikeInnes/Lazy.jl ↩
"JeffreySarnoff/TypedDelegation.jl". GitHub. https://github.com/JeffreySarnoff/TypedDelegation.jl ↩
"Method forwarding macro". JuliaLang. 20 April 2019. Retrieved 18 August 2022. https://discourse.julialang.org/t/method-forwarding-macro/23355 ↩
"Delegated Properties". Kotlin Reference. JetBrains. Retrieved 2018-07-11. https://kotlinlang.org/docs/reference/delegated-properties.html ↩
"PHP: Traits". www.php.net. Retrieved 23 February 2023. https://www.php.net/manual/en/language.oop5.traits.php ↩
"Type system". docs.raku.org. Retrieved 18 August 2022. https://docs.raku.org/language/typesystem#index-entry-handles_trait-handles ↩
"Export Clauses". Scala Documentation. Retrieved 2021-10-06. https://docs.scala-lang.org/scala3/reference/other-new-features/export.html ↩
"Protocols". The Swift Programming Language. Apple Inc. Retrieved 2018-07-11. https://docs.swift.org/swift-book/LanguageGuide/Protocols.html ↩
Tempero, Ewan; Yang, Hong Yul; Noble, James (2013). "What programmers do with inheritance in Java" (PDF). ECOOP 2013 – Object-Oriented Programming. ECOOP 2013–Object-Oriented Programming. Lecture Notes in Computer Science. Vol. 7920. pp. 577–601. doi:10.1007/978-3-642-39038-8_24. ISBN 978-3-642-39038-8. 978-3-642-39038-8 ↩