Introduction
1) MVC
2) Front Controller
3) Factory
4) Singleton
5) Registry
6) Prototype
7) Object Pool
8) Iterator
9) Lazy Loading
10) Service Locator
11) Module
12) Observer
Conclusion
Magento its architecture is sometimes
deemed overly engineered. If we look at it from a helicopter view, commonly
used design patterns are easily spotted.
A software design pattern is a reusable solution to an often occurring problem. This
doesn’t mean that software is better if it has more design patterns. Instead, a
good software engineer should be able to spot the problem and implement the
pattern instead of introducing implementations without purpose. The earlier
behavior is leadingly noticeable in Magento, where most if not all design
pattern-implementations have a purpose.
Magento represents a PHP system that is
built on well thought-out coding principles - reusable design patterns being
one of them. In terms of an example of a PHP system, I think it can be
considered pretty cutting edge and therefore worth considering from an
architectural point of view.
As I understand it, there are many
design patterns that are available to the OOP developer. Seeing such patterns
being put to use in an open-source system such as Magento allows a developer to
view examples of such patterns in real use and in situ, rather than in examples
that can sometimes be rather achedemic, and even a little misleading.
As such, I am wondering what patterns,
other than the ones I have listed below, Magento programmers have used when
developing for Magento.
As a note, I understand that some of
these patterns are in place as a consequence of being built on the Zend
Framework, MVC / Front Controller being a couple of them,
Magento
utilizes a unique MVC pattern, utilizing a DOM based configuration layer. It
leverages xml to drive the configuration and actions of the application on top
of the regular Model-View-Controller architecture.
Model
View Controller, MVC for short, is a design pattern where business,
presentation and coupling logic are separated. Magento heavily utilizes XML as
templating-logic and HTML mixed with PHP files for its views. Models are backed
by Varien’s ORM. Most business logic happens in the models whereas the
controllers map the model-data to the views.
Because
Magento its views are “fat” – they often contain a lot of logic – it’s not rare
that views have an additional PHP class (the Block system) which will help with
rendering.
The
front controller pattern makes sure that there is one and only one point of
entry. All requests are investigated, routed to the designated controller and
then processed accordingly to the specification. The front controller is
responsible of initializing the environment and routing requests to designated
controllers.
Magento
has only one point of entry (index.php) which will initialize the application
environment (Mage::app()) and route the request to the correct controller.
Let's
see how it all works.
Magento
leverages the class Mage_Core_Controller_Front_Action. A module's controllers
usually extend this base class.
Magento
handles requests through URLS in the browser. The front controller helps to
process these requests.
A
url is broken up into multiple sections. A basic url has the following
elements:
- A router
- A controller
- An action
A
router is defined in your Magento
module configuration xml. This is an alias for your module. It is the first
section after your base URL.
A
controller is a class used to help
process your request. It is the second section after your base URL.
An
action is the actual method called
within your defined class. It is the third section after your base URL.
Here
is a basic example of Magento's Front Controller process.
http://www.mymagentosite.com/customer/account/login/
http://www.mymagentosite.com/
- Base URL
/customer/
- Router for the Customer module
/account/ - Account controller class. This is
located in the controllers folder of the Customer module and is the file
AccountController.php and calls the class Mage_Customer_AccountController.
/login/ - The actual method called within Mage_Customer_AccountController.
Whenever a method is called, standard Magento practice is to append the word
'Action' at the end, to differentiate between methods that parse files. The
method called here is loginAction().
Anything
after the standard router/controller/method call is parsed any additional
parameters are parsed as key=>value pairs to be handled with the request.
The
Factory Method is used to instantiate classes in Magento. You instantiate a
class in Magento by calling an appropriate method passing an abstract name
representing a class group followed by a class name. Class groups and their
appropriate abstractions are declared in your configuration XML files in your
module's /etc/ folder.
$product
= Mage::getModel('catalog/product');
Another
way to retrieve an instance of a class, is to call Mage::getSingleton(). It accepts a class alias and before returning
an instance, it checks the internal registry whether this class has already
been instantiated before – this results in a shared instance.
Much
like factory class abstraction and class groups in Magento, the Singleton
pattern is instantiated for Blocks and Classes just the same.
An
example of where this is mandatory is the session storage which should be
shared through the code base instead of creating it anew every time.
$category
= Mage::getSingleton('catalog/session');
The
registry pattern is basically a pattern that allows any object or data to be
available in a public global scope for any resource to use.
The
registry is often used for transferring data between scopes when they cannot be
passed on, otherwise.
This
is especially helpful transferring data between Models and Blocks without
having to instantiate an entire class and load data.
You
can register an object or data with:
Mage::register('identifier',
$object_or_data);
After
it is registered, you can call it with:
Mage::registry('identifier');
You
can also unregister an object at any time with:
Mage::unregister('identifier');
For
example,
$currentCategory
= Mage::registry('current_category');
The
Prototype pattern in Magento is used as an extension of the Abstract Factory
pattern. It ensures that an appropriate subclass is instantiated via
appropriate types that are assigned to an object. What does this mean?
Basically, it means that whenever you need to get a specific class that is
defined via its parent type, the prototype pattern ensures you get the right
class that can handle what you need.
A
notable example is the Mage_Catalog_Model_Product class which has a
getTypeInstance method to retrieve the specific Mage_Catalog_Model_Product_Type
with a specific subset of methods and properties not applicable to all
products.
For
example, the Mage_Downloadable_Model_Product_Type ultimately extends the
Mage_Catalog_Model_Product_Type. If you are iterating over an order and want to
call a specific method of a downloadable product, you will need to factorize it
first with the getTypeInstance method of the original product.
Mage:getModel('catalog/product')->getTypeInstance();
The
object pool pattern is simply a box with objects so that they do not have to be
allocated and destroyed over and over again. It is a great way to save on
memory consumption and compute cycles.
It’s
not used a lot in Magento other than for heavy tasks where resources can get
limited soon, like importing products. The object pool (managed by
Varien_Object_Cache) can be accessed with Mage::objects().
$id
= Mage::objects()->save($object);
$object
= Mage::objects($id);
The
iterator pattern defines that there is a shared way to iterate over a container
with objects. In Magento, this is handled by the Varien_Data_Collection which
on its turn uses various baked-in PHP classes (like ArrayIterator) for having a
more OO-interface to arrays. This ensures that model-collections will always
have a common API to iterate over without being dependent of the actual models.
The
Iterator Pattern is a design pattern that allows an object traverse through the
elements of another class. This allows you to specify an iterator and allow for
multiple different sets of data to be passed without changing the underlying
structure that allows the iteration.
Mage::getModel('catalog/product')->getCollection();
Lazy
Loading is a design pattern that delays the loading of an object until the time
that the object is called upon. This results in less resources being used. With
Magento, they don't utilize this with objects, but data.
One
of the lazy loading behaviors of Magento is that of collections.
If
you were to retrieve a collection of products with Mage::getModel('catalog/product')->getCollection(), the database will only be touched when
you actually access the collection by, for example, iterating over it or
retrieving the count of models found.
Lazy
Loading, which means that database access only occurs when strictly necessary.
For example:
$productCollection
= Mage::getModel('catalog/product')->getCollection();
$productCollection->addFieldToFilter('sku','n2610');
The
database query will not be made until you attempt to access an item in the
Collection.
The
service locator is a design pattern that allows a user to get a service by
encapsulating the process inside an abstraction layer. This allows the user to retrieve the
appropriate or best service without knowing what that service is at runtime.
Allows
overrides or renamed physical resources (e.g. Classes, DB tables, etc)
Mage::getModel('catalog/product')
and
$installer->getTable('customer/address_entity')
The
Module Design Pattern is a form of modular programming that emphasizes the
grouping of functionality of a program into independent, interchangeable
modules.
Anyone
familiar with Magento development has stumbled upon the module pattern. It
basically defines that different domains are grouped into separate modules
which function independent of each other and can be plugged-in to the main
system as deemed appropriate. In an ideal situation, an implementation of the
module pattern would make sure that each element can be removed or swapped. One
of the protagonists of the module pattern in PHP is the Composer package
manager.
Though
Magento heavily relies on a modular architecture, it’s not modular to the bone.
Certain functionality is heavily tied to the core and cannot be easily changed.
There is also the heavy usage of the super-global Mage core-class which
introduces all sorts of system-wide dependencies not easily overseen.
The
observer pattern is where an event listener is set at a certain point during an
application's execution. Other
components of the application can "hook" into this event listener and
execute their code during this point.
Magento
its event-driven architecture is a result of an implementation of the observer
pattern. By defining observers (or listeners), extra code can be hooked which
will be called upon as the observed event fires. Magento uses its XML-data
storage to define observers. If an event is fired with Mage::dispatchEvent($eventName, $data), the data storage will be consulted and
the appropriate observers for $event will be fired.
#
PHP
Mage::dispatchEvent('event_name', array('key'=>$value));
#
config.xml
<config>
<global>
<events>
<event_name>
<observers>
<unique_name>
<class>Class_Name</class>
<method>methodName</method>
</unique_name>
</observers>
</event_name>
</events>
</global>
</config>
Hopefully
these 12 design patterns, give you a bit better understanding of the
architectural decisions made in Magento. Not all design patterns found in
Magento are implemented as defined by the original concepts. This is perfectly
fine, because design patterns are there to help with solving common problems as
seen fit, instead of deploying exact implementations.
Let me know in the comments if this has helped you........... Enjoy........Happy coding :)...