はじめに
こんにちは。
今回の記事では、命名法という観点から、SOLID原則の一つである単一責任の原則を考えていきたいと思います。
大前提として断っておくと、そもそも命名法と、単一責任の原則は一切関係ありません。
ただ、クラスの命名をしっかり考えて、
その名前の通りの機能(責任)を愚直に実装することが、
結果として単一責任の原則を遵守することにつながるのではないかということです。
併せて、今回の記事の内容は筆者の主張であって、
この内容が一般的に正しいということではない可能性があることをご理解ください。
あくまでこう考える人もいるんだな程度で解釈していただけると嬉しいです。
単一責任の原則とは(概要)
※単一責任の原則はもうわかるよという方はこの章は飛ばしてください。
とりあえずchatGPT先生に聞いてみましょう。(脳死)
単一責任の原則は、次のように要約できます:
クラスやモジュールは、単一の責任を持つべきです。
つまり、1つの理由で変更されるべきです。
この原則を守ることで、変更の範囲を制限し、
バグのリスクを減らすことができます。
クラスやモジュールが複数の異なる責任を持つ場合、
それらの責任は別々のクラスやモジュールに分割すべきです。
これにより、コードの理解、テスト、保守が容易になります。
単一責任の原則を守ることの利点は次のとおりです:
ソフトウェアの可読性が向上します。
各クラスやモジュールが特定の責任を持つため、コードを理解しやすくなります。
バグの発生率が低下します。
1つの責任を持つクラスが変更された場合、その変更の影響が他の責任に広がる可能性が低くなります。
コードの再利用が容易になります。
単一の責任を持つクラスは、他のプログラム部分で再利用しやすくなります。
だそうです。
特に間違えたことは言ってなさそうですね。
他のメリットとしては、一つのクラスに多数の責務が混在していると、
複数の方と同時に同じ多責務クラスを修正していると
コンフリクトを起こしてしまうことを防いだり、
修正した後の影響範囲が特定しやすいなどのメリットもあると思います!
命名法と単一責任の原則
例えば、ある文字列を、copyする機能と、printする機能があるとします。
この機能を同一のクラスに、混在されるコードを悪い例として提示します。
class TextManager {
void copyText () {
...
}
void printText() {
...
}
}
このコード自体は、一つのクラスで、copy,printの二つの責任を負ってしまっています。
ですが、このコードが単一責任の原則を違反してしまっている根本の理由は、
そもそもTextManagerという命名に問題があるのではないでしょうか?
TextManagerという曖昧な命名では、そもそもtextの何をmanageしているか不明瞭です。
このような曖昧で不明瞭な命名では、多くの機能を詰め込んでも別にいいやみたいなメンタルになりませんか?
printもcopyもtextをmanageしてはいるしね。。
ではクラス名を変更してみましょう。
//class TextManager {
//命名をTextManagerからcopyTextに変更。
class copyText {
void copy () {
...
}
void print() {
...
}
}
上記のように、copyTextというクラスにprint()という関数があったら流石に違和感を感じるはずです。
そう、命名に曖昧な要素がなく、直感的に理解できる名前であれば、
そもそもクラスに複数の責任が混在するなんてことにはならないはずです。
以下は正しいと思われる命名が行われた例です。
class CopyText {
void copy() {
...
}
}
class PrintText {
void print() {
...
}
}
PrintText,CopyTextはそれぞれの名前からクラスが持っている機能が目に見えてわかります。
正しい命名は単一責任の原則を遵守するためにとって重要というのはわかっていただけたのではないでしょうか?
もう一つ別のケースを考えてみましょう。
firstViewとsecondViewという二つの画面を表示するためのclassを考えます。
firstViewの画面
secondViewの画面
そこで、この二つの画面、一見すると似ているように見えますが、
画面中心の文言と画面上部の文言が異なるのと、
右下のボタン有無に違いがあるのがわかると思います。
もしこれを、
「あ、なんかほとんど画面一緒だし共通化しちゃえ!
そっちの方が楽だし!!」
となってしまった時のコードを想定しましょう。
class DefaultView {
// ...以下略
//条件分岐等でDefaultViewがfirst,secondViewの役割を兼ねている。
}
このコードの何が悪い?
このコードの何がだめなのかというと、
将来、FirstViewの仕様が大幅に変更になり改修が入ることになったとします。
DefaultViewの制作を担当した人は既に退職し、
別の方がこの改修の担当になったとしましょう。
この方が、DefaultViewが複数の画面で活用されていることに気づかず、
仕様に沿った変更を実施してしまった結果。。。。
secondViewで意図しないバグが発生してしまいます。
もし新しい担当者が、共通化されていることに気付いたとしても、
共通化を解除して、複雑な条件分岐もセコセコ直さないと。。
ってだけでも十分負債コードと言えそうですね。
根本な原因はDefaultViewという曖昧な命名
そもそも、一つのクラスが、複数の役割を持ってしまっているのが悪いんですが、
DefaultViewという曖昧な命名がダメです。
defaultってなんやねん。。
きちんと、firstView,secondViewとクラス名を定義していれば、今回のようなことにはならなかったと思います。
(まあfirstView,secondViewも命名としては微妙ですが、今回は例なので勘弁して)
じゃあどうすれば命名すればいい?
-
抽象的な名前を避け、なるべく具体的に。
-
and,orなどはなるべく使用しない。
-
クラスがなんの機能を担っているのかわかるように命名する。
以上のことを意識して命名すると、結果的にクラスの責務が明確になって、
単一責任の原則を遵守することに繋がります。
良い命名の方法を掘り下げることは別の記事に譲ることとします。
参考記事
最後に
最後まで読んでいただきありがとうございました。
今回の記事に関して、ご指摘等あればコメントいただける大変助かります。