前回
パッケージの分離の続き。
依存の方向を考えてみる
そもそも依存って?
依存とは、あるクラスがあるクラスを 使っている 。
あるメソッドがあるメソッドを 使っている 。
つまり、簡潔に言うと使っていることを依存という。
AというクラスがBというクラスを使っているとき、 AはBに依存している と言えるよ。
車はタイヤがないと存在できないけど、タイヤは車がなくても存在できる みたいなこと。
じゃあ、ここで一度 パッケージの 依存の方向を確認 してみよう。
プレゼンテーション層がデータを受け取ってアプリケーション層に調整を指示する。
アプリケーション層がドメイン層に業務の遂行を指示する。
ドメイン層がインフラストラクチャ層に永続化を指示する。1
インフラストラクチャ層が永続化を行う。
このように上から順に依存してしまっている。
じゃあ、OpenAiを使用するためのAPIが大幅に変わった場合、それに依存している層がどうなってしまうのか?
もちろん、その変更に対応しなければならない。
あれれ~おっかしいぞ~?
やるべきことを変えたらやりたいことが変わった???
本来は やりたいことが変わったらやるべきことも変わる が自然だよね。
そこで、型と実装の分離を行うよ。
型と実装の分離
型?実装?
型と実装というのは、オブジェクト版 やりたいこととやるべきこと 。
型( interface )にはこんな引数を受け取ってこんな戻り値を返してこんな名前で…… ということだけを定義するよ。
例えば、OpenAiにリクエストを投げてレスポンスを受け取りたい!という業務上のロジックがあったら
public interface ChatGPT{
String getResponse(String request);
}
このように定義。
型はこれだけ。
次に実装の定義。
public class ChatGPTImpl implements ChatGPT{
@Override
String getResponse(String request){
//OpenAiServiceは外部のAPI
OpenAiService openAiService = new OpenAiService("your-api-token");
//以降の処理...
}
}
インターフェイスを実装したクラスで外部のAPIを使用する。
じゃあ、OpenAiServiceというクラスが廃止になってNewOpenAiServiceというクラスに変更された場合、どこを修正すればいいのか?
そう、クラス。つまり、やるべきことを変更する。
public class ChatGPTImpl implements ChatGPT{
@Override
String getResponse(String request){
//OpenAiServiceが廃止になってしまいました;;
//OpenAiService openAiService = new OpenAiService("your-api-token");
//新しいクラスに修正
NewOpenAiService openAiService = new NewOpenAiService("your-api-key");
//以降の処理...
}
}
このように、APIが大幅に変更されても、型、つまり やりたいこと は何も変わっていないよね。
ここで実装クラスの定義をもう一度見直してみよう。
public class ChatGPTImpl implements ChatGPT
ChatGPTという 型 をChatGPTImplという 実装 が 使っている のがわかると思う。
つまり、 やるべきこと が やりたいこと に 依存 しているよね。
やりたいことがやるべきことに依存していた状況をひっくり返して
やるべきことがやりたいことに依存するようにすること を
依存性逆転の法則
というよ。
また、アプリケーションサービスなどの使う側は 型しか意識しなくていい から、実装クラスがまだできてない!という場合にもどのクラスが実際に動くかはわからないけど、とりあえずこのインターフェイスを使っておけばいいのね。というノリでコードを書き進めていくことができるよ。
次回
実装編へGO!!
-
今回の例ではこのようにはならないけど、ルールの実現上こうなってしまうことがあるよ。 ↩