The Dance of Components: The Composite Design Pattern

[tr] Türkçe Oku

2023-06-22

Once upon a time, there was a tree in a forest. This tree was a complex structure with leaves and branches. Each branch could have smaller branches and leaves on it. The tree worked as a whole, holding its branches and leaves together.

This tree is an example of the Composite design pattern. The tree (Composite) contains two types of components: branches (also Composite) and leaves (Leaf). Both branches and leaves implement the same interface (Component) recognized by the tree. This allows the tree to handle its branches and leaves in the same way.

Composite Pattern Class Diagram

Now let’s tell this fairy tale with code examples:

C#

interface IComponent
{
    void Operation();
}

class Leaf : IComponent
{
    public void Operation()
    {
        // leaf operation
    }
}

class Composite : IComponent
{
    private List<IComponent> _children = new List<IComponent>();

    public void Operation()
    {
        foreach (var child in _children)
        {
            child.Operation();
        }
    }

    public void Add(IComponent component)
    {
        _children.Add(component);
    }

    public void Remove(IComponent component)
    {
        _children.Remove(component);
    }
}

Java

interface Component {
    void operation();
}

class Leaf implements Component {
    public void operation() {
        // leaf operation
    }
}

class Composite implements Component {
    private List<Component> children = new ArrayList<>();

    public void operation() {
        for (Component child : children) {
            child.operation();
        }
    }

    public void add(Component component) {
        children.add(component);
    }

    public void remove(Component component) {
        children.remove(component);
    }
}

Go

type Component interface {
    Operation()
}

type Leaf struct{}

func (Leaf) Operation() {
    // leaf operation
}

type Composite struct {
    children []Component
}

func (c *Composite) Operation() {
    for _, child := range c.children {
        child.Operation()
    }
}

func (c *Composite) Add(component Component) {
    c.children = append(c.children, component)
}

func (c *Composite) Remove(component Component) {
    // remove children
}

**Go** (continued)
```go
    for i, child := range c.children {
        if child == component {
            c.children = append(c.children[:i], c.children[i+1:]...)
            break
        }
    }
}

Rust

trait Component {
    fn operation(&self);
}

struct Leaf;

impl Component for Leaf {
    fn operation(&self) {
        // leaf operation
    }
}

struct Composite {
    children: Vec<Box<dyn Component>>,
}

impl Component for Composite {
    fn operation(&self) {
        for child in &self.children {
            child.operation();
        }
    }
}

impl Composite {
    fn add(&mut self, component: Box<dyn Component>) {
        self.children.push(component);
    }

    fn remove(&mut self, component: Box<dyn Component>) {
        // remove children
    }
}

These code examples show how to implement the Composite design pattern. In each language, an interface or trait named Component (or IComponent) is defined. This interface is implemented by the Leaf and Composite classes. The Composite class stores and manages child components. This allows Composite and Leaf objects to be handled through the same interface.

There are several important reasons to use the Composite design pattern:

  1. Simplifies Hierarchical Structures: The Composite design pattern simplifies tree-like structures. For example, files and folders in an

operating system or departments and sub-departments in a company.

  1. Provides Transparency Between Objects: The Composite design pattern allows client code to treat individual objects and object collections in the same way. This helps to make the code simpler and cleaner.

  2. Increases Code Extensibility: It is easy to add new types of components because you don’t need to change existing code. To add a new type of component, you just need to create a new class that implements the Component interface.

  3. Increases Reusability: The Composite design pattern increases code reusability. You can share code between different components that implement the same operations.

  4. Supports the Open/Closed Principle: The Composite design pattern supports the open/closed principle. That is, your code is open to new types of components but closed to modifications of existing code.

For these reasons, the Composite design pattern is an excellent choice for managing complex object hierarchies. However, it may not be suitable for all situations. For example, if the hierarchy is very deep or contains many objects, the Composite design pattern could cause performance issues. Therefore, it is important to carefully evaluate your requirements before using this pattern.



More posts like this

Gaining Flexibility by Building Bridges: Analysis and Implementations in Four Different Languages with the Bridge Design Pattern

2023-06-22 | #bridge-pattern #design-patterns #structural-patterns

Once upon a time, there were two kingdoms. Despite being very close to each other, a wide and deep river was located between them. This river made it difficult for the kingdoms to interact with each other. To solve this situation, both kingdoms decided to build a bridge. This bridge became an interface that facilitated communication between the two kingdoms. However, the bridge had different structures and features on each side.

Continue reading 


Decorator Design Pattern: The Adornments of Software

2023-06-15 | #decorator-pattern #design-patterns #structural-patterns

Once upon a time, there was an object named “Component”. This object was used to perform a specific function. However, sometimes it was necessary to extend or modify this function. That’s where “Decorators” came into play. Decorators are objects that “decorate” or extend the Component. A Decorator comes on top of a Component and extends or modifies its function. This is used to extend the functionality of the Component without changing it itself.

Continue reading 


From the Language of Code to Fairy Tales: Uniting Different Worlds with the Adapter Pattern

2023-06-15 | #adapter-pattern #design-patterns #structural-patterns

Once upon a time, there were two friends from two different worlds: the Electric Vacuum Cleaner and the Electric Outlet. The Electric Vacuum Cleaner needed energy and wanted to get this energy from the Electric Outlet. However, there was a problem. The plug of the Electric Vacuum Cleaner did not fit the Electric Outlet. They were both produced in different standards and could not communicate directly with each other. In this case, a hero emerged: the Adapter.

Continue reading 