言い訳とはじめに
Tくんアドカレの順番変わってくれてありがとう.最近C++が親友な黒岩です.
僕がPythonのDIライブラリを使ってみたかっただけなので,多分わからないです.
ブラウザバックを推奨します.いや,もはや読むな.まあアメブロってプログラム書く場所でもないっすね.キレました.結構段階的にコード書いたりしたんですけどね.載せられないんで,この辺に興味を持つきっかけになればいいです.詳しくは聞きにきたら何時間でも付き合います.いやあ,Qiitaに書くべきなんでしょうね.............ここまで読んでしまいましたね.始めます.
人は,何かに依存して生活しています.依存は必ずありますが,過度に依存することは良くありません.依存とはうまく付き合う必要があります.プログラムも同じです.
本題
依存性逆転の法則とはwikiより,以下の通りです.
- 上位モジュールはいかなるものも下位モジュールから持ち込んではならない。双方とも抽象(例としてインターフェース)に依存するべきである。
"High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces)."[3] - 抽象は詳細に依存してはならない。詳細(具象的な実装内容)が抽象に依存するべきである。
"Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions."[3]
何を言ってんだという感じでしょう.まあ大切なのは,抽象に依存しましょうってことです.
キーボードをPCに接続する時を考えてみましょう.
下位モジュールは特定のキーボード,上位モジュールは特定のPCです.抽象はUSBですね.依存性逆転の法則に違反すると言うのは,特定の PCに向けてキーボードを作ってしまうのです.つまり,USBの規格から逸脱する.(なんか,新しいポートが必要そうな気がしますね.)そのキーボードは,そのPCでしか使えないといったことが起きます.
僕たちは宇宙に関する団体なので,宇宙ぽいことを題材に依存性について書いてみたいと思います.発電量計算のために.衛星パネルと太陽光のなす角の余弦を求めるコードを書いてみました.
このプログラムはcalculatorから軌道クラスorbitと姿勢クラスattitudeを用いて,1パネルのなす角を求めるプログラムです.
最終的にこんなプログラムを書きました.
calculator.py - メイン 軌道と姿勢のクラスを使って計算する
bindings.py - DIライブラリの設定
IOrbit.py - 軌道クラスのインターフェイス
Orbit.py - β角を用いた軌道クラス (https://ameblo.jp/sssrc/entry-12716333438.html)
SkyfieldOrbit.py - skyfieldを用いたTLEから計算した軌道のクラス(時間は合わせてない)
IAttitude.py - 姿勢クラスのインターフェイス
Attitude.py - 姿勢クラス
何も考えずにプログラムを書くとすると,依存関係はこのようになるでしょう.(もちろん,こういった場合は規模がかなり小さいので,これでいいです.)
この時の問題点を考えてみましょう.
- クラスのどれかに変更があったときに,その影響が他にも伝播する.
- テストができない
- 密です
では,抽象に依存させてみましょう.抽象はインターフェイスを作ります.インターフェイスとは,クラスが持つべきメソッドを規定するものです.USBも規格がありますね.ちなみにPythonはABCという抽象規定クラスを定義できるやつがありますが,僕はあんまり好きじゃないので,必要なメソッドを定義してraise Not Implemented Errorとしてます.こうする人も結構いると思います.監修的にInterfaceの頭文字のIをとって名付けがちです.OrbitとAttitudeのインターフェイスをIOrbitとIAttitudeとしてしまいましょう.さあ,依存関係はどのようになるでしょうか...水色がインターフェイスになります.
濃い青色同士が直接つながらないのが確認できるでしょう.こうすることで,プログラム同士が疎結合になります.
では,AttitudeクラスがIOrbitクラスに依存していますが,プログラムではどのようになっているのでしょうか.簡単に思いつくのは,OrbitクラスをAttitudeクラスで生成するとかですが,それではいけません.依存性逆転の法則に反してますね.では,コンストラクタで与えるのはどうでしょう.今回は.こんな感じで描いてみました.コンストラクタで,orbitを受け取りインスタンス変数に保持しています.コンストラクタの型ヒントをIOrbitとすることで,エディタも抽象(IOrbit)に対してのメソッドを表示してくれます.ちなみに,pythonで型ヒントを使ってないやつはヤンキーです.更生しましょう.僕に聞きに来てください.
さあ最後です.Calculatorでは,IOrbitとIAttitudeに対応するクラスを用いる必要があります.これが二つとかだといいんですが,複数増えてきて,IAttitude -> IOrbitだけでなくISolarPanel -> IAttitude , IOrbit,IBattery -> ISolarPanel, IEquipmentsなどと依存関係が増えてきたらどうでしょう??(まだまだ少ない.もっと増えます.)ちょっとやってられないですね.そこで活躍するのがDIライブラリです.今回は,以下のものを使ってみました.
こういう話をするとコードが書きにくくなっちゃう人へ
世間では,クソコードと呼ばれるものがあります.僕もなんだこれ?ってものに会いましたし,書きました.クソコードはストーリーがあります.期限や納期が差し迫っていた.技術がなかったなどなど.そのストーリーを考えてみると,微笑ましいものです.また,そのストーリの中で最適出力がそのコードです.その時の全力で書いたそれなりに動くコードです.クソコードなんて言わずに,可愛がりましょう.あとは俺に任せなという気持ちで取り組んでやりましょう.それでは本当のクソコードでお別れしましょう.これだ!!!!!!!!