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: Pull Up Field

Practical PHP Refactoring: Pull Up Field

Giorgio Sironi user avatar by
Giorgio Sironi
·
Dec. 26, 11 · Interview
Like (0)
Save
Tweet
Share
9.50K Views

Join the DZone community and get the full member experience.

Join For Free

We now enter in the territory of generalizations. It's a natural process, as we add more and more tests, for our code to become more general; but after we have made our test pass, the refactoring phase include many ways to deal with generalization.

Refactoring for generalization is meant to solve the problem of duplication: most of these refactoring start from a situation where code (or, to say it better, knowledge) is duplicated in multiple points and end up eliminating it.

I don't have to remind you that code duplication is universally recognized as cancer because the multiple places where a concept (or a rule, a strategy, a requirement) is expressed can easily get out of sync: one of the entities may be updated while the other is not.

The first generalization refactoring we encounter is Pull Up Field: it targets a scenario where multiple subclasses have the same field, or a very similar field that with a name change can become identical to the other copies. The solution proposed by this refactoring (by no means the unique one) is to move up the field into the hierarchy, so that the it is inherited by the subclasses.

Why moving up in the hierarchy?

From the external point of view, nothing will change: method calls will be exactly the same, but the classes will be easier to maintain as only one field definition has to change. There is less code to move, delete and update.

If you apply TDD and the 4 rules of simple design, you may start from a solution containing duplication, and even copy and paste code sometimes (with the idea that most of it will change in the new class or method). But before your next commit, you will have to reduce the duplication as much as possible.

This refactoring allows also to subsequently move methods using the common field: it is in fact a prerequisite for some instances of Pull Up Method.

Steps

  1. Inspect the field's usage: if you keep fields private or protected, you know you will have a limited set of source files where to look and execute a find operation. All operations performed with the field value, or on the field value, should be regarded.
  2. In case it's required, rename the fields to the same identifier (that's the old school name for names referencing to variables and fields).
  3. Create a field in the superclass, which for now will be overridden by the subclasses fields.
  4. Delete the subclass fields.

Note that if the pulled up field maintains the original name, between steps 3 and 4 you may get failing tests if their scope was originally private. It is possible to redefine fields in PHP, but not with stricter visibility. The tests however, won't change at all in any step.

Fowler notes that field may undergo a change in scope during the refactoring: the field must become protected if it was private. This is part of the price to pay for using inheritance.

Example

In the initial state, the author field is duplicated in the two subclasses. We suppose a common superclass already exists, and we will focus on eliminating only the duplication originating from the field definition itself, not from the code that references it. In both cases, other refactorings will target those needs.

<?php
class PullUpField 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());
    }
}

abstract class NewsFeedItem
{
}

class Post extends NewsFeedItem
{
    private $text;
    private $author;

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

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

class Link extends NewsFeedItem
{
    private $url;
    private $author;

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

    public function __toString()
    {
        return "<a href=\"$this->url\">$this->url</a> -- $this->author";
    }
}

Since the fields already have the same name, we don't have to modify them. We just add a field in the protected scope of the superclass. We can specify a common documentation for all the classes that inherit from it:

abstract class NewsFeedItem
{
    /**
     * @var string  references the author's Twitter username
     */
    protected $author;
}

To make the tests pass again, we should eliminate the fields from the subclasses:

class Post extends NewsFeedItem
{
    private $text;

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

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

class Link extends NewsFeedItem
{
    private $url;

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

    public function __toString()
    {
        return "<a href=\"$this->url\">$this->url</a> -- $this->author";
    }
}

There are no duplicated fields now, so this refactoring stops here. In the next episodes of this series we will remove even more duplication (but not necessarily get fewer lines of code).

PHP

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 7 Most Sought-After Front-End Frameworks for Web Developers
  • Using GPT-3 in Our Applications
  • Build an Automated Testing Pipeline With GitLab CI/CD and Selenium Grid
  • Practical Example of Using CSS Layer

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: