DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Refactoring: Replace Delegation with Inheritance

Practical PHP Refactoring: Replace Delegation with Inheritance

Giorgio Sironi user avatar by
Giorgio Sironi
·
Feb. 01, 12 · Interview
Like (0)
Save
Tweet
Share
6.54K Views

Join the DZone community and get the full member experience.

Join For Free

Delegation is a more flexible solution with respect to inheritance, because it allows to change collaborators by introducing new classes. However, it hides the public protocol of the collaborator into a private field.

If you find yourself writing many delegation methods, you may refactor to inheritance to simplify the code. The subclass will inherit everything automatically, which makes inheritance a powerful weapon.

Why moving towards inheritance?

Fowler cites the growth of delegation code as a force calling for this refactoring. Not all problems can be modelled accurately with delegation, although it is equivalent to inheritance. Depending on the domain and on the entity, you may prefer a Vehicle^-Coach inheritance relationship between classes (where Vehicle contains field like driver) or a Coach-*>Driver composition one.

There are other reasons to choose one over the other, like mutable state in the collaborator. In the inheritance case it cannot be shared between objects, while in the delegation one it can.

A first corollary is that if you want to avoid aliasing and one object changing the state of the other, inheritance will stop this from happening for sure.

A second corollary is that if you want to share date between objects this way instead, you cannot use inheritance.

A warning: all the public methods will be inherited. If you do not use all of them, you're violating LSP by refactoring to inheritance, overusing this construct. Moreover, inheritance is a one-shot strategy: you can use it only once for a class, and afterwards other responsibilities must consider only composition.

Steps

The steps perform the inverse routine of Replace Inheritance With Delegation.

  1. Make the client object extend the delegate class.
  2. The delegate should become $this, by initialization in the client constructor.
  3. Remove delegation: methods should now be inherited; change one method at a time if you're unsure of the effects.
  4. Remove the delegate field and clean up unnecessary indirections.

Example

We start from the end of the previous example, but it has brought us into a ne wdirection. Most of the state has been pushed into Format, due to the desire to eliminate the duplication of fields into the subclasses (text/content, author). Incidentally, it is not a format anymore, but a name like FeedItem starts to pop up...
__toString() is an example of a method directly delegated to the format object, but there could (and will) be many more in real code.

<?php
class ReplaceDelegationWithInheritance extends PHPUnit_Framework_TestCase
{
    public function testAPostShowsItsAuthor()
    {
        $post = new Post("Hello, world!", "giorgiosironi");
        $this->assertEquals("Hello, world! -- giorgiosironi",
                            $post->__toString());
    }

    public function testALinkShowsItsAuthor()
    {
        $link = new Link("http://en.wikipedia.com", "giorgiosironi");
        $this->assertEquals("<a href=\"http://en.wikipedia.com\">http://en.wikipedia.com</a> -- giorgiosironi",
                            $link->__toString());
    }
}

class TextSignedByAuthorFormat
{
    private $text;
    private $author;

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

    public function __toString()
    {
        return "$this->text -- $this->author";
    }
}

class Post
{
    private $format;

    public function __construct($text, $author)
    {
        $this->format = new TextSignedByAuthorFormat($text, $author);
    }

    public function __toString()
    {
        return $this->format->__toString();
    }
}

class Link
{
    private $format;

    public function __construct($url, $author)
    {
        $this->format = new TextSignedByAuthorFormat($this->displayedText($url), $author);
    }

    protected function displayedText($url)
    {
        return "<a href=\"$url\">$url</a>";
    }

    public function __toString()
    {
        return $this->format->__toString();
    }
}

While extending the delegate class, we prove it's compatible with the future subclass:

class TextSignedByAuthorFormat
{
    private $text;
    private $author;

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

    public function __toString()
    {
        return "$this->text -- $this->author";
    }
}

class Post extends TextSignedByAuthorFormat
{
    private $format;

    public function __construct($text, $author)
    {
        $this->format = new TextSignedByAuthorFormat($text, $author);
    }

    public function __toString()
    {
        return $this->format->__toString();
    }
}

class Link extends TextSignedByAuthorFormat
{
    private $format;

    public function __construct($url, $author)
    {
        $this->format = new TextSignedByAuthorFormat($this->displayedText($url), $author);
    }

    protected function displayedText($url)
    {
        return "<a href=\"$url\">$url</a>";
    }

    public function __toString()
    {
        return $this->format->__toString();
    }
}

We modify  __construct() and remove the delegation in the methods. If we don't, a call will recur indefinitely as $this->method() will call $this->format->method(), which is actually $this->method(), and start again the cycle. Xdebug will discover these problems and block the script at a maximum level of nesting.

class Post extends TextSignedByAuthorFormat
{
    private $format;

    public function __construct($text, $author)
    {
        parent::__construct($text, $author);
    }
}

class Link extends TextSignedByAuthorFormat
{
    private $format;

    public function __construct($url, $author)
    {
        parent::__construct($this->displayedText($url), $author);
    }

    protected function displayedText($url)
    {
        return "<a href=\"$url\">$url</a>";
    }
}

We can now simplify, by removing the delegate field, renaming the superclass, and deleting constructors which just delegate. Again, delegating methods had to be removed in the previous step unless they substitute $this->format-> with parent::, which delegating methods never use instead.

class FeedItem
{
    private $text;
    private $author;

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

    public function __toString()
    {
        return "$this->text -- $this->author";
    }
}

class Post extends FeedItem {}

class Link extends FeedItem
{
    public function __construct($url, $author)
    {
        parent::__construct($this->displayedText($url), $author);
    }

    protected function displayedText($url)
    {
        return "<a href=\"$url\">$url</a>";
    }
}
Inheritance (object-oriented programming) Delegation (object-oriented programming) PHP

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • What To Know Before Implementing IIoT
  • DevOps vs Agile: Which Approach Will Win the Battle for Efficiency?
  • Unlocking the Power of Elasticsearch: A Comprehensive Guide to Complex Search Use Cases
  • What Is the Temporal Dead Zone In JavaScript?

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: