「オープン/クローズドの原則:新機能の追加に強いオブジェクト指向設計」に関しての考えをまとめました。

目次

OCP(Open-Closed Principle)は、オブジェクト指向プログラミングにおける重要な設計原則の一つです。この原則を適用することで、既存のコードを変更することなく新しい機能を追加できるようになります。この記事では、OCP原則の概要と、PHPを使った具体的な実装例を紹介します。

概要

  • ocpは、既存のコードを変更することなく新しい機能を追加できるようにするオブジェクト指向による設計原則です
  • ドメインに沿った軸を決定し、軸に沿ったクラスを設計することで実現できます。軸とは2種類以上の近似するものにおける、バリエーションを生み出す要素です。ToDoリストにおけるタイトルかもしれませんし、ToDoリストの描写ロジックかもしれません
  • アンチパターンは、ifやswitch文がメインロジックに使用されている場合です。
  • クラスを引数に持つことで、インタフェースによるポリフォーリズムを利用することで、、パラメータにクラスを持たせます。 これによりインスタンスの作成を追加するだけで、既存の他のロジックに修正を加える必要がなくなります。
  • ocp準拠の一例としてGoFでまとめられているデザインパターンであるFactoryMethodがあります。

ドメインに沿った軸の設計

OCP原則を適用するためには、ドメインに沿った軸を決定し、その軸に沿ったクラスを設計します。軸は、同じ種類の異なるバリエーションを持つ要素です。たとえば、ToDoリストのアプリケーションを考えてみましょう。ToDoリストにおけるタスクのバリエーションは、タイトルや優先度、期限などが考えられます。これらのバリエーションに基づいて、軸に沿ったクラスを設計することが重要です。

アンチパターン:ifやswitch文によるメインロジック

OCP原則を守らない場合、新しい機能を追加する際に既存のコードを修正する必要が生じることがあります。特に、ifやswitch文によって異なるケースごとのロジックを記述している場合、新しいケースを追加するたびに既存のコードを変更する必要が出てきます。これはOCP原則の逆を示すアンチパターンと言えます。

OCP準拠の例:ストラテジーパターンを活用した価格計算

OCP(Open-Closed Principle)の原則を守るために、新しい機能の追加を容易にするデザインパターンの一つが「ストラテジーパターン」です。このパターンを用いて、既存のコードを変更することなく価格計算の機能を追加する方法を見てみましょう。

ストラテジーパターンの概要

ストラテジーパターンは、アルゴリズムをカプセル化し、それぞれのアルゴリズムを交換可能な形で提供するデザインパターンです。これにより、新しいアルゴリズムの追加や変更を行う際に既存のコードを変更せずに済みます。

価格計算の例

商品の価格計算を行うシステムを考えます。商品ごとに異なる価格計算方法を適用する場合、ストラテジーパターンを用いてOCP原則を守る設計を行うことができます。

interface PricingStrategy {
    public function calculatePrice($basePrice);
}

class RegularPrice implements PricingStrategy {
    public function calculatePrice($basePrice) {
        return $basePrice;
    }
}

class DiscountPrice implements PricingStrategy {
    public function calculatePrice($basePrice) {
        return $basePrice * 0.8; // 20% discount
    }
}

class Product {
    private $basePrice;
    private $pricingStrategy;

    public function __construct($basePrice, PricingStrategy $pricingStrategy) {
        $this->basePrice = $basePrice;
        $this->pricingStrategy = $pricingStrategy;
    }

    public function calculateFinalPrice() {
        return $this->pricingStrategy->calculatePrice($this->basePrice);
    }
}

$regularProduct = new Product(100, new RegularPrice());
$discountedProduct = new Product(100, new DiscountPrice());

echo $regularProduct->calculateFinalPrice(); // 出力: 100
echo $discountedProduct->calculateFinalPrice(); // 出力: 80

このコードでは、PricingStrategy インターフェースを用いて価格計算のアルゴリズムをカプセル化し、異なる価格計算方法を実装しています。Product クラスでは、異なる商品に対して異なる価格計算方法を設定することができます。新しい価格計算方法を追加する際には、新しいクラスを作成し、既存のコードを修正せずに済むため、OCP原則を守ることができます。

まとめ

ストラテジーパターンを活用することで、価格計算などの機能を追加する際に既存のコードを変更することなく、拡張性を保つことができます。OCP原則を意識した設計とデザインパターンの活用を通じて、柔軟で保守性の高いアプリケーションを実現しましょう。

Factory Methodデザインパターンの活用

Factory Methodデザインパターンは、OCP原則を適用するための有力な方法です。このパターンでは、インスタンスの生成をサブクラスに委ねることで、新しい機能の追加を容易にします。具体的な実装例を見てみましょう。

interface Task {
    public function display();
}

class NormalTask implements Task {
    public function display() {
        return "Normal Task";
    }
}

class ImportantTask implements Task {
    public function display() {
        return "Important Task";
    }
}

class TaskFactory {
    public static function createTask($type) {
        switch ($type) {
            case 'normal':
                return new NormalTask();
            case 'important':
                return new ImportantTask();
            default:
                throw new Exception("Invalid task type");
        }
    }
}

$task1 = TaskFactory::createTask('normal');
$task2 = TaskFactory::createTask('important');

echo $task1->display(); // 出力: Normal Task
echo $task2->display(); // 出力: Important Task

この例では、TaskFactory クラスを介して新しいタスクのインスタンスを生成します。新しいタスクの種類を追加する際には、新しいクラスを作成し、TaskFactory の中での条件分岐を避けることができます。これにより、既存のコードを変更せずに新しい機能を追加できるというOCP原則が守られます。

まとめ

OCP原則は、オブジェクト指向設計の中で新しい機能の追加に強い基盤を提供します。ドメインに沿った軸の設計とデザインパターンの活用を通じて、既存のコードを変更することなくシステムを拡張できる可能性が広がります。適切な設計とパターンの活用を通じて、柔軟性と保守性を持ったアプリケーションを構築しましょう。