Practical PHP Refactoring: Self Encapsulate Field
Join the DZone community and get the full member experience.
Join For FreeWe are entering the data organization refactorings section of Fowler's book: these methods apply to object which maintain state, like entities, records, value objects and parameter objects. Sometimes these objects are persisted, while sometimes they remain transient: the refactoring techniques are equivalent for both cases.
We start with a simple refactoring, Self Encapsulate Field, which introduce a bit of indirection over private fields in a class. This refactoring leads to the usage of getters instead of private fields directly, even in the class's own code. Remember the bidirectionality of refactorings: this step can be performed in reverse for simplification.
Why another abstraction?
The reason to access fields indirectly is that subclasses are then allowed to override the getters. Getters and setters introduced by this refactoring do not need to be public (they should remain at least protected however.)
Remember why getters are introduced over public fields? Not for encapsulation (there is a 1-to-1 correspondence with fields), but to prevent modification and allow substitution with a calculated value in the future without affecting the client code.
In this case, the client code is the class itself: you prevent accidental modification, ensuring an immutable class; you also can substitute the body of getX() with a computed value in the future, or perform the substitution only in some subclasses.
Steps
- Introduce a getter (and an optional setter) methods, with protected or public visibility.
- Find references to the field, and replace them with calls to getX() or setX().
- Run tests at the unit level.
You can do a find/replace operation to ensure the field is not referenced anymore outside of the getter or setter, and the constructor. The constructor may in fact need to perform special initialization, or maybe the only place where to populate the field in absence of a setter.
Example
In the code sample, we start from a Paragraph object with a $class field containing the CSS class to display.
<?php class SelfEncapsulateField extends PHPUnit_Framework_TestCase { public function testParagraphIsDisplayed() { $p = new Paragraph('Lorem ipsum', 'important'); $expected = '<p class="important">Lorem ipsum</p>'; $this->assertEquals($expected, $p->__toString()); } } class Paragraph { private $text; private $class; public function __construct($text, $class) { $this->text = $text; $this->class = $class; } public function __toString() { return "<p class=\"$this->class\">$this->text</p>"; } }
A new requirement is introduced: a fixed class for some kind of paragraphs:
public function testParagraphIsDisplayedAsAlwaysImportant() { $this->markTestSkipped(); $p = new ImportantParagraph('Lorem ipsum'); $expected = '<p class="important">Lorem ipsum</p>'; $this->assertEquals($expected, $p->__toString()); }
The usage of the field in the class is encapsulated by a getter:
class Paragraph { private $text; private $class; public function __construct($text, $class) { $this->text = $text; $this->class = $class; } public function __toString() { return "<p class=\"" . $this->getClass() . "\">$this->text</p>"; } protected function getClass() { return $this->class; } }
And the subclass can finally be created:
class ImportantParagraph extends Paragraph { /** * This constructor is an hack, but at least the Liskov Substitution * Principle is respected in the external interface, which only * consists of __toString(). * If we want to improve this constructor, we can extract a common * abstract class between Paragraph and ImportantParagraph, so that * both can have their own constructors. */ public function __construct($text) { parent::__construct($text, null); } protected function getClass() { return 'important'; } }
Opinions expressed by DZone contributors are their own.
Comments