Factory Method パターン

画像の保存先を変えよう

画像の保存先を、コードを修正せずconfigによって変更できるようにしたいと思っています。
さっそく書いてみましょう。

Factory Method パターンとは

インスタンスを生成するFactoryメソッドを抽象メソッドとして用意し、サブクラスにメソッドの生成を任せることで、メソッドの生成と取り扱いの柔軟性を高めるパターンです。

/**
 * 画像の格納庫を作成するFactoryクラス.
 */
abstract class ImageStorehouseFactory
{
  public function createImageStorehouse(): ImageStorehouse
  {
    return $this->createStorehouse();
  }
  protected abstract function createStorehouse(): ImageStorehouse;
}

/**
 * 画像の格納庫
 */
interface ImageStorehouse
{
  public function store(string $path): void;
}

class S3ImageStorehouseFactory extends ImageStorehouseFactory
{
  public function createStorehouse(): ImageStorehouse
  {
    $storehouse = new S3ImageStorehouse();
    $storehouse->setBucketName('バケット名');
    return $storehouse;
  }
}

class S3ImageStorehouse implements ImageStorehouse
{

  private string $bucketName;

  public function setBucketName(string $bucketName): void
  {
    $this->bucketName = $bucketName;
  }
  public function store(string $path): void
  {
    echo $path . "をS3({$this->bucketName})に保存します.\n";
  }
}

class LocalImageStorehouseFactory extends ImageStorehouseFactory
{
  public function createStorehouse(): ImageStorehouse
  {
    $storehouse = new LocalImageStorehouse();
    $storehouse->setStorehousePath('/var/www/html/assets/');
    return $storehouse;
  }
}

class LocalImageStorehouse implements ImageStorehouse
{
  private string $path;

  public function setStorehousePath(string $path): void
  {
    $this->path = $path;
  }
  public function store(string $path): void
  {
    echo $path . "を公開ディレクトリ({$this->path})に保存します.\n";
  }
}

function getStorehouseFactoryClassName(): string
{
  return 'LocalImageStorehouseFactory';
}

// configからクラス名を取得します.
$storehouseFactoryClassName = getStorehouseFactoryClassName();

$factory = new $storehouseFactoryClassName();
$storehouse = $factory->createImageStorehouse();
$storehouse->store('/var/www/uploaded/hoge.jpg');

ImageStorehouseの実装クラスをconfigに記載すれば良いかも知れませんが、
ImageStorehouseの実装クラスにはそれぞれsetBucketName, setStorehousePathという異なる初期設定方法が実装されています。

Factoryクラス側でインスタンス生成だけでなく初期設定も合わせて隠蔽できるのが、Factory Methodパターンのメリットでしょう。

またこのパターンはインスタンスの生成を子クラスに任せるというパターンであることもあり、
Template Method パターンとも相性の良いパターンとも言えるでしょう。