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]

Examples

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

The following example, written in Java, implements a graphic class, which can be either an ellipse or a composition of several graphics. Every graphic can be printed. In Backus-Naur form,

       Graphic ::= ellipse | GraphicList        GraphicList ::= empty | Graphic GraphicList

It could be extended to implement several other shapes (rectangle, etc.) and methods (translate, etc.).

Java

importjava.util.List;importjava.util.ArrayList;/** "Component" */interfaceGraphic{//Prints the graphic.publicvoidprint();}/** "Composite" */classCompositeGraphicimplementsGraphic{//Collection of child graphics.privatefinalList<Graphic>childGraphics=newArrayList<>();//Adds the graphic to the composition.publicvoidadd(Graphicgraphic){childGraphics.add(graphic);}//Prints the graphic.@Overridepublicvoidprint(){for(Graphicgraphic:childGraphics){graphic.print();//Delegation}}}/** "Leaf" */classEllipseimplementsGraphic{//Prints the graphic.@Overridepublicvoidprint(){System.out.println("Ellipse");}}/** Client */classCompositeDemo{publicstaticvoidmain(String[]args){//Initialize four ellipsesEllipseellipse1=newEllipse();Ellipseellipse2=newEllipse();Ellipseellipse3=newEllipse();Ellipseellipse4=newEllipse();//Creates two composites containing the ellipsesCompositeGraphiccompositGraphic2=newCompositeGraphic();compositGraphic2.add(ellipse1);compositGraphic2.add(ellipse2);compositGraphic2.add(ellipse3);CompositeGraphiccompositGraphic3=newCompositeGraphic();compositGraphic3.add(ellipse4);//Create another graphics that contains two graphicsCompositeGraphiccompositGraphic=newCompositeGraphic();compositGraphic.add(compositGraphic2);compositGraphic.add(compositGraphic3);//Prints the complete graphic (Four times the string "Ellipse").compositGraphic.print();}}

See also

Related Research Articles

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">Singleton pattern</span> Design pattern in object-oriented software development

In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to a singular instance. One of the well-known "Gang of Four" design patterns, which describe how to solve recurring problems in object-oriented software, the pattern is useful when exactly one object is needed to coordinate actions across a system.

<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 class-based 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 the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.

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 objects from 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 computer programming, the proxy pattern is a software design pattern. A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate. In short, a proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy, extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked. For the client, usage of a proxy object is similar to using the real object, because both implement the same interface.

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

In computer programming, the strategy pattern is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

Multiple dispatch or multimethods is a feature of some programming languages in which a function or method can be dynamically dispatched based on the run-time (dynamic) type or, in the more general case, some other attribute of more than one of its arguments. This is a generalization of single-dispatch polymorphism where a function or method call is dynamically dispatched based on the derived type of the object on which the method has been called. Multiple dispatch routes the dynamic dispatch to the implementing function or method using the combined characteristics of one or more arguments.

A method in object-oriented programming (OOP) is a procedure associated with a message and an object. An object consists of state data and behavior; these compose an interface, which specifies how the object may be utilized by any of its various consumers. A method is a behavior of an object parametrized by a consumer.

In object-oriented programming, in languages such as C++, and Object Pascal, a virtual function or virtual method is an inheritable and overridable function or method for which dynamic dispatch is facilitated. This concept is an important part of the (runtime) polymorphism portion of object-oriented programming (OOP). In short, a virtual function defines a target function to be executed, but the target might not be known at compile time.

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.

<i>Modern C++ Design</i> Book by Andrei Alexandrescu

Modern C++ Design: Generic Programming and Design Patterns Applied is a book written by Andrei Alexandrescu, published in 2001 by Addison-Wesley. It has been regarded as "one of the most important C++ books" by Scott Meyers.

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.

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

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

In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic. The pattern is frequently used in the context of domain-driven design.

Wt is an open-source widget-centric web framework for the C++ programming language. It has an API resembling that of Qt framework, also using a widget-tree and an event-driven signal/slot system.

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.