Refactoring the Front Controller of the Zend Framework
One of the most fundamental decision in object design is deciding where to put responsibilities. No one, and I mean no one, gets it right the first time. That’s why refactoring is so important. As Kent Beck puts it, refactoring is the process of taking a system and adding to its value, not by changing its behaviour but by giving it more of these qualities that enable us to continue developing at speed.
Extract Class Refactoring
You’ve probably heard that a class should handle a few clear responsibilities. In practice, classes grow. You add some operations here, a bit of data there. You add a responsibility to a class feeling that it’s not worth a separate class, but as that responsibility grows and breeds, the class becomes too complicated. Extract Class is a common technique for moving features between objects.
Extract Class:
You have one class doing work that should be done by two.
Zend Framework
Zend Framework organizes code in a project structure and puts the project files into different directory structures:
- MVC directory structure
- Modular directory structure
The framework allows users to choose between one or the other. My goal is to move this responsibility away from the Zend_Controller_Front class. To do this I need to use the Extract Class to alter the internal structure of the class without changing its external behaviour. This refactoring will reduce the complexity of the Front Controller and increase its flexibility. Also, it will allow users to define custom directory names and paths.
First, I need to identify the methods I want to extract from the Zend_Controller_Front class:
addControllerDirectory() setControllerDirectory() getControllerDirectory() removeControllerDirectory() addModuleDirectory() setModuleControllerDirectoryName() getModuleControllerDirectoryName()
Then, I need to create a set of classes to express the split-off responsibilities:
Zend_Controller_Directory_Abstract Zend_Controller_Directory_Exception Zend_Controller_Directory_Application Zend_Controller_Directory_Module
And finally, I need to use Move Field and Move Method to move fields and methods over from Zend_Controller_Front to the new classes.
abstract class Zend_Controller_Directory_Abstract
{
public function setControllerDirectoryName()
public function getControllerDirectoryName()
public function setControllerDirectory()
public function getControllerDirectory()
public function addControllerDirectory()
public function removeControllerDirectory()
}
class Zend_Controller_Directory_Application
extends Zend_Controller_Directory_Abstract
{
public function addApplicationDirectory()
public function setApplicationDirectory()
public function getApplicationDirectory()
}
class Zend_Controller_Directory_Module
extends Zend_Controller_Directory_Abstract
{
public function addModuleDirectory()
public function setModuleDirectory()
public function getModuleDirectory()
}
All I need to do now is add a setter and getter method to the Zend_Controller_Front class and inject the object. For the sake of this example, I’ll assume that I can add additional methods to the Zend_Controller_Front class.
class Zend_Controller_Front
{
public function setDirectory(Zend_Controller_Directory_Abstract $directory)
{
$dirs = $directory->getControllerDirectory();
foreach ($dirs as $module => $dir) {
$this->getDispatcher()->addControllerDirectory($dir, $module);
}
$this->_directory = $directory;
}
public function getDirectory()
{
return $this->_directory;
}
}
Before:
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->addControllerDirectory('../application');
$frontController->setModuleControllerDirectoryName('controllers');
After:
$directory = new Zend_Controller_Directory_Application();
$directory->setControllerDirectoryName('controllers');
$directory->addApplicationDirectory('../application');
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setDirectory($directory);
That’s it. I’ve demonstrated how to refactor the Front Controller of the Zend Framework using a refactoring known as “Extract Class”. Martin Fowler discusses this technique in “Refactoring: Improving the Design of Existing Code“.
[...] Refactoring the Front Controller of the Zend Framework [...]
DUODRACO
August 20, 2008 at 11:54 pm
Interesting demonstration, except IMHO the implementation of Zend_Controller_Front::setDirectory(). That method needs to list each variation of Zend_Controller_Directory concrete instance. If someone want to provide his own concrete class, he has to alter the controller’s setDirectory() code. An alternative solution would consist in passing the front controller instance to the Zend_Controller_Directory object. Then that object configures the dispatcher itself. No more hard coded types in the controller’s setDirectory() method.
quode
August 22, 2008 at 4:37 am
You are right, the getControllerDirectory() method returns all the paths, so there’s no need to check the instance of the object. I’ve updated the example.
Federico
August 22, 2008 at 1:22 pm
will you send this to the zf list? or do you have your own framework forked?
I have seen many things you’ve done ‘improving’ the framework, but it wouls be sad if they are not merged to zf
Stuado -StR- Rodríguez
August 23, 2008 at 12:47 pm
Yes, I created a proposal the other day: Zend_Controller_Directory. The implementation is different, but the idea is the same.
Federico
August 23, 2008 at 6:49 pm
[...] http://phpimpact.wordpress.com/…e-zend-framework/ [...]
Mrasnika’s Lair
August 23, 2008 at 6:57 pm
[...] Refactoring the Front Controller of the Zend Framework [...]
Zend Framework Controller: 22% Drop in Responsiveness
September 16, 2008 at 11:12 pm
[...] Refactoring the Front Controller of the Zend Framework [...]
Implementing your own Front Controller in Zend Framework « fede.carg ( blog )
April 5, 2009 at 12:12 pm