Initial Situation

You have one class doing work that should be done by two.

Goal

Create a new class and move the relevant fields and methods from the old class into the new class.

Improves

Complexity   

Difficulty

easy

Pros/Cons

Crisp class with clear responsibilities
Easier to understand class
May help reduce duplicate code
Complexifies a design when the class is rarely used and changed infrequently
Setup Continuous Code Quality Management in minutes.

Refactoring Basics

Extract Class

Overview

You might have heard that classes should have a single responsibility. Even if you try to follow this dogma, in practice classes grow. Some operations are, some more data is added, and sooner or later your class has more than one reponsibility.

The Extract Class refactoring is best applied if there are logical units of data which are grouped together, or operations which are just performed on a subset of the data.

Checklist

Step 1
Determine what to extract
Find logical units of data which are grouped together or operations which are performed on a subset of the data.
Step 2
Create a New Class
Create a new class with the extracted operations and establish a link from the old to new class. This link might be bidirectional.
Step 3
Rename the Old Class
If the name of the old class is not fitting anymore, consider renaming it.
Step 4
Review the interfaces and links of each class
The goal should be minimally coupled classes. Examine both classes to see if you can replace a bidirectional relationship with a unidirectional relationship.
Step 5
Value object vs. reference object
Decide whether you would like to expose the new object as a value object (immutable), or a reference object (may be modified).

Example: Moving Related Fields to Value Object

Step 1: Determine what to extract

Let’s assume the following class:

class BlogPost
{
    // ...
    private $previewTextRst;
    private $previewTextHtml;
    private $textRst;
    private $textHtml;

    // ...

    public function setPreviewText($rst, $html)
    {
        $this->previewTextRst = $rst;
        $this->previewTextHtml = $html;
    }

    public function setText($rst, $html)
    {
        $this->textRst = $rst;
        $this->textHtml = $html;
    }

    // ...
}

As you can see, we have two sets of fields which are grouped together and some operations which only perform work on a specific subset of fields. In this example, it is very obvious since we also have some duplication which we can reduce by extracting a class.

Step 2: Extracting Class

We will continue by introducing a new Text class and move the textRst and textHtml fields to it:

class Text
{
    private $rst;
    private $html;

    public function __construct($rst, $html)
    {
        $this->rst = $rst;
        $this->html = $html;
    }

    public function getRst()
    {
        return $this->rst;
    }

    public function getHtml()
    {
        return $this->html;
    }
}

class BlogPost
{
    private $previewText;
    private $text;

    public function setPreviewText($rst, $html)
    {
        $this->previewText = new Text($rst, $html);
    }

    public function setText($rst, $html)
    {
        $this->text = new Text($rst, $html);
    }
}

Step 3: Rename old class

In our case, the name of the old class is still fitting, we do not need to rename it. So, we will just skip this step.

Step 5: Value Object vs. Reference Object

In general, prefer to use a value object as this limits the scope of how and when the object can be modified. In our case, we decided to use a value object: If the text is changed, a new object must be created. This way we ensure that any modification must go through the owning class, in our case BlogPost.

How to use the Checklist?

Checklists present a proven order of necessary refactoring steps and are especially helpful for beginners.

If you feel comfortable with a refactoring, there is no need to follow each of the steps literally; you can often perform multiple steps at once or skip some.

We suggest to try the recommended order at least once; this will often provide new insights - even for seasoned developers.