Everyone has their own set of tools. As a PHP developer, one of my favorites is a Rapid Application Development framework called “Nooku”. In the words of the development group: “Nooku is more of a web development toolkit than a framework”.
In case you are not familiar with it, have a look. It’s an open source project that makes heavy use of industry accepted design patterns to produce highly componentized applications that are easily extensible and reusable (initially created by one of the lead Joomla! developers). Out of the box, Nooku gives you a great deal of rapid application development tools to help get projects off the ground faster. A small, but strong sample:
- A default implementation of MVC where all you need to do is write the layout (this is what hooked me)
- HMVC availability right away
- Support for different output formats like JSON and XML for all your data (i.e., expose your API in minutes)
- Default administrative and front-end implementations
At the heart of Nooku is the “Composition over Inheritance” design principle (in fact, it’s the first concept on the Nooku introductory page. In one line: you should aim to compose (or add up) the functionality of multiple objects to create some sort of composite object, rather than relying on subclassing.
This principle lets you write less code and often leads to some pretty elegant solutions. So how exactly is it promoted? Well, at the code level, the best examples come through the use of Mixins and Resource/Service Identifiers. Lets take a look.
Before PHP 5.4, the language had no concept of Traits. These are class-like structures that, when ‘used’ by an object, provide some type of functionality (similar to Multiple Inheritance). Nooku has been solving this problem for years (since PHP 5.2) with the Mixin.
The Mixin not only lets you compose objects together, but it also adds each mixed object’s methods to the composite object’s interface. The object using the mixin seems to ‘inherit’ the mixed in object’s methods.
Almost all objects in Nooku have this ability because they extend the base class KObject that defined the mixin method.
The major classes in Nooku’s controller architecture are also descended from KObject. The abstract controller is the KControllerAbstract class and by inspection you can see that it takes advantage of the Mixing ability right away. Whenever an instance of this class is constructed, the KMixinCommand and KMixinBehavior functionality is immediately added to its interface. Consequently, each controller in Nooku is composed with Command Chain and Behavior management functionality through the respective objects.
Going back to the Nooku controller: the KMixinBehavior class holds all the pieces to give KControllerAbstract the ability to load specific Behaviors at runtime. Behavioral strategies are classes that describe a process or logic that can be separated out and used by other classes (e.g., editable, orderable). KMixinBehavior is fairly simple and has only four methods: getBehavior, hasBehavior, addBehavior and getBehaviors. And thats all we need to give an object the ability to handle and encapsulate different behavioral strategies.
Similarly, KMixinCommand has only three methods: getCommandContext, getCommandChain, setCommandChain. If you hadn’t guessed, these three methods provide KControllerAbstract with the ability to implement a command chain, but lets it do so at runtime.
By definition Abstract classes are meant to be extended and so by the magic of inheritance all objects that are children or instances of KControllerAbstract also gain the ability to add behaviors and a command chain at runtime.
Sounds cool. But what does that actually mean? In short, Nooku provides componentized functionality; that is, Nooku allows you to modularize your functionality and compose functionalities across modules at runtime.
These two examples serve to demonstrate composition. They also serve to demonstrate the Nooku RAD framework’s support for further composition at its core. This is an important advantage. The methods added to KControllerAbstract above support the “Strategy Design Pattern” by giving developers the tools to encapsulate what varies before one line of code has been written. The fact that the mixin() method is part of every extension of KObject means that you can readily define and add other behavioral management interfaces to most objects at runtime.
The Service and Resource Identifiers and Locators in Nooku also provide powerful support for the separation of concerns.
Again, lets look again at KObject, but also KService. We can treat most things in Nooku as a service or a resource, and as such instantiate and interrogate them in exactly the same way.
- looked at specifically (Read) (e.g., looking at a can of tomato soup)
- moved around the store (Edited) (e.g., move the soup to the produce aisle)
- added to or removed from the store’s inventory (Add and Delete) (e.g., add a new kind of soup and get rid of the tomato)
Taking this example even further, imagine the grocery store has a franchise department and you want to be in business. In that situation, the Service is the franchise department and the resource is the grocery store you buy. It is very much a contextual classification. As a whole, this is known as the BREAD action pattern (You’ll see each of these represented between KControllerService and KControllerResource prepended with ‘_action’, i.e _actionRead()).
A model can be a service, a table object can be thought of as a service, a specific MVC triad is instantiated as a resource or service, while a specific record resulting from interrogating the service can be thought of as a resource.
Every object in Nooku is a composition of objects in that every one of them contains a reference to the entire application’s instantiated services in a ‘service container’ and a method to access the services called getService(). All that’s required by the KObject::getService() method is that we pass a valid resource identifier and it will return an instantiated service ready for use Toptal Web Engineers Community.
In PHP rapid application development, Resource Identifiers give us a powerful way to decouple the instantiation of an object from it’s class name, and thus provide aliases for that identification. This has important implications on the maintainability of an application. Through aliasing a developer can change the class used by every object which is instantiated with a given identifier by adding one line of code with KService::addAlias().