**Single Responsibility Principle(単一責任の原則)**は、Tom DeMarco の "Structured Analysis and Systems Specification" と、Meilir Page-Jones の "The Practical Guide to Structured Systems Design" で説明されている。この原則は次のようなものだ。
クラスを変更する理由は複数存在してはいけない
なぜこうする必要があるのか?
役割が単一なクラスの仕様要求が変化した場合、その変更部分は浮き彫りになり、どのように変化したのかわかりやすい。これは、ほとんどの人が納得するはなしではないだろうか。
しかし、役割が複数あると、その 1 つ 1 つが変更理由になってしまう。複数の変更理由によってクラスが変更されると、変更部分がぼやけてしまう。また、ある理由により変更した部分が、他の役割に影響してしまい、連鎖的に変更が必要になるなどといったケースが生じうる。これは もろい設計 にあてはまる。もろい設計がソフトウェア開発にどんな影響を及ぼすかは、前に書いた記事を参照されたい。
役割とはなにか?
「役割=変更理由」と定義している。この判断が非常に難しいケースも多々有る。
変更の理由が変更の理由たるのは、実際に変更の理由が生じた場合だけである
変化の兆候がないのに SRP を含めた原則を適用するのは賢明ではない。原則を適用することを目的にしちゃいけないというのは、いつも気に止めておかなければならないなぁと思います。
結合している役割を見つけそれらを分離する作業は、ソフトウェア設計の本質である
非常に身にしみる言葉。
どのように分離するのか
たとえば、次のようなモデムインターフェースを考える。
interface Modem {
public void dial(String pno);
public void hangup();
public void send(char c);
public char recv();
}
最初の 2 つのメソッドは接続の開始と終了、すなわち接続管理の機能を持つ。後半の 2 つのメソッドはメッセージのやりとりに用いる。この 2 つの役割を分離すべきかどうかは今後このアプリケーションがどのように変更されるか次第。たとえば接続管理の機能が影響を受けるような変更がはいる場合は、send と recv のみを呼び出しているクラスをリコンパイル、リロードしなおさなければならない。もっとも単純にこの状態から打破するには、以下のように分離すればよい。ModelImpl はその場しのぎではあるが、すべての依存関係が解消しているはずだ。
テスト主導の開発方法に従えば、設計に怪しい兆しが現れる前に、複雑な役割を持ったクラスは分離されるはずだ。なぜならば、複雑な役割をもったクラスのテストを書くのは非常に難しいからだ。しかしそれでもなお、「硬さ」や「もろさ」を感じる場合には Facade や Proxy などを使ってリファクタリングするとよいそうだ[要確認]。
余談
アジャイル開発の奥義を読むまでは、この原則の名前からして、なぜ「クラスの役割は複数存在してはいけない」という内容ではないのかという疑問を持っていた。読んだ結果自分なりには、「変更」という観点に立ったときにはじめて「役割」ごとに分離する必要がでてくるから、「クラスを変更する理由は複数存在してはいけない」と主張しているのではないかと思った。
たしかに役割は 1 つのほうが、見通しがよいかもしれないが、複雑にはなる。変更がとうてい生じないような場所に関して、無駄に分離してコードを複雑にする必要がないという意味を込めたい場合、たしかに「クラスを変更する理由は複数存在してはいけない」のほうがよい言葉だと感じた。この解釈自体は、完全に自分が読んだ感想なので、なにか間違っているなどの指摘がある方はコメントいただけるとありがたい。
Pinned Articles
About
ウェブ界隈でエンジニアとして労働活動に励んでいる @gomi_ningen 個人のブログです
Tags
JavaScript
PowerShell
kibana
elasticsearch
fluentd
nginx
イベント
五十嵐裕美
村川梨衣
logrotate
IoT
Scala
Java
C言語
iputils
ICMP
WUG
mastodon
Swift
AWS
Clock
Windows
アーキテクチャ
PoEAA
iOS
DeviceFarm
プログラミング言語
OS
StepFunctions
Lambda
Serverless
terraform
ポエム
RHEL
ネットワーク
GraphQL
CloudWatch
Linux
Coreutils
network
nc
telnet
LinuxKernel
fpinscala
ELB
IAM
AppSync
EFS
Gradle
english