Composite pattern

Last updated

In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly. [1]

Contents

Overview

The Composite [2] design pattern is one of the twenty-three well-known GoF design patterns that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.

What problems can the Composite design pattern solve?

When defining (1) Part objects and (2) Whole objects that act as containers for Part objects, clients must treat them separately, which complicates client code. [3]

What solution does the Composite design pattern describe?

This enables clients to work through the Component interface to treat Leaf and Composite objects uniformly: Leaf objects perform a request directly, and Composite objects forward the request to their child components recursively downwards the tree structure. This makes client classes easier to implement, change, test, and reuse.

See also the UML class and object diagram below.

Motivation

When dealing with Tree-structured data, programmers often have to discriminate between a leaf-node and a branch. This makes code more complex, and therefore, more error prone. The solution is an interface that allows treating complex and primitive objects uniformly. In object-oriented programming, a composite is an object designed as a composition of one-or-more similar objects, all exhibiting similar functionality. This is known as a "has-a" relationship between objects. [4] The key concept is that you can manipulate a single instance of the object just as you would manipulate a group of them. The operations you can perform on all the composite objects often have a least common denominator relationship. For example, if defining a system to portray grouped shapes on a screen, it would be useful to define resizing a group of shapes to have the same effect (in some sense) as resizing a single shape.

When to use

Composite should be used when clients ignore the difference between compositions of objects and individual objects. [1] If programmers find that they are using multiple objects in the same way, and often have nearly identical code to handle each of them, then composite is a good choice; it is less complex in this situation to treat primitives and composites as homogeneous.

Structure

UML class and object diagram

A sample UML class and object diagram for the Composite design pattern. W3sDesign Composite Design Pattern UML.jpg
A sample UML class and object diagram for the Composite design pattern.

In the above UML class diagram, the Client class doesn't refer to the Leaf and Composite classes directly (separately). Instead, the Client refers to the common Component interface and can treat Leaf and Composite uniformly.
The Leaf class has no children and implements the Component interface directly.
The Composite class maintains a container of child Component objects (children) and forwards requests to these children (for each child in children: child.operation()).

The object collaboration diagram shows the run-time interactions: In this example, the Client object sends a request to the top-level Composite object (of type Component) in the tree structure. The request is forwarded to (performed on) all child Component objects (Leaf and Composite objects) downwards the tree structure.

Defining Child-Related Operations
Defining child-related operations in the Composite design pattern. W3sDesign Composite Design Pattern Type Safety UML.jpg
Defining child-related operations in the Composite design pattern.

There are two design variants for defining and implementing child-related operations like adding/removing a child component to/from the container (add(child)/remove(child)) and accessing a child component (getChild()):

The Composite design pattern emphasizes uniformity over type safety.

UML class diagram

Composite pattern in UML. Composite UML class diagram (fixed).svg
Composite pattern in UML.
Component
Leaf
Composite
Composite pattern in LePUS3. Composite pattern in LePUS3.png
Composite pattern in LePUS3.

Variation

As it is described in Design Patterns, the pattern also involves including the child-manipulation methods in the main Component interface, not just the Composite subclass. More recent descriptions sometimes omit these methods. [7]

Example

This C++14 implementation is based on the pre C++98 implementation in the book.

#include<iostream>#include<string>#include<list>#include<memory>#include<stdexcept>typedefdoubleCurrency;// declares the interface for objects in the composition.classEquipment{// Componentpublic:// implements default behavior for the interface common to all classes, as appropriate.virtualconststd::string&getName(){returnname;}virtualvoidsetName(conststd::string&name_){name=name_;}virtualCurrencygetNetPrice(){returnnetPrice;}virtualvoidsetNetPrice(CurrencynetPrice_){netPrice=netPrice_;}// declares an interface for accessing and managing its child components.virtualvoidadd(std::shared_ptr<Equipment>)=0;virtualvoidremove(std::shared_ptr<Equipment>)=0;virtual~Equipment()=default;protected:Equipment():name(""),netPrice(0){}Equipment(conststd::string&name_):name(name_),netPrice(0){}private:std::stringname;CurrencynetPrice;};// defines behavior for components having children.classCompositeEquipment:publicEquipment{// Compositepublic:// implements child-related operations in the Component interface.virtualCurrencygetNetPrice()override{Currencytotal=Equipment::getNetPrice();for(constauto&i:equipment){total+=i->getNetPrice();}returntotal;}virtualvoidadd(std::shared_ptr<Equipment>equipment_)override{equipment.push_front(equipment_.get());}virtualvoidremove(std::shared_ptr<Equipment>equipment_)override{equipment.remove(equipment_.get());}protected:CompositeEquipment():equipment(){}CompositeEquipment(conststd::string&name_):equipment(){setName(name_);}private:// stores child components.std::list<Equipment*>equipment;};// represents leaf objects in the composition.classFloppyDisk:publicEquipment{// Leafpublic:FloppyDisk(conststd::string&name_){setName(name_);}// A leaf has no children.voidadd(std::shared_ptr<Equipment>)override{throwstd::runtime_error("FloppyDisk::add");}voidremove(std::shared_ptr<Equipment>)override{throwstd::runtime_error("FloppyDisk::remove");}};classChassis:publicCompositeEquipment{public:Chassis(conststd::string&name_){setName(name_);}};intmain(){// The smart pointers prevent memory leaks.std::shared_ptr<FloppyDisk>fd1=std::make_shared<FloppyDisk>("3.5in Floppy");fd1->setNetPrice(19.99);std::cout<<fd1->getName()<<": netPrice="<<fd1->getNetPrice()<<'\n';std::shared_ptr<FloppyDisk>fd2=std::make_shared<FloppyDisk>("5.25in Floppy");fd2->setNetPrice(29.99);std::cout<<fd2->getName()<<": netPrice="<<fd2->getNetPrice()<<'\n';std::unique_ptr<Chassis>ch=std::make_unique<Chassis>("PC Chassis");ch->setNetPrice(39.99);ch->add(fd1);ch->add(fd2);std::cout<<ch->getName()<<": netPrice="<<ch->getNetPrice()<<'\n';fd2->add(fd1);}

The program output is

3.5inFloppy:netPrice=19.995.25inFloppy:netPrice=29.99PCChassis:netPrice=89.97terminatecalledafterthrowinganinstanceof'std::runtime_error'what():FloppyDisk::add

See also

Related Research Articles

<span class="mw-page-title-main">Abstract factory pattern</span> Software design pattern

The abstract factory pattern in software engineering is a design pattern that provides a way to create families of related objects without imposing their concrete classes, by encapsulating a group of individual factories that have a common theme without specifying their concrete classes. According to this pattern, a client software component creates a concrete implementation of the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part of the family. The client does not know which concrete objects it receives from each of these internal factories, as it uses only the generic interfaces of their products. This pattern separates the details of implementation of a set of objects from their general usage and relies on object composition, as object creation is implemented in methods exposed in the factory interface.

The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently", introduced by the Gang of Four. The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.

<span class="mw-page-title-main">Flyweight pattern</span> Software design pattern for objects

In computer programming, the flyweight software design pattern refers to an object that minimizes memory usage by sharing some of its data with other similar objects. The flyweight pattern is one of twenty-three well-known GoF design patterns. These patterns promote flexible object-oriented software design, which is easier to implement, change, test, and reuse.

In object oriented programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify their exact class. Rather than by calling a constructor, this is done by calling a factory method to create an object. Factory methods can either be specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes.

The prototype pattern is a creational design pattern in software development. It is used when the types of objects to create is determined by a prototypical instance, which is cloned to produce new objects. This pattern is used to avoid subclasses of an object creator in the client application, like the factory method pattern does, and to avoid the inherent cost of creating a new object in the standard way when it is prohibitively expensive for a given application.

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified. Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object.

In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.

In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.

In computer programming, the interpreter pattern is a design pattern that specifies how to evaluate sentences in a language. The basic idea is to have a class for each symbol in a specialized computer language. The syntax tree of a sentence in the language is an instance of the composite pattern and is used to evaluate (interpret) the sentence for a client. See also Composite pattern.

In software design and engineering, the observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

A method in object-oriented programming (OOP) is a procedure associated with an object, and generally also a message. An object consists of state data and behavior; these compose an interface, which specifies how the object may be used. A method is a behavior of an object parametrized by a user.

In object-oriented programming such as is often used in C++ and Object Pascal, a virtual function or virtual method is an inheritable and overridable function or method that is dispatched dynamically. Virtual functions are an important part of (runtime) polymorphism in object-oriented programming (OOP). They allow for the execution of target functions that were not precisely identified at compile time.

In computer programming, run-time type information or run-time type identification (RTTI) is a feature of some programming languages that exposes information about an object's data type at runtime. Run-time type information may be available for all types or only to types that explicitly have it. Run-time type information is a specialization of a more general concept called type introspection.

In computer science, object composition and object aggregation are closely related ways to combine objects or data types into more complex ones. In conversation the distinction between composition and aggregation is often ignored. Common kinds of compositions are objects used in object-oriented programming, tagged unions, sets, sequences, and various graph structures. Object compositions relate to, but are not the same as, data structures.

In computer programming, an opaque pointer is a special case of an opaque data type, a data type declared to be a pointer to a record or data structure of some unspecified type.

The curiously recurring template pattern (CRTP) is an idiom, originally in C++, in which a class X derives from a class template instantiation using X itself as a template argument. More generally it is known as F-bound polymorphism, and it is a form of F-bounded quantification.

C++11 is a version of the ISO/IEC 14882 standard for the C++ programming language. C++11 replaced the prior version of the C++ standard, called C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by the publication year of the specification, though it was formerly named C++0x because it was expected to be published before 2010.

In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (null) behavior. The null object design pattern, which describes the uses of such objects and their behavior, was first published as "Void Value" and later in the Pattern Languages of Program Design book series as "Null Object".

In object-oriented design, the chain-of-responsibility pattern is a behavioral design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.

<span class="mw-page-title-main">Composition over inheritance</span> Software design pattern

Composition over inheritance in object-oriented programming (OOP) is the principle that classes should favor polymorphic behavior and code reuse by their composition over inheritance from a base or parent class. Ideally all reuse can be achieved by assembling existing components, but in practice inheritance is often needed to make new ones. Therefore inheritance and object composition typically work hand-in-hand, as discussed in the book Design Patterns (1994).

References

  1. 1 2 Gamma, Erich; Richard Helm; Ralph Johnson; John M. Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. pp.  395. ISBN   0-201-63361-2.
  2. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. pp.  163ff. ISBN   0-201-63361-2.{{cite book}}: CS1 maint: multiple names: authors list (link)
  3. "The Composite design pattern - Problem, Solution, and Applicability". w3sDesign.com. Retrieved 2017-08-12.
  4. Scott Walters (2004). Perl Design Patterns Book. Archived from the original on 2016-03-08. Retrieved 2010-01-18.
  5. "The Composite design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
  6. "The Composite design pattern - Implementation". w3sDesign.com. Retrieved 2017-08-12.
  7. Geary, David (13 September 2002). "A look at the Composite design pattern". Java Design Patterns. JavaWorld . Retrieved 2020-07-20.