Skip to content

Instantly share code, notes, and snippets.

@St0iK
Last active May 27, 2019 21:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save St0iK/8e4aa0f5839a5651c6bf5e6cf806aeed to your computer and use it in GitHub Desktop.
Save St0iK/8e4aa0f5839a5651c6bf5e6cf806aeed to your computer and use it in GitHub Desktop.
Composite Pattern

Composite

Com­pos­ite is a struc­tur­al design pat­tern that lets you com­pose objects into tree struc­tures and then work with these struc­tures as if they were indi­vid­ual objects.

Using the Com­pos­ite pat­tern makes sense only when the core model of your app can be rep­re­sent­ed as a tree.

For exam­ple, imag­ine that you have two types of objects: Products and Boxes. A Box can con­tain sev­er­al Products as well as a num­ber of small­er Boxes. These lit­tle Boxes can also hold some Products or even small­er Boxes, and so on.

Use the Com­pos­ite pat­tern when you have to imple­ment a tree-like object structure.

The Com­pos­ite pat­tern pro­vides you with two basic ele­ment types that share a com­mon inter­face: sim­ple leaves and com­plex con­tain­ers

A con­tain­er can be com­posed of both leaves and other con­tain­ers. This lets you con­struct a nest­ed recur­sive object struc­ture that resem­bles a tree.

You can work with com­plex tree struc­tures more con­ve­nient­ly: use poly­mor­phism and recur­sion to your advantage.

Open/Closed Prin­ci­ple. You can intro­duce new ele­ment types into the app with­out break­ing the exist­ing code, which now works with the object tree.

RenderableInterface.php

<?php

namespace DesignPatterns\Structural\Composite;

interface RenderableInterface
{
   public function render(): string;
}
<?php

namespace DesignPatterns\Structural\Composite;

/**
 * The composite node MUST extend the component contract. This is mandatory for building
 * a tree of components.
 */
class Form implements RenderableInterface
{
    /**
     * @var RenderableInterface[]
     */
    private $elements;

    /**
     * runs through all elements and calls render() on them, then returns the complete representation
     * of the form.
     *
     * from the outside, one will not see this and the form will act like a single object instance
     *
     * @return string
     */
    public function render(): string
    {
        $formCode = '<form>';

        foreach ($this->elements as $element) {
            $formCode .= $element->render();
        }

        $formCode .= '</form>';

        return $formCode;
    }

    /**
     * @param RenderableInterface $element
     */
    public function addElement(RenderableInterface $element)
    {
        $this->elements[] = $element;
    }
}
<?php

namespace DesignPatterns\Structural\Composite;

class InputElement implements RenderableInterface
{
    public function render(): string
    {
        return '<input type="text" />';
    }
}
<?php

namespace DesignPatterns\Structural\Composite;

class TextElement implements RenderableInterface
{
    /**
     * @var string
     */
    private $text;

    public function __construct(string $text)
    {
        $this->text = $text;
    }

    public function render(): string
    {
        return $this->text;
    }
}

Com­pos­ite and Dec­o­ra­tor have sim­i­lar struc­ture dia­grams since both rely on recur­sive com­po­si­tion to orga­nize an open-ended num­ber of objects.

A Dec­o­ra­tor is like a Com­pos­ite but only has one child com­po­nent. There’s anoth­er sig­nif­i­cant dif­fer­ence: Dec­o­ra­tor adds addi­tion­al respon­si­bil­i­ties to the wrapped object, while Com­pos­ite just “sums up” its chil­dren’s results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment