オープン・クローズドの原則 (Open/Closed Principle, OCP)
オープン・クローズドの原則 (Open/Closed Principle, OCP)
1. どんなもの?
オープン・クローズドの原則(Open/Closed Principle, OCP)は、ソフトウェアのエンティティ(クラス、モジュールなど)は拡張に対して開かれているが、変更に対して閉じているべきだという設計原則です。
つまり、新しい機能を追加するときは既存のコードを変更せず、新しいコードを追加して拡張できるように設計することを目指します。
例えば、新しい種類のロジックを追加する際に既存クラスを直接変更するのではなく、新しいクラスやモジュールを追加することで対応する、といった設計を指します。
2. 通常の実装方法と比べてどこがすごいの?
通常の方法
単純にコードを追加して新しい機能を実装する場合、既存のクラスやメソッドに直接変更を加えることがあります。
class Discount
def apply(order)
if order.amount > 100
order.amount * 0.9
else
order.amount
end
end
end
order = Order.new(amount: 150)
discount = Discount.new
puts discount.apply(order) # => 135.0
-
課題:
- 新しい割引ルールを追加するたびに、既存の
Discount
クラスを変更しなければならない。 - 既存のコードを変更することで、新しいバグが発生するリスクがある。
- 新しい割引ルールを追加するたびに、既存の
オープン・クローズドの原則に基づいた方法
オープン・クローズドの原則を適用することで、新しい機能を追加しても既存のクラスを変更する必要がなくなります。
class Discount
def apply(order)
raise NotImplementedError, "This method should be overridden"
end
end
class PercentageDiscount < Discount
def apply(order)
order.amount * 0.9
end
end
class FlatDiscount < Discount
def apply(order)
order.amount - 10
end
end
order = Order.new(amount: 150)
discount = PercentageDiscount.new
puts discount.apply(order) # => 135.0
-
利点:
- 新しいディスカウントルールを追加する場合は、
Discount
クラスを継承した新しいクラスを作成するだけでよい。- 例では
FlatDiscount
クラスを追加しています。 - ディスカウントルールを変えたい場合は
PercentageDiscount
クラスを変更すればよい。
- 例では
- 既存のクラスに影響を与えないため、安全性が向上。
- 新しいディスカウントルールを追加する場合は、
3. 技術や手法の"キモ"はどこにある?
-
ポリモーフィズムの活用
- 親クラスやインターフェースを通じて、同じメソッド名で異なる振る舞いを実装することで、新しい機能を既存コードを変更せずに追加できます。
-
責務の分離
- 各クラスが特定の責務に集中することで、拡張が容易になります。
-
既存コードの安全性
- 既存コードを変更しないため、バグのリスクを最小化できます。
「拡張が容易」をもう少し詳しく
ここでいう「拡張」とは、ソフトウェアの機能や振る舞いを追加することを意味します。この際、既存のコードを変更せずに新しいコードを追加することで、機能を増やしたりロジックを変えたりすることを指します。
具体例で見る「拡張」
例えば、次のようなケースが「拡張」に該当します:
-
新しい割引ルールを追加する
- 例では、既存の
Discount
クラスを変更するのではなく、新しいFlatDiscount
やPercentageDiscount
クラスを追加することで対応しています。
- 例では、既存の
-
RailsのActiveModelバリデーションに新しいルールを追加する
-
EmailValidator
のように、新しいバリデーションを別のクラスとして追加して、User
クラスを変更せずにバリデーション機能を拡張しています。
-
「拡張」の具体的な意義
1. 既存コードを守る
拡張では新しいコードを追加するだけで済むため、既存のクラスやロジックを変更する必要がありません。これにより、既存のコードが引き起こす可能性のあるバグを防ぎます。
2. 機能の追加が簡単
新しい振る舞いや機能を追加する際に、新しいクラスやメソッドを追加するだけで対応できます。
3. 安全性が高まる
既存のコードを変更しないため、予期しない影響を回避できます。
拡張が「変更」と異なる点
変更
- 既存のコードに手を加えること。
-
例:
Discount
クラスに直接新しい割引ロジックを追加する。
拡張
- 既存コードには手を加えず、新しいコードを追加すること。
-
例:
Discount
クラスを変更せず、新しいFlatDiscount
クラスを作成する。
「拡張」と「変更」の違いは、既存のコードを触るかどうかにあります。「拡張」は既存コードを修正することなく安全性を保ちながら、新しい機能を増やす手法です。
まとめ
ここでいう「拡張」とは、既存のコードを変更することなく新しい機能や振る舞いを追加することです。この設計思想に基づくと、コードの安全性や柔軟性が向上します。
4. 実装例
RailsのActiveModelバリデーション
Railsでは、独自のバリデーションを追加する場合にActiveModelのカスタムバリデーションを活用します。
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless URI::MailTo::EMAIL_REGEXP.match?(value)
record.errors.add(attribute, "is not a valid email")
end
end
end
class User < ApplicationRecord
validates :email, presence: true, email: true
end
-
実装ポイント:
- 新しいバリデーションを追加しても、
User
クラスを変更する必要がない。
- 新しいバリデーションを追加しても、
5. 議論はあるか?
メリット
- 既存コードを変更せずに、新しい機能を簡単に追加できる。
- 変更によるリスクを低減し、コードの安全性が向上する。
- 拡張性が高く、将来的な機能追加に柔軟に対応できる。
デメリット
- 設計の段階で拡張を考慮するため、初期の実装が複雑になることがある。
- 過度に適用すると、必要以上にクラスが増え、管理が煩雑になる。
議論
オープン・クローズドの原則は、将来の拡張を考慮した設計に非常に有効ですが、小規模なシステムでは過剰設計になる場合があります。拡張性が必要な場面を見極めて適用することが重要です。
6. まとめ
オープン・クローズドの原則(OCP)は、ソフトウェアを拡張に対して開き、変更に対して閉じる設計を目指す原則です。 います。
この原則を守ることで、新しい機能を追加する際の安全性が向上し、将来的な拡張にも対応できる柔軟な設計を実現できます。
Discussion