The Composite Design Pattern in PHP: Part III Getting Child Elements and Removing Composites and Leaves

removeGet the Children and Remove Them

If you haven’t looked at Part I and Part II of the Composite pattern series, you may want to do so first before looking at this post. As you saw in Parts I & II, the Composite pattern, while clearly working with a tree-like hierarchy is a great pattern that favors composition over inheritance, and as you work with this pattern, you’ll see why programming to the interface instead of programming to the implementation makes life easier for the programmer.

In this installment, I want to implement two key methods, getChild() and remove(). Going back to the beginning, again I’d like to use materials developed by former colleague and friend, Dr. Chandima Cumaranatunge. Also, I’m going to switch the main interface (IComponent) to an abstract class so that some of the methods can be implemented in the interface. Because both abstract classes and interfaces are both interfaces, the abstract class retains the name IComponent. To see all of the changes and the new implementations, first take a look at the outcome generated (click the Play button) and download the source code:
PlayDownload

Reference to Composites as Root Children

Up to this point, the Composite examples have worked perfectly well without implementing the getChild() method. All access to the composites and leaves has been handled by the operation() method. Why bother? First of all, the Client can directly access any single node. The child the program is “getting” is either a composite or a leaf, and there will be no need to iterate through the entire root component to find a single node. Secondly, and more importantly, the Client can create new nodes without creating new variables. When a node is removed, this insures good garbage collection. (Garbage collection, in this context, refers to regaining unused resources and memory used by the nodes that have been removed.) It makes for cleaner and faster operations.

Since the getChild() is part of the interface, first take a look at the abstract class. Then, we’ll look at the implementation in the Composite class.

<?php
abstract class IComponent
{
    protected $parentNode;
 
    abstract function operation();
    abstract function add(IComponent $comOn);
    abstract function remove(IComponent $comGone);
    abstract function getChild($int);
    protected abstract function getComposite();
 
    protected function setParent(IComponent $compositeNode)
    {
        $this->parentNode=$compositeNode;
    }
 
    public function getParent()
    {
        return $this->parentNode;
    }
 
    protected function removeParentRef()
    {
        $this->parentNode=null;
    }
}
?>

As you can see, the getChild($int) method is still abstract with a single parameter. Were it possible in PHP, an int type-hint would be included in the parameter; so we’ll just have to make-do with the variable name, $int to assure that we remember to use an integer in the parameter. However, the method’s implementation is pretty straightforward:

public function getChild($int)
{
   if(($int > 0) && ($int <= count($this->aChildren)))
    {
        return $this->aChildren[$int-1];
     }
     else
     {
         return null;
     }   
}

The method checks the value of the integer to make sure it’s greater than zero and greater than or equal to the length of the array (aChildren) and then returns the requested object as an element of the array. Otherwise, it returns a null value.

The value of the method becomes apparent in the Client reference, as the following snippet from the Client shows:

...
$this->rootCompos=new Composite("Root");       
$this->rootCompos->add(new Composite("-Composite 1"));
$this->rootCompos->getChild(1)->add(new Leaf("--C1:leaf 1"));
...

On the third line of the snippet, the leaf is added to the first child (a Composite instance) instead of to a variable holding the reference to the first child. As you will see, this approach requires no new variable for the composite name.

Safe Removal

In an earlier version of a Composite design I had made, I used something like the following to implement the remove() method:

function remove(IComponent $node)
{
    $nodeNow = array_search($node, $this->aChildren);
    unset($this->aChildren[$nodeNow]);
}

That works fine, but to my surprise, I found that others considered it “unsafe.” Specifically, the Freemens (Head First Design Patterns p367) noted that a Client might inadvertently try to add a composite element to a leaf node.

Chandima took that to heart, and designed a remove() method with a safety valve. So, despite testing the simpler code, I went ahead and followed Chandima’s lead and re-wrote his remove() method in PHP. To see the results, it’s easer to see it in the context of the Composite class where it is implemented: (By the way, the Freemans kept the less safe remove() version since it is probably a lot clearer.)

<?php
class Composite extends IComponent
{
    private $sName;
    private $aChildren;
 
    public function __construct($sNodeName)
    { 
        $this->sName=$sNodeName;
        $this->aChildren=array();
    }
 
    public function add(IComponent $comOn)
    {
        array_push($this->aChildren,$comOn);
        $comOn->setParent($this);
    }
 
    public function remove(IComponent $comGone)
    {
        if($comGone === $this)
        {
            for($countA = 0; $countA < count($this->aChildren); $countA++)
            {
                $this->safeRemove($this->aChildren[$countA]);
            }
            $this->aChildren= array();
            $this->removeParentRef();
        }
        else
        {
            for($countB = 0; $countB < count($this->aChildren); $countB++)
            {
                if($this->aChildren[$countB] == $comGone)
                {
                    $this->safeRemove($this->aChildren[$countB]);
                    array_splice($this->aChildren, $countB, 1);
                }
            }
        }    
    }
 
    private function safeRemove(IComponent $safeCom)
    {
        if($safeCom->getComposite())
        {
            $safeCom->remove($safeCom);
        }
        else
        {
            $safeCom->removeParentRef();
        }
    }
 
    protected function getComposite()
    {
        return $this;
    }
 
    public function getChild($int)
    {
        if(($int > 0) && ($int <= count($this->aChildren)))
        {
            return $this->aChildren[$int-1];
        }
        else
        {
            return null;
        }   
    }
 
    //Note: The following method is recursive
    public function operation()
    {
        echo "<!doctype html><html><head>";
        echo "<link rel='stylesheet' href='composite.css'>";
        echo "<meta charset='UTF-8'></head><body>";
        echo "<div class='composite'> $this->sName </div>";
        echo "</body></html>";
 
        foreach($this->aChildren as $elVal)
        {
            $elVal->operation();
        }
    }
}
?>

The remove() method itself is really three methods:

  • remove()
  • safeRemove()
  • getComposite()

Because the remove() method calls the private safeRemove(), and because the remove() method is not implemented on the Leaf in the same way, there’s not much of a chance of a Leaf trying to remove a parent node through the remove() method. Ironically, you can reference a leaf’s parent using the getParent() method, and then call for a remove of the parent. However, as you will see in the Client example, the getParent() is simply a way to address the parent of a node and call the whole composite from the node reference.

The Client Makes and Breaks Composites

Finally, we can look at the Client. In this implementation, the goal is to illustrate how nodes are manipulated and removed from composites. Treat hierarchical output samples as composites and nodes and look at how they are addressed and changed. (When looking at the Client, you might want to click the Play button and compare the output with the code in the Client.)

<?php
ERROR_REPORTING( E_ALL | E_STRICT );
ini_set("display_errors", 1);
function __autoload($class_name) 
{
    include $class_name . '.php';
}
class Client
{
    private $rootCompos;
    private $aloneLeaf;
 
    public function __construct()
    {
        //Create 3 Compositions using getChild()
        $this->rootCompos=new Composite("Root");       
        $this->rootCompos->add(new Composite("-Composite 1"));
        $this->rootCompos->getChild(1)->add(new Leaf("--C1:leaf 1"));
        $this->rootCompos->getChild(1)->add(new Leaf("--C1:leaf 2"));
 
        $this->rootCompos->add(new Composite("-Composite 2"));
        $this->rootCompos->getChild(2)->add(new Leaf("--C2:leaf 3"));
        $this->rootCompos->getChild(2)->add(new Leaf("--C2:leaf 4"));
        $this->rootCompos->getChild(2)->add(new Leaf("--C2:leaf 5"));
 
        $this->rootCompos->add(new Composite("-Composite 3"));
        $this->rootCompos->getChild(3)->add(new Leaf("--C3:leaf 6"));
        $this->rootCompos->getChild(3)->add(new Leaf("--C3:leaf 7"));
        $this->rootCompos->getChild(3)->add(new Leaf("--C3:leaf 8"));
 
        //Add a lone leaf to root composition
        $this->aloneLeaf=new Leaf("<br />--All alone");
        $this->rootCompos->add($this->aloneLeaf);
        //Show all components and leaves
        $this->rootCompos->operation();
 
        //Get this Parent of Leaf 5
        $leaf5=$this->rootCompos->getChild(2)->getChild(3);
        $leaf5parent=$leaf5->getParent();
        echo "<br />Leaf 5 parent operation below this line <br />";  
        //Run operation on Leaf5's Parent
        $leaf5parent->operation();
        echo "Leaf 5 parent operation above this line <br />";
 
        //Remove a Composite
        $this->rootCompos->remove($this->rootCompos->getChild(3));
        //Remove a single leaf
        echo "<br />Removed Composite 3, the first leaf from Composite 2 <br />";
        echo "and 'All alone leaf' <br />";
        //Method 1 using getChild()
        $com2=$this->rootCompos->getChild(2);
        $com2->remove($com2->getChild(1));
        //Method 2 using leaf name
        $this->rootCompos->remove($this->aloneLeaf);
        $this->rootCompos->operation();   
    }
}
$worker=new Client();
?>

In stepping through the Client’s actions you can see that only a single variable ($rootCopos) was declared for the three compositions. All of the others were created using the add() method and referenced using the getChild() method. A second variable ($aloneLeaf) creates a left node, but no other variables were necessary.

To demonstrate using the getParent() method, the program uses both $leaf5 and $leaf5parent, and the reference enable the operation() method to display just the second composite, which is the parent of leaf #5. Finally, examples of removing an entire composite and single nodes show the flexibility of the the design pattern. It shows both the new functionality of the implemented methods, remove() and getChild() and the added method getParent() (not a fundamental part of the design pattern but a nice-to-have added feature).

A Dynamic Application

In Part IV, the final part of the Composite design pattern series, I want to see if the Composite can be used in a PHP example that further reflects the example that the Gang of Four had in their original work. I’d like to see if we can use this pattern to create a dynamic drawing program or something else equally practical for use in PHP. If any readers have any ideas, please send them to the comments.

Share

Copyright © 2013 William Sanders. All Rights Reserved.

0 Responses to “The Composite Design Pattern in PHP: Part III Getting Child Elements and Removing Composites and Leaves”


  • No Comments

Leave a Reply