Singleton-Factory Hybrids FTW

Slowly but surely my framework is coming off the ground. I decided to adopt a singleton design pattern for my Cache, Template, and possibly any other engines I might have. Originally my design started as a factory, however it is now somewhat of a hybrid as there is a ::load() function that loads arbitrary classes into our singleton (rather than an instance of the same class like a proper singleton).

I wanted the system pluggable so the developer was not beholden to any particular engine, but I also needed to guarantee access to the Cache and Template objects to the models without having to either add to the $GLOBALS[] array or add to a controller's attribute (and call like $controller->cache->...).

So I adopted a singleton design pattern, because (a) one only needs 1 cache or template object and (b) it just looks plain nice.

For example, lets look at our cache object. I modeled the interface for the cache object around the APC naming conventions since I will be using APC myself, I wanted to retain all of APC's functionality without locking any other developers down to making sure they have APC installed.

 
final class Cache
{
	protected static $obj;
 
	public static function load( $drivername )
	{
		App::include_framework("drivers.cache/class.".strtolower($drivername).".php");
 
		$drivername .= "CacheEngine";
 
		self::$obj = new $drivername();
	}
 
	//Add if not exists
	public static function add( $key, $var, $life = 0 )
	{
		return self::$obj->add($key,$var,$life);
	}
 
	//Overwrite if exists
	public static function store( $key, $var, $life = 0 )
	{
		return self::$obj->store($key,$var,$life);
	}
 
	public static function fetch( $key )
	{
		return self::$obj->fetch($key);
	}
 
	public static function delete( $key )
	{
		return self::$obj->delete($key);
	}
 
	public static function clear()
	{
		return self::$obj->clear();
	}
}
 

Our boostrapping file (or any file where we initialize the Cache class) will have Cache::load('drivername'); statement, filling our static attribute with an instance of the proper Cache Engine, of which we have a nice pretty interface for:

 
interface CacheEngine
{
	//Add if not exists
	public function add( $key, $var, $life = 0 );
 
	//Overwrite if exists
	public function store( $key, $var, $life = 0 );
 
	public function fetch( $key );
 
	public function delete( $key );
 
	public function clear();
}

From here on out in our code, all we have to do to access the application's caching ability is call say Cache::store( ... );. What this results in is a completely transparent abstraction layer allowing for drop-in replacements without having to worry about managing object instances, etc.

As for now I'm working on how I want to manage DB connections and DB abstraction. I desire the framework to be flexible and handle multiple database connections should a developer so require, or at the very least not restrain to a single connection, HOWEVER I need to make things remain easy for developers only maintaining a single connection. Obviously my Singleton-Factory hybrid will not work here.

Then comes the fun of working out the ORM aspects of the models...

Leave a comment