多様性を意識したデザインパターン

に公開

動物の分類

動物について、オブジェクト指向で表現しようと思う。
動物の種類といっても、いろいろいる。猫や犬、鳥など、今回はここら辺を例として挙げよう。

テンプレートメソッドPatternでの記述

これをテンプレートメソッドpatternで書き表すと、

  1. おおもとの分類クラス
abstract class AnimalTemplate {
    // テンプレートメソッド(処理の流れを定義)
    public function describe(): void {
        echo "生物分類: " . $this->classifyBiology() . "\n";
        echo "生態分類: " . $this->classifyEcosystem() . "\n";
        echo "----------------------------\n";
    }

    // サブクラスが実装する抽象メソッド
    abstract protected function classifyBiology(): string;
    abstract protected function classifyEcosystem(): string;
}
  1. 具象クラス
class CatAnimal extends AnimalTemplate {
    protected function classifyBiology(): string {
        return "哺乳類 > 食肉目 > ネコ科";
    }

    protected function classifyEcosystem(): string {
        return "小型捕食者・単独行動・ペット";
    }
}
class DogAnimal extends AnimalTemplate {
    protected function classifyBiology(): string {
        return "哺乳類 > 食肉目 > イヌ科";
    }

    protected function classifyEcosystem(): string {
        return "中型捕食者・群れ行動・番犬・ペット";
    }
}
class BirdAnimal extends AnimalTemplate {
    protected function classifyBiology(): string {
        return "鳥類 > スズメ目 > ○○科";
    }

    protected function classifyEcosystem(): string {
        return "飛翔性・雑食性・観賞用ペット";
    }
}
$animals = [
    new CatAnimal(),
    new DogAnimal(),
    new BirdAnimal()
];
foreach ($animals as $animal) {
    $animal->describe(); // ← テンプレートメソッドが呼ばれる!
}

メリットとデメリット

実際、今までの感じたとこれが普通のように思える。処理の流れが共通化されており、わかりやすい。
ただ、これだとデメリットが存在する。

組み合わせに限界が来るのである。
というのも、変わった生物(ネコ生物+イヌ生態など)がいると、途端に破綻するのである。
その変わった生物専用のクラスを作らなくてはいけない。

ブリッジPatternでの記述

ここで解決方法がある。
classifyBiology
classifyEcosystem
この二つの分類結果で、種類を判別するのである。

interface petKindsInterface
{
    public function classifyBiology(): string;
    public function classifyEcosystem(): string;
}
  1. おおもとの分類クラス
class Animal implements petKindsInterface {
    private BiologyClassifier $biology;
    private EcosystemClassifier $ecosystem;

    public function __construct(BiologyClassifier $biology, EcosystemClassifier $ecosystem) {
        $this->biology = $biology;
        $this->ecosystem = $ecosystem;
    }

    public function classifyBiology(): string {
        return $this->biology->getClassification();
    }

    public function classifyEcosystem(): string {
        return $this->ecosystem->getClassification();
    }
}
  1. 具象クラス
class DogBiology implements BiologyClassifier {
    public function getClassification(): string {
        return "哺乳類 > 食肉目 > イヌ科";
    }
}

class DogEcosystem implements EcosystemClassifier {
    public function getClassification(): string {
        return "中型捕食者・番犬・ペット";
    }
}
class CatBiology implements BiologyClassifier {
    public function getClassification(): string {
        return "哺乳類 > 食肉目 > ネコ科";
    }
}

class CatEcosystem implements EcosystemClassifier {
    public function getClassification(): string {
        return "小型捕食者・単独行動・ペット";
    }
}
class BirdBiology implements BiologyClassifier {
    public function getClassification(): string {
        return "鳥類 > スズメ目 > トリ科";
    }
}

class BirdEcosystem implements EcosystemClassifier {
    public function getClassification(): string {
        return "飛翔性・雑食性・観賞用ペット";
    }
}

これにより、

$animal = new Animal(new CatBiology(), new DogEcosystem());

このように、分類の組み合わせで無限に動物を表現できるのである。
また、新しい分類の追加が楽で、拡張性が高い。

結果

身もふたもないことを言ったらトレードオフなのである。

  • 処理の流れが分かりやすいテンプレートメソッドパターン
  • 無限に種類を表現できるブリッジメソッドパターン

うまく使いこなせるようになりたい。

Discussion