OO Design in Kohana - Adapter Pattern

Published on:
Tags: Kohana

OO Design in Kohana Thought I would do a couple of posts on some basic Object Oriented design patterns and examples of their implementation within the Kohana framework. They’re not going to go into any great detail but will hopefully provide a small insight into some of the inner workings of Kohana and enable  you to make full use of some of the inherent power these patterns provide.

This article covers the adapter pattern. You can see some of the Kohana system libraries making use of this pattern by checking out the system/libraries directory. Within this directory you will find the drivers folder, for now take a quick look at it and see if you recognise any of the class names, we’ll come back to it later.

The adapter pattern has a simple goal, that of ‘adaption’, that is to translate the interface of one class (or system) to that of another interface which some other class (or system) expects. Within the context of Kohana we are adapting the interface of different systems to fit with the standard Kohana libraries.

For a high level overview of this, take a look at the imaege above

  • A – Would represent the external library or system which we would like to make use of
  • B – Would represent our adapter class, or ‘driver’ to use the Kohana terminology
  • C – Would represent the Kohana native class (in the case of currently existing libraries), or your own Kohana library perhaps Kohana Session Library One practical example of this within Kohana is the Session library. Kohana comes as standard with adapters (or drivers as I shall now refer to them) so that the Session library can work seemlessly with cookie, database or cache based storage.

Each one of these solutions has a very different set of native php functions, and even different methodology in terms of achieving the same end result: Writing to a database is very different when compared to creating and storing a cookie, yet somehow the same Session library must work with all of these.

Lets take a look at reading current session data with a couple of different solutions.

Database
1
2
3
4
5
6
7
8
9
10
<?php
// Load the session
$query = (/*query db here*/);

if ($query->count() === 0) //return nothing

// Load the data
$data = $query->current()->data;

return $data;
Cookie
1
2
3
4
5
6
<?php
$data = (string) cookie::get('session-name');

if ($data == '') //return nothing

return $data;

So how do we come up with a class, which can offer all of these different possibilities to our users?

One solution One relatively basic solution would be to put some inline logic into our Session class, something like the following;

1
2
3
4
5
6
<?php
if(//database driver)
  //database specific read
else if (//cookie driver)
  //cookie specific else if (//cache driver) //cache specific
...

this would certainly work, and would allow us to use the specific code depending on which configured method the user had chosen. The flaw with the above comes to light when we would like to add another method for storing and reading sessions, perhaps one of the currently trendy cloud services? We would need to directly edit the Session library and hunt out each area in which such a logic switch is employed. This would then lead to a very long and complex class, not fun to maintain! So how can we improve this scenario?

Enter the adapter pattern The problem here is because in the above situation the Session class has intrinsic knowledge of each of the currently available methods, if we could remove these dependencies then we could ‘de-couple’ the session class from each of the individual implementations (the specifics of which, as the author of the Session library, do not interest us).

So instead we build our library against some abstract functions, which are defined elsewhere. We can then delegate the specific implementation of each of these abstract functions to individual implementing classes (our drivers).

1
2
3
4
$driver = 'database';

//How the data is read no longer interests us
$data = $driver->read(); [/code]

Defining our ‘abstract’ functions So when creating our Session class we need a way of defining these generic functions against which we build the class, and which each driver must implement; enter the interface.

An interface defines the functions an implementing class must provide. Take a look within the drivers directory, you will see a file named Session and a directory of the same name. The file is the interface of which we just spoke, open it up and take a look at it, within it you can see each of the functions that must be provided by any implementing class. The Session directory then is where all our drivers are stored . .easy!

To recap

  • We have seen how we can decouple a class from the individual, low level implementations of a scenario (e.g.  reading session data)
  • We’ve show that this leads to a very flexible, and ultimately resilient design, offering much greater scope for expandability and greater ease of maintenence
  • ****We have seen that Kohana provides a file structure and loading mechanism which enables easy and effective use of the adapter pattern
  • We have shown an example of an implementing library (the Session library)
  • We know where the interface which each of our adapters must implement resides
  • We have found the directory within which our drivers are stored Conclusion Kohana has been written, from the ground up, with clean well architectured code, the benefits of which are two-fold. Firstly you benefit immediately from the existing libraries and helpers bundled with the framework. Secondly, and most importantly, you are encouraged (note encouraged not forced) to write your (inevitable) extensions to the system in a similarly well architectured fashion.

IMO Kohana strikes the perfect balance between an overly ‘nursed’ system (such as Codeignitor) and a well structured set of libraries (such as Zend). As such it facilitates RAD and at the same time provides ample scope for future expansion and custom system add-ons.

As usual all comments greatly appreciated, I hope to have the next article in this mini-series up next week!

/Matt