解放/閉鎖原則
1. 原則
クラスは拡張に対して開き、修正に対して閉じていること
- 「拡張に対して開いている」とは、既存コードに影響を与えず機能を追加できる特性のこと
- 「修正に対して閉じている」とは、ある機能を修正しても別の機能に影響を与えない特性のこと
- つまり、クラス自体を修正することなく、クラスの機能を追加できなくてはならない
- 具体的にはクラスの内部で条件式などで機能を切り替えるのではなく、抽象クラスやインターフェイスを用いて、ポリモフィズムで機能を切り替え可能にしておくということ
2. 根拠
保守性
- 解放/閉鎖原則に則っているクラスは機能追加や変更の際に追加や変更の影響を他のクラスが受けなくなる
- 機能追加や変更の影響からそれぞれが独立しているクラスは保守性が高く、不具合も起こりにくい
- コードも理解しやすくなる
3. 指針
インターフェイスに対してプログラムを書く
class Program:
def exec(type):
if type == '01':
return 'This is 01'
elif type == '02':
return 'This is 02'
else:
raise ValueError()
- このようなクラスに更に機能を追加したい場合、↓のように既存コードに手を入れることになる
class Program:
def exec(type):
if type == '01':
return 'This is 01'
elif type == '02':
return 'This is 02'
elif type == '03': # <-- 新たな処理が追加された
return 'This is 03' # <-- 新たな処理が追加された
else:
raise ValueError()
- このサンプルは簡単な処理の為、大した変更でもないが、これが複雑なロジックを持つプログラムなら影響範囲の推定やテストケースの検討など影響が甚大になる
- また、1つのクラスに処理が集中しており、機能追加の度にクラスが巨大化することになる(=保守性が非常に悪い)
- そこで、以下のようにプログラムを改造してみる
class Program(metaclass=ABCMeta):
@classmethod
def exec():
pass
class ProgramType01(Program):
def exec():
return 'This is 01'
class ProgramType02(Program):
def exec():
return 'This is 02'
- 処理毎にクラスを分割し、共通のインターフェイスを導入した
- このようにしておけば新しい処理を追加する際は
ProgramType03
クラスを新たに作成すればよく、その際ProgramType01
とProgramType02
は全く影響を受けることがない
class Program(metaclass=ABCMeta):
@classmethod
def exec():
pass
class ProgramType01(Program):
def exec():
return 'This is 01'
class ProgramType02(Program):
def exec():
return 'This is 02'
class ProgramType03(Program):
def exec():
return 'This is 03'
- これが「拡張に対して開いている(=既存コードに影響を与えず機能を追加できる)」「修正に対して閉じている(=ある機能を修正しても別の機能に影響を与えない)」状態である
4. 注意事項
参考資料
関連