Refactor, a solution with class abstraction
Tags: php, refactor
Published: 2017-08-23 23:24:47

---
title: Refactor, a solution with class abstraction
published: true
description: A PHP refactor pratical example with class abstraction.
tags: php, refactor
---

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.");
    }
}

```

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.");
    }
}
```

**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](https://wiki.php.net/rfc/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
}
```

*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();
}
```

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();
    }
}
```

### 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.