Composition Over Inheritance - Wikipedia

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

5/12/23, 11:01 PM Composition over inheritance - Wikipedia

Composition over inheritance


Composition over
inheritance (or composite
reuse principle) in object-
oriented programming (OOP)
is the principle that classes
should achieve polymorphic
behavior and code reuse by
their composition (by
containing instances of other
classes that implement the
desired functionality) rather
than inheritance from a base or
parent class.[2] This principle
of OOP is discussed in the book
Design Patterns (1994).[3]

Basics
An implementation of This diagram shows how the fly and sound behavior of an animal can be
composition over inheritance designed in a flexible way by using the composition over inheritance
typically begins with the design principle.[1]
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.

Example

Inheritance

An example in C++ follows:

class Object
{
public:
virtual void update() {
// no-op
}

virtual void draw() {

https://en.wikipedia.org/wiki/Composition_over_inheritance 1/7
5/12/23, 11:01 PM Composition over inheritance - Wikipedia
// no-op
}

virtual void collide(Object objects[]) {


// no-op
}
};

class Visible : public Object


{
Model* model;

public:
virtual void draw() override {
// code to draw a model at the position of this object
}
};

class Solid : public Object


{
public:
virtual void collide(Object objects[]) override {
// code to check for and react to collisions with other objects
}
};

class Movable : public Object


{
public:
virtual void update() override {
// code to update the position of this object
}
};

Then, suppose we also have these concrete classes:

class Player - which is Solid, Movable and Visible


class Cloud - which is Movable and Visible, but not Solid
class Building - which is Solid and Visible, but not Movable
class Trap - which is Solid, but neither Visible nor Movable

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.

Composition and interfaces

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 Java and C#.

Introduce an abstract class named VisibilityDelegate, with the subclasses NotVisible and
Visible, which provides a means of drawing an object:

class VisibilityDelegate
{
public:
virtual void draw() = 0;
};

https://en.wikipedia.org/wiki/Composition_over_inheritance 2/7
5/12/23, 11:01 PM Composition over inheritance - Wikipedia
class NotVisible : public VisibilityDelegate
{
public:
virtual void draw() override {
// no-op
}
};

class Visible : public VisibilityDelegate


{
public:
virtual void draw() override {
// code to draw a model at the position of this object
}
};

Introduce an abstract class named UpdateDelegate, with the subclasses NotMovable and
Movable, which provides a means of moving an object:

class UpdateDelegate
{
public:
virtual void update() = 0;
};

class NotMovable : public UpdateDelegate


{
public:
virtual void update() override {
// no-op
}
};

class Movable : public UpdateDelegate


{
public:
virtual void update() override {
// code to update the position of this object
}
};

Introduce an abstract class named CollisionDelegate, with the subclasses NotSolid and Solid,
which provides a means of colliding with an object:

class CollisionDelegate
{
public:
virtual void collide(Object objects[]) = 0;
};

class NotSolid : public CollisionDelegate


{
public:
virtual void collide(Object objects[]) override {
// no-op
}
};

class Solid : public CollisionDelegate


{
public:
virtual void collide(Object objects[]) override {
// code to check for and react to collisions with other objects
}
};

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()

https://en.wikipedia.org/wiki/Composition_over_inheritance 3/7
5/12/23, 11:01 PM Composition over inheritance - Wikipedia

simply calls a method on the UpdateDelegate:

class Object
{
VisibilityDelegate* _v;
UpdateDelegate* _u;
CollisionDelegate* _c;

public:
Object(VisibilityDelegate* v, UpdateDelegate* u, CollisionDelegate* c)
: _v(v)
, _u(u)
, _c(c)
{}

void update() {
_u->update();
}

void draw() {
_v->draw();
}

void collide(Object objects[]) {


_c->collide(objects);
}
};

Then, concrete classes would look like:

class Player : public Object


{
public:
Player()
: Object(new Visible(), new Movable(), new Solid())
{}

// ...
};

class Smoke : public Object


{
public:
Smoke()
: Object(new Visible(), new Movable(), new NotSolid())
{}

// ...
};

Benefits
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 is 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-
https://en.wikipedia.org/wiki/Composition_over_inheritance 4/7
5/12/23, 11:01 PM Composition over inheritance - Wikipedia

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 Go[4] and Rust,[5] use type composition exclusively.

Drawbacks
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.

// Base class
public abstract class Employee
{
// Properties
protected string Name { get; set; }
protected int ID { get; set; }
protected decimal PayRate { get; set; }
protected int HoursWorked { get; }

// Get pay for the current pay period


public abstract decimal Pay();
}

// Derived subclass
public class HourlyEmployee : Employee
{
// Get pay for the current pay period
public override decimal Pay()
{
// Time worked is in hours
return HoursWorked * PayRate;
}
}

// Derived subclass
public class SalariedEmployee : Employee
{
// Get pay for the current pay period
public override decimal Pay()
{
// Pay rate is annual salary instead of hourly rate
return HoursWorked * PayRate / 2087;
}
}

Avoiding drawbacks

This drawback can be avoided by using traits, mixins, (type) embedding, or protocol extensions.

https://en.wikipedia.org/wiki/Composition_over_inheritance 5/7
5/12/23, 11:01 PM Composition over inheritance - Wikipedia

Some languages provide specific means to mitigate this:

C# provides default interface methods since version 8.0 which allows to define body to
interface member.[6]
D provides an explicit "alias this" declaration within a type can forward into it every method and
member of another contained type.[7]
Dart provides mixins with default implementations that can be shared.
Go type embedding avoids the need for forwarding methods.[8]
Java provides default interface methods since version 8. Project Lombok[9] supports
delegation using the @Delegate annotation on the field, instead of copying and maintaining the
names and types of all the methods from the delegated field.[10]
Julia macros can be used to generate forwarding methods. Several implementations exist
such as Lazy.jl[11] and TypedDelegation.jl.[12][13]
Kotlin includes the delegation pattern in the language syntax.[14]
PHP supports traits, since PHP 5.4.[15]
Raku provides a handles trait to facilitate method forwarding.[16]
Rust provides traits with default implementations.
Scala (since version 3) provides an "export" clause to define aliases for selected members of
an object.[17]
Swift extensions can be used to define a default implementation of a protocol on the protocol
itself, rather than within an individual type's implementation.[18]

Empirical studies
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"[19]

See also
Delegation pattern
Liskov substitution principle
Object-oriented design
Object composition
Role-oriented programming
State pattern
Strategy pattern

References

https://en.wikipedia.org/wiki/Composition_over_inheritance 6/7
5/12/23, 11:01 PM Composition over inheritance - Wikipedia

1. Freeman, Eric; Robson, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Head First Design
Patterns (https://archive.org/details/headfirstdesignp00free_633). O'Reilly. p. 23 (https://archiv
e.org/details/headfirstdesignp00free_633/page/n57). ISBN 978-0-596-00712-6.
2. Knoernschild, Kirk (2002). Java Design - Objects, UML, and Process: 1.1.5 Composite Reuse
Principle (CRP) (https://books.google.com/books?id=4pjbgVHzomsC&q=composite+reuse+pri
nciple&pg=PA17). Addison-Wesley Inc. ISBN 9780201750447. Retrieved 2012-05-29.
3. Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns:
Elements of Reusable Object-Oriented Software. Addison-Wesley. p. 20 (https://archive.org/de
tails/designpatternsel00gamm/page/20). ISBN 0-201-63361-2. OCLC 31171684 (https://www.w
orldcat.org/oclc/31171684).
4. Pike, Rob (2012-06-25). "Less is exponentially more" (https://commandcenter.blogspot.com/20
12/06/less-is-exponentially-more.html). Retrieved 2016-10-01.
5. "Characteristics of Object-Oriented Languages - The Rust Programming Language" (https://do
c.rust-lang.org/stable/book/ch17-01-what-is-oo.html#inheritance-as-a-type-system-and-as-cod
e-sharing). doc.rust-lang.org. Retrieved 2022-10-10.
6. "What's new in C# 8.0" (https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#d
efault-interface-methods). Microsoft Docs. Microsoft. Retrieved 2019-02-20.
7. "Alias This" (https://dlang.org/spec/class.html#alias-this). D Language Reference. Retrieved
2019-06-15.
8. "(Type) Embedding" (https://golang.org/doc/effective_go.html#embedding). The Go
Programming Language Documentation. Retrieved 2019-05-10.
9. https://projectlombok.org
10. "@Delegate" (https://projectlombok.org/features/experimental/Delegate). Project Lombok.
Retrieved 2018-07-11.
11. https://github.com/MikeInnes/Lazy.jl
12. https://github.com/JeffreySarnoff/TypedDelegation.jl
13. "Method forwarding macro" (https://discourse.julialang.org/t/method-forwarding-macro/23355).
JuliaLang. 20 April 2019. Retrieved 18 August 2022.
14. "Delegated Properties" (https://kotlinlang.org/docs/reference/delegated-properties.html). Kotlin
Reference. JetBrains. Retrieved 2018-07-11.
15. "PHP: Traits" (https://www.php.net/manual/en/language.oop5.traits.php). www.php.net.
Retrieved 23 February 2023.
16. "Type system" (https://docs.raku.org/language/typesystem#index-entry-handles_trait-handles).
docs.raku.org. Retrieved 18 August 2022.
17. "Export Clauses" (https://docs.scala-lang.org/scala3/reference/other-new-features/export.html).
Scala Documentation. Retrieved 2021-10-06.
18. "Protocols" (https://docs.swift.org/swift-book/LanguageGuide/Protocols.html). The Swift
Programming Language. Apple Inc. Retrieved 2018-07-11.
19. Tempero, Ewan; Yang, Hong Yul; Noble, James (2013). "What programmers do with
inheritance in Java" (https://www.cs.auckland.ac.nz/~ewan/qualitas/studies/inheritance/Temper
oYangNobleECOOP2013-pre.pdf) (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 (https://doi.org/10.1007%2F978-3-642-3903
8-8_24). ISBN 978-3-642-39038-8.

Retrieved from "https://en.wikipedia.org/w/index.php?title=Composition_over_inheritance&oldid=1150938294"

https://en.wikipedia.org/wiki/Composition_over_inheritance 7/7

You might also like