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
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Refactoring: Form Template Method

Practical PHP Refactoring: Form Template Method

Giorgio Sironi user avatar by
Giorgio Sironi
·
Jan. 25, 12 · Interview
Like (0)
Save
Tweet
Share
8.68K Views

Join the DZone community and get the full member experience.

Join For Free

Duplication is not always expressed as an identical block of code: often it is subtler to discover, because it exists at an higher level of abstraction.

Consider a sorting algorithm, a classic example in computer science: it can be implemented on widely different data structures, as long as their element can be compared and swapped. If we compare quicksort implemented on an array of integers or on a SplLinkedList containing objects, we see the same algorithm.

Form Template Method is about producing common blocks of code by extracting different blocks into methods with the same signature; in our example, the compare and swap operations of a collection-like objects. The common blocks of code can then be pulled up in a subclass.

Why a Template Method?

Eliminating duplication goes beyond targeting the immediately visible cases. The particular case we are tackling with this refactoring is that of methods performing the same higher-level steps, but with some differences in a few lines.

The result of the refactoring is that common code is moved in the superclass, as a Template Method; the implementation of the steps is left to the subclasses as hook methods. They are usually defined as abstract in the superclass, which knows of their existence but not about their implementations. In PHP it's not strictly necessary to define abstract hook methods due to the lack of static checks, but it is desirable since it catches a potential fatal error at loading time instead of at runtime.

Template Method is based on inheritance, so the client code won't have to change. This refactoring is an application of the Open/Closed Principle: it will be easy to add new versions of the algorithm starting from the Template Method, by simply adding a new subclass.

Steps

  1. Decompose the similar methods: extract only the different small blocks of code into methods with the same signature. Fowler suggests that after this step each pair of analogue methods in the subclasses should be either identical (Template Method) or completely different (hook methods).
  2. If there are methods different just for the name of the method calls they make, rename the different hook methods they call with the same name and signature.
  3. Use Extract Superclass if needed, and Pull Up Method to move the identical methods up in it.
  4. Define signatures of the hook methods as abstract, to ensure they are defined in each subclass.

Example

In the initial state, two classes VideoTweet and ArticleTweet are representing the text for a tweet linking to a video, or an article on DZone.

<?php
class FormTemplateMethod extends PHPUnit_Framework_TestCase
{
    public function testAVideoTweet()
    {
        $link = new VideoTweet('http://www.youtube.com/watch?...', "Lolcats");
        $this->assertEquals('Check out this video: Lolcats http://www.youtube.com/watch?...',
                            $link->__toString());
    }

    public function testAnArticleTweet()
    {
        $link = new ArticleTweet('http://css.dzone.com/category/tags/practical-php-refactoring', "Practical PHP Refactoring");
        $this->assertEquals('RT @DZone: Practical PHP Refactoring http://css.dzone.com/category/tags/practical-php-refactoring',
                            $link->__toString());
    }
}

class VideoTweet
{
    private $url;
    private $title;

    public function __construct($url, $title)
    {
        $this->url = $url;
        $this->title = $title;
    }

    public function __toString()
    {
        return "Check out this video: $this->title $this->url";
    }
}

class ArticleTweet
{
    private $url;
    private $title;

    public function __construct($url, $title)
    {
        $this->url = $url;
        $this->title = $title;
    }

    public function __toString()
    {
        return "RT @DZone: $this->title $this->url";
    }
}

A preliminary step is to extract the common parts into a superclass with Extract Superclass, Pull Up Field and Pull Up Constructor Body.

class Tweet
{
    protected $url;
    protected $title;

    public function __construct($url, $title)
    {
        $this->url = $url;
        $this->title = $title;
    }
}

class VideoTweet extends Tweet
{
    public function __toString()
    {
        return "Check out this video: $this->title $this->url";
    }
}

class ArticleTweet extends Tweet
{
    public function __toString()
    {
        return "RT @DZone: $this->title $this->url";
    }
}

Now we begin this refactoring. __toString() will become our Template Method, and we have to extract the different bits into methods with a common signature.
These hook methods are different, while __toString() is now the same for all (two) subclasses:

class VideoTweet extends Tweet
{
    public function __toString()
    {
        return $this->prefix() . ": $this->title $this->url";
    }

    protected function prefix()
    {
        return "Check out this video";
    }
}

class ArticleTweet extends Tweet
{
    public function __toString()
    {
        return $this->prefix() . ": $this->title $this->url";
    }

    protected function prefix()
    {
        return "RT @DZone";
    }
}

We pull up __toString() to eliminate its duplication:

class Tweet
{
    protected $url;
    protected $title;

    public function __construct($url, $title)
    {
        $this->url = $url;
        $this->title = $title;
    }

    public function __toString()
    {
        return $this->prefix() . ": $this->title $this->url";
    }
}

class VideoTweet extends Tweet
{
    protected function prefix()
    {
        return "Check out this video";
    }
}

class ArticleTweet extends Tweet
{
    protected function prefix()
    {
        return "RT @DZone";
    }
}

We also define the hook methods as abstract: new classes will have to ensure their presence before they can be instantiated at all. It's also easier to define new type of tweets, since they just take a little prefix() method instead of new copies of __construct() and __toString().

abstract class Tweet
{
    protected $url;
    protected $title;

    public function __construct($url, $title)
    {
        $this->url = $url;
        $this->title = $title;
    }

    public function __toString()
    {
        return $this->prefix() . ": $this->title $this->url";
    }

    /**
     * @return string
     */
    protected abstract function prefix();
}
PHP Template Form (document)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 11 Observability Tools You Should Know
  • Best CI/CD Tools for DevOps: A Review of the Top 10
  • Application Architecture Design Principles
  • Master Spring Boot 3 With GraalVM Native Image

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: