概要
単一責任原則(Single Responsibility Principle, SRP)についての理解がいまいちだったので、自分なりに勉強した結果、考えたことをまとめてみた。
※ この記事ではサンプルコードを使うなどしておらず、具体的な解決方法については触れていません🙏
最初に結論
SOLID原則のなかで、パット見、一番簡単そうに見えるが、実は一番奥が深いのかもしれない。
何についての責任なのかが大事である。
- 機能(目的)について、もしくはクラスの変更理由について、の2通りに分けて考える必要がありそうと思った。
そもそも単一責任原則(SRP)とは
Wikipediaの説明を拝借すると、微妙に違う2つの説明がされているように思った。
以下は、単一の機能についての責任ということであった。
単一責任の原則 (たんいつせきにんのげんそく、英: single-responsibility principle) は、プログラミングに関する原則であり、モジュール、クラスまたは関数は、単一の機能について責任を持ち、その機能をカプセル化するべきであるという原則である。モジュール、クラスまたは関数が提供するサービスは、その責任と一致している必要がある[1]。
以下は、クラスの変更理由が一つであり、それがアクターについてのものだとなっている。
※ アクターとは、そのシステムを利用するユーザーのことだと思う。
単一責任の原則は、ロバート・C・マーティン(英語版)によって定義された。この原則について、彼は、「クラスを変更する理由は、ひとつだけであるべきである」[1] と表し、「変更する理由」に関して、「この原則は、人についてのものである」と述べ[2]、アクターについてのものであると補足した[3]。
上記の説明で思ったこと
おそらく、多くの人が単一責任の原則について取り上げていることとしては、1つ目の説明に出てくる「単一の機能」という部分なのかなと思った。
しかも、この「機能」というものを、おそらく振る舞いとか処理内容とかという具体な部分に絞っていそうかなとも思った。
たしかにそこだけ考えると楽に思えるのだが、そこだけに着目しすぎると、極端ではあるが、一つのクラスには一つの振る舞い、にするというような勘違いも起こりえそう。
実際にそう捉えられかねない内容が書かれた記事もあった。
この「機能」について個人的に思ったこととしては、振る舞いとか処理内容とかということではなく、もう少し抽象化した「目的」ということだと考えてみると腹落ちするような気がした。
その目的を見出し、どこに落とし所を見つけるかという作業も難しいことではあるが、単一責任なクラスを設計するうえではそうやって考えるのがよさそうなのかもしれない。
また、2つ目の説明とごっちゃにして考えるのではなく、それはそれで考えたほうがよさそうとも思った。
というか、アクターを基準にクラスを分けるのは現実的にはなかなか難しそうな気がするので、まずは1つ目の説明の内容をコードに反映するだけでも十分なのかもしれない。
(まだまだそこの概念についての理解が足りていないというのもあったり、特に自分の場合はRailsを使っているというのもあるが、、)
とにかく、単一責任の原則は奥が深いことがわかった。
あと、細かいところではあるが、1つ目の説明は関数にも触れているが、2つ目の説明では関数には触れていないのも気になったりしたので、そういったことも含め、もっと勉強してみようと思った。
アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技 を読むのがいいのかな?
余談
(その1)記事にしようと思った背景
先日、Object-Oriented Conference 2024というカンファレンスに参加してきたのだが、度々SOLID原則についての話題があがっていた。
SOLID原則自体はもちろん聞いたことはあるけど、説明できるほど理解はしていないし、普段からきちんと意識できているわけでもなかった。
そして、いい機会なのでSOLID原則のそれぞれについて、ちゃんと勉強しようという気にさせてもらえた。
(カンファレンスから少し日が経過してしまったが、、)
あと、単純にSOLID原則のそれぞれの概要だけだとつまらないので、一つ一つの原則について少しだけ丁寧目に理解をしていきたかったので、とりあえずS: 単一責任原則
から勉強して、記事にしてみようと思った。
(とはいいつつ、そんなに深堀れているわけではないが。。)
(その2)Railsのモデルに単一責任の原則を適用するのって難しそう
Wikipediaの2つ目の説明にあるような、変更理由がひとつである部分について、サンプルコードを書いてみようかと思ったが、自分には難しく書けなかった。
特に、自分は普段Railsを触っているので、最初はRailsのActiveRecordのモデルを例にサンプルコードを書いてみようと思ったが、なおさら難しくて諦めてしまった。
STIを使うべきなのか、テーブルレベルで分割すべきなのか、それとも別のアプローチで解決すべきなのかがわからなかった。。
DDDやクリーンアーキテクチャについて学んでいけばヒントがあるのかもしれないので、それはそれで追々勉強してみようと思う。