単一責務原則
1. 原則
クラスの責務は1つであること
- クラスが受け持つ責務はたった1つでなければならない
- クラスが受け持つ責務とは、クラス(とそのロジック)を利用する者(アクター)へ提供する機能のことである
- 複数の責務を持つクラスを作ってはならず、そのようなクラスになってしまった場合は単一責務になるようクラスを分割する
2. 根拠
責務の拡大と共にコードが複雑化する
- 複数の責務を持つクラスは仕様変更に伴う責務の増加や拡大を受け入れることになる
- クラスが担う責務が増加すれば、コードは複雑化しバグは増え保守性も低下するので良いことがない
3. 指針
- 1つのクラスの責務は1つだけに限定する
- 複数の責務がある場合は別のクラスに分離する
4. 注意事項
「単一責任 == 単一メソッド」ではない
- クラスの責務が単一になることとメソッドが1つだけになることは関係ない(結果的にそうなることもある)
- 例えば、あるデータの操作が責務であるなら「参照」と「変更」は必要になる
- クラスの正常な挙動に対する責務
参考資料
Appendix-1 NG事例
役割が複数種類ある事例
- 以下のコードは1つのクラスがCSV、TSV、JSONの読み込み機能を持っているものである
- クラスが「CSV、TSV、JSONの読み込み」の役割(責務)を持っており、単一責務原則に違反している
- 「CSVが欲しいアクター」「TSVが欲しいアクター」「JSONが欲しいアクター」それぞれに対する責務を1つのクラスが担っている状態である
class DataLoader:
def csv_load(file_path):
# CSVファイルをロードして返却する
def tsv_load(file_path):
# TSVファイルをロードして返却する
def json_load(file_path):
# JSONファイルをロードして返却する
- このようなクラスはメソッド毎にクラスへ分割し、同じインターフェイスを実装するようにする
- ついでにファクトリークラス経由でインスタンスを取得するようにしておけば、利用者側からはインターフェイス(とファクトリー)しか見えないようにできる
from abc import ABCMeta, abstractmethod
class DataLoader(metaclass=ABCMeta):
@abstractmethod
def load(file_path):
pass
class CsvDataLoader(DataLoader):
def load(file_path):
# CSVファイルをロードして返却する
...
class TsvDataLoader(DataLoader):
def load(file_path):
# TSVファイルをロードして返却する
...
class JsonDataLoader(DataLoader):
def load(file_path):
# JSONファイルをロードして返却する
...
class Factory:
@staticmethod
def createCsvDataLoader():
return CsvDataLoader()
@staticmethod
def createTsvDataLoader():
return TsvDataLoader()
@staticmethod
def createJsonDataLoader():
return JsonDataLoader()
if __name__=="__main__":
loader1 = Factory.createCsvDataLoader()
print(loader1.load()) # CSVが出力される
loader2 = Factory.createTsvDataLoader()
print(loader2.load()) # TSVが出力される
loader3 = Factory.createJsonDataLoader()
print(loader3.load()) # JSONが出力される