全編読む必要なさそう
どうも、限界派遣SESのnikawamikanです。
ひとりアドカレ12日目です。
やっと半分ほど終わりましたねー。なかなか記事のネタが思いつかないので、結構困っていました。
最近SOLID原則をAIに対して質問しながら覚えていました。
なんていうか、真面目に記事とか読んで理解するよりも、疑問に思ったことをすぐに質問できるので効率が良いんです。
単純に学習するという効率で言えば、以下の記事で示されているように「どうして?」って聞けることに価値があるんですよね。
つまり、この記事を読むよりはAIに質問した方が効率が良いということですね。
かなしいね。
とってもだいすきSOLID原則
良いですよね、SOLID原則。私も大好きです。
単一責任の原則についてはモノリス化されたプロジェクトをリファクタリングするときにとても役立っています。
しかし、SOLID原則を理解するのって難しいですよね。
筆者はちゃんと理解しているのか、とても不安です。
そこで、何かに例えてみることにしました。
SOLID原則は「ドラえもん」です。
ドラえもんは何のためにのび太の時代にきたのか
「何を世迷言を」と思われるかもしれませんが、SOLID原則は「ドラえもん」です。
プリンの繋がっていないカラメルが半導体の例えとして使われることがあります1が、それと同じようにSOLID原則は「ドラえもん」です。
まず、ドラえもんがのび太の時代にきたのは、先祖の中で最ものび太が落ちぶれていたからです。
大人になったのび太は就職活動に失敗し、会社を立ち上げるも社屋が全焼し、多大な借金を抱えてしまうという最悪の未来が待っています。この借金によって孫の孫であるセワシは150年間残り続けた借金のためにお小遣いが500円しかもらえないという問題を抱えています。
そこで、セワシは一族の未来を変えるためにドラえもんをのび太の時代に送り込んでしずかちゃんと結婚させることで、自分が将来その借金で苦しむことを防ぐという目的を果たすのです。
のび太は誰と結婚したとしてもセワシが生まれる
本来であれば、バタフライエフェクトやらなんやらで過去に行ってしまったドラえもんがいるだけで未来が変わってもおかしくはないはずです。
しかし、セワシは言っています。
「心配はいらない。ほかでつりあいとるから。
歴史の流れが変わっても、けっきょくぼくは生まれてくるよ。
たとえば、きみが大阪へ行くとする。いろんな乗りものや道すじがある。
だけど、どれを選んでも、方角さえ正しければ大阪へ着けるんだ」
つまり、セワシ曰くのび太がどんな選択をしても、セワシは生まれてくるということです。
なお、このセリフはのび太がセワシに対して「ぼくの運命が変わったら、きみは生まれてこないことになるぜ」とのび太が行ったことに対してのセワシの返答です。
よく考えるとのび太は未来から来たという人間に出会ってすぐにその事実を受け入れています。更には自分の運命によって未来が変わることを予想していますよね。
やっぱり、のび太って天才ですね。
SOLID原則とドラえもん
SOLID原則は、のび太がどんな選択をしてもセワシが生まれるということに似ています。2
それぞれの原則(順不同)をドラえもんの例えで説明していきます。
リスコフの置換原則
SOLID原則の中でもリスコフの置換原則は、のび太がどんな選択をしてもセワシが生まれることには変わりないということです。
例えば以下のように考えると、リスコフの置換原則が成り立っていることがわかります。
# のび太の人生のクラス
class のび太:
def 結婚(self):
# ジャイ子と結婚する
...
# ドラえもんがいた時ののび太の人生のクラス
class ドラえもんがいた時ののび太(のび太):
def 結婚(self):
# しずかちゃんと結婚する
...
class セワシを生むもの:
def __init__(self, おじいちゃん: のび太):
self.おじいちゃん = おじいちゃん
def セワシを生む(self) -> セワシ:
...
if __name__ == "__main__":
nobita = ドラえもんがいた時ののび太()
nobita.結婚()
sewashi_generator = セワシを生むもの(nobita)
sewashi = sewashi_generator.セワシを生む()
このように、リスコフの置換原則は、のび太がどんな選択をしてもセワシが生まれることには変わりないということです。
これはいうなれば、抽象的"のび太"が何をしてものび太であるということには変わりないということです。
まるで人生ですね。
単一責任の原則
のび太は、結婚をするという責任を持っています。
これはセワシにとっては「のび太の人生」というのは結婚をして世代を継ぐという点以外は関係ないということです。
先ほどの例ではのび太は結婚関数しか持っていませんが、それ以外はそもそもセワシには関係ありません。(いや、関係あるかもしれませんが)
つまり、セワシにとってののび太は結婚関数以外を公開するべきではありません。
# のび太の人生のクラス
class のび太:
def 結婚(self):
# ジャイ子と結婚する
...
# これはセワシの関心事ではない
def __いじめられる(self):
# のび太にとっては悲しい出来事
...
インターフェース分離の原則
なぜ、のび太は結婚関数を持っているのでしょうか?
まず、のび太の前に一般の人間がいるとします。
この人間は誰もが結婚できるわけではないですよね。
つまり、のび太は一般の人間とは異なる結婚関数を持っているということです。
しかし、結婚というものは一般的なものであるため、のび太が特別に結婚関数を持つ必要はありません。
そこで、のび太は結婚する人間のインターフェースを持つことにしました。
# 人間のインターフェース
class 人間:
def __労働の喜びを感じる(self):
# 誰もが持つ関数
...
# 結婚する人間のインターフェース
class 結婚するもの:
def 結婚(self):
...
# のび太の人生のクラス
class のび太(人間, 結婚するもの):
def 結婚(self):
# ジャイ子と結婚する
...
これにより、のび太は結婚する人間のインターフェースを持つことで、一般的な結婚関数を持つことができるようになりました。
セワシの視点から見ると、のび太は結婚する人間のインターフェースを持つことでセワシの関心事である結婚関数を持つことができるようになりました。
オープン/クローズドの原則
のび太は結婚する人間のインターフェースを持つことで、一般的な結婚関数を持つことができるようになりました。
先ほどはドラえもんがいた時にのび太を継承していましたが、それはのび太を継承しているのであって、「のび太である」と言い切るには少し違和感があります。
そもそも、結婚インターフェースをもう少し一般的にしておくと、ジャイ子やしずかちゃん以外にも結婚できるようになります。
# 人間のインターフェース
class 人間:
def __労働の喜びを感じる(self):
...
# 結婚する人間のインターフェース
class 結婚するもの:
def 結婚(self, 相手: 結婚するもの):
...
# のび太の人生のクラス
class のび太(人間, 結婚するもの):
def 結婚(self, 相手: 結婚するもの):
# 相手と結婚する
...
このように、のび太は結婚する人間のインターフェースを持つことで、一般的な結婚関数を持つことができるようになりました。
これで、のび太を改造することなく、他の人間とも結婚できるようになりました。
人間と結婚できるのであれば、ジャイ子である必要はありません。人間であれば誰とでも結婚できるので、オープン/クローズドの原則が守られています。
ここで、セワシは別の方法でしずかちゃんと結婚させる必要が出てきてしまいました。
依存関係逆転の原則
セワシの目的は、のび太がしずかちゃんと結婚することです。
しかし、のび太はジャイ子と結婚することができます。
このままでは、セワシの目的を達成することができません。
そこで、ドラえもんを使ってしずかちゃんと結婚させることにしました。
# 人間のインターフェース
class 人間:
def __労働の喜びを感じる(self):
...
# 結婚する人間のインターフェース
class 結婚するもの:
def 結婚(self, 相手: 人間):
...
# のび太の人生のクラス
class のび太(人間, 結婚するもの):
def 結婚(self, 相手: 人間):
# 相手と結婚する
...
# たぬきと言われると怒る
class ドラえもん:
def のび太をしずかちゃんと結婚させる(self, のび太: のび太):
# のび太の人生を変える
のび太.結婚(しずかちゃん)
...
でも、これってドラえもんがのび太に依存しているような気がしますね。
なぜなら、ドラえもんはのび太を必要とする関数を持っているからです。
これでは、のび太がいない世界線ではドラえもんがいる意味がなくなってしまいます。
そこで、ドラえもんは誰でも良いので、結婚させる関数を持つことにしました。
# 2112年9月3日生まれ
class ドラえもん:
def 結婚させる(self, なにか1: 結婚するもの, なにか2: 結婚するもの):
# なにか1となにか2を結婚させる
なにか1.結婚(なにか2)
...
ここで、結婚させるのは結婚するものであるため、ドラえもんはのび太に依存していません。
これでドラえもんはのび太に依存していないので、ドラえもんがいない世界でもやっていけますね。
つまり、依存関係逆転の原則が守られています。
まとめ
最終的にセワシが目的を達成するために以下のコードを書きました。
# 人間のインターフェース
class 人間:
def __労働の喜びを感じる(self):
...
# 結婚するもののインターフェース
class 結婚するもの:
def 結婚(self, 相手: 結婚するもの):
...
# のび太の人生のクラス
class のび太(人間, 結婚するもの):
def 結婚(self, 相手: 人間):
# 相手と結婚する
...
class しずかちゃん(人間, 結婚するもの):
def 結婚(self, 相手: 人間):
# 相手と結婚する
...
class セワシを生むもの:
def __init__(self, おじいちゃん: のび太):
self.おじいちゃん = おじいちゃん
def セワシを生む(self) -> セワシ:
...
# 129.3センチかつ、体重129.3キログラムらしい
class ドラえもん:
def 結婚させる(self, なにか1: 結婚するもの, なにか2: 結婚するもの):
# なにか1となにか2を結婚させる
なにか1.結婚(なにか2)
...
if __name__ == "__main__":
nobita = のび太()
nobita.結婚(しずかちゃん())
sewashi_generator = セワシを生むもの(nobita)
sewashi = sewashi_generator.セワシを生む()
完璧ですね。
おわりに
SOLID原則を真面目に説明しようとするならば、最も単純に考えるのであれば動詞の単位でクラスを分けるということになります。
例えば、「目的地に移動」のような動詞のインターフェースを持てば、それを実装するクラスは「自動車」や「自転車」などを使っても良いということです。(ちなみにこのアイデアからドラえもんの例えが生まれました)
あくまで、関心事は「する」ことであって、「過程や…!方法なぞ…!どうでもよいのだァーッ」3ということです。
なお、上記の世迷言は筆者の主観によるものであり、実際のSOLID原則と異なっていたらすみません。
参考にしたもの