DEV Community

Sebastian Rapetti
Sebastian Rapetti

Posted on

Refactor, a solution with class abstraction

Working on my little PHP framework I encountered a situation when the code suffered of duplication. Situation apparently, cannot be resolved because code concern two different part of the framework.

The Mess

I have two classes, one for get a connection to persistent storage and the other for get a connection to cache. Classes return different objects, but do action at the same way.

Code is not optimized but that's another story. I wish focus on duplication and refactor.

Storage Factory:

<?php /** * Storage Factory. */ class StorageFactory { private $driver; private $supportedDriver = [ 'pdo' => PdoStorage::class, 'mysqli' => MysqliStorage::class, 'mongodb' => MongoDbStorage::class, ]; private $options; public function __construct(string $driver, array $options) { $this->driver = $driver; $this->options = $options; } public function getConnection() : StorageInterface { $driver = $this->driver; $options = $this->options; if (isset($this->supportedDriver[$driver])) { $storageClass = $this->supportedDriver[$driver]; return new $storageClass($options); } throw new InvalidArgumentException("[$driver] not supported."); } } 
Enter fullscreen mode Exit fullscreen mode

And Cache Factory:

<?php /** * Cache Factory. */ class CacheFactory { private $driver; private $supportedDriver = [ 'disk' => DiskCache::class, 'memcached' => MemcachedCache::class, ]; private $options; public function __construct(string $driver, array $options) { $this->driver = $driver; $this->options = $options; } public function get() : CacheInterface { $driver = $this->driver; $options = $this->options; if (isset($this->supportedDriver[$driver])) { $cache = $this->supportedDriver[$driver]; return new $cache($options); } throw new InvalidArgumentException("[$driver] not supported."); } } 
Enter fullscreen mode Exit fullscreen mode

Previous code is an obvious example of duplication :(

Questions begin

How resolve this problem?

Cache is a type of storage, a very fast storage. I can merge all inside one factory class but *Storage classes and *Cache classes in my framework implement different interfaces.

Furthermore, I wish use PHP return types declaration I cannot have a method that returns two different types of objects.

I analize code...

Where are the differences in the classes?

I have to isolate the differences in the code:

<?php // Storage class private $supportedDriver = [ 'pdo' => PdoStorage::class, 'mysqli' => MysqliStorage::class, 'mongodb' => MongoDbStorage::class, ]; // Cache class private $supportedDriver = [ 'disk' => DiskCache::class, 'memcached' => MemcachedCache::class, ]; // Storage class public function getConnection() : StorageInterface { //code } // Cache class public function get() : CacheInterface { //code } 
Enter fullscreen mode Exit fullscreen mode

And now?

Now I know the differences, must to split them in two concrete implementation and move duplicate code into an abstract class.

Abstract class:

<?php /** * Abstract Storage Factory. */ abstract class AbstractStorageFactory { protected $driver; protected $supportedDriver = []; protected $options; public function __construct(string $driver, array $options) { $this->driver = $driver; $this->options = $options; } protected function returnStorageObject() { $driver = $this->driver; $options = $this->options; if (isset($this->supportedDriver[$driver])) { $class = $this->supportedDriver[$driver]; return new $class($options); } throw new InvalidArgumentException("[$driver] not supported."); } /** * Get storage object. * * @return object */ abstract public function get(); } 
Enter fullscreen mode Exit fullscreen mode

Concrete factories:

<?php /** * Storage Factory. */ class StorageFactory extends AbstractStorageFactory { protected $supportedDriver = [ 'pdo' => PdoStorage::class, 'mysqli' => MysqliStorage::class, 'mongodb' => MongoDbStorage::class, ]; public function get() : StorageInterface { return $this->returnStorageObject(); } } /** * Cache Factory. */ class CacheFactory extends AbstractStorageFactory { protected $supportedDriver = [ 'disk' => DiskCache::class, 'memcached' => MemcachedCache::class, ]; public function get() : CacheInterface { return $this->returnStorageObject(); } } 
Enter fullscreen mode Exit fullscreen mode

Finally

Code duplication is disappeared and I used the PHP return types.

I wrote this little article not for the code, but for share the reasoning did for reach final result.

Top comments (0)