web-dev-qa-db-ja.com

依存関係の逆転の原則に故意に違反しても大丈夫ですか?

クラスをリファクタリングする作業をしています。これは現在「神のクラス」であり、そのクラスでのみすべての異なるロジック/操作が含まれています。私のソリューションの1つは、ロジックのすべての異なる部分を独自のクラスに抽出することです。これにより、「Orchestrator」クラスがすべての個別のクラスを呼び出してロジックを実行します。例:

public class TripOrchestrator {

  public Trip build(TripInformation information) {

    final TripFee tripFee = new TripFeeBuilder(information.getFee());

    final TripTiming tripTiming = new TripTimingBuilder(information.getTripTiming());

    final TripSettings tripSettings = new TripSettingsBuilder(information.getSettings());

    final TripNotification tripNotification = new TripNotification(information.getNotification);

    return new Trip(tripFee, tripTiming, tripSettings, tripNotification);
  }
}

このリファクタリングの前は、このロジックはすべて、ビルダークラスなしで1つの「神クラス」に配置されていました。

私の質問

まず、これはこのような問題に対処するための合理的なアプローチですか?次に、DIPに違反しているのは悪いことですか?例えば、

final TripFee tripFee = new TripFeeBuilder(information.getFee());

TripFeeオブジェクトは具象クラスであり、TripFeeBuilderも同様です。これらの実装はすべて「具体的」であり、インターフェースに「抽象化」されていません。なぜこれが問題ないのかという私の考えは、これらのビルダーとクラスはすべて非常に安定しているということです。ロジック/機能が変更されることはまれであり、変更があった場合、それは非常にマイナーなものになります。

DIPでは、上記の行は次のようになっている必要があります。

final ITripFee tripFee = new TripFeeBuilder(information.getFee());

'ITripFee'はインターフェイスであり、TripFeeBuilderは可能な実装の1つです。しかし、私が述べたように、TripFeeの別の実装はおそらくありません。

これについての考え/意見が好きですか。私はJavaでの経験がありますが、デザインの初心者です。ありがとう。

1
bob9123

ポリモーフィズムやテストのためのインターフェースが必要ない場合は、作成しないでください。 (そして、テスト用に作成したとしても、どこでも使用する必要はありません)

後からいつでもインターフェイスを抽出できます。多くのIDEでこれを「自動的に」行うためのツールがあります。

5
Bwmat

あなたのアプローチは結構です。

DIは、インターフェイスに依存することがクラスに依存するよりも優れているとは言いません。

DI原則では、低レベルの詳細ではなく抽象化に依存する必要があると述べています。これは「実装ではなくインターフェイスへのプログラム」と表現されることもありますが、このコンテキストでは「インターフェイス」はモジュールまたはタイプのパブリックサーフェスを指し、特にinterface宣言を指しません。

5
JacquesB

依存関係の逆転の原則に故意に違反しても大丈夫ですか?

はい。これは予想されることです。何かを初めて機能させるときに、すべての原則に従うことはまれです。これを後で変更する必要がある場合は、技術的負債と呼ばれるものを引き受けています。これが変更されるのは、他の要件の変更と同じように強制的に変更する必要がある場合だけです。原則に従うことはあなたに何かを得るべきです。それが何であるかわからないときは、原則を決して守らないでください。信仰に基づいてプログラミングしないでください。理解し、その価値を理解できる原則に従います。

ただし、どちらの方法でも混乱する可能性があります。簡単なレビューをさせてください。

DIPでは、上記の行は次のようになっている必要があります。

final ITripFee tripFee = new TripFeeBuilder(information.getFee()).build();

実際には、これはDIPではなくC#の規則の問題です。これは、Javaの慣習に従います。

final TripFee tripFee = new TripFeeBuilder(information.getFee()).build();

ITripFeeTripFeeはC#のものではありませんJavaものです。JavaはTripFeeTripFeeImplを使用します。良い点は、ここではTripFeeImplも表示されないことです。したがって、ソースは静的にそれに依存していません。

また、明示的なインターフェイスキーワードは、これらの2つの言語のバグの多い型システムの陰謀であり、作成者は複数の継承を行う方法を理解していなかったため、それぞれの言語に完全に追加するのが遅くなってしまいました。実装ではなく抽象化に依存することが何を意味するかについての理解を汚さないでください。インターフェイスキーワードの有無にかかわらず、DIPを追跡できます。そうすることができます。

多重継承の必要がない場合は、interfaceキーワードなしで簡単に実行できます。 1つの方法は、古い学校のテンプレートパターンです。それは時代遅れですが、それでも動作します。変更できないものがnewTripFeeを使用していない限り、抽象クラスにすることができます。その後、さまざまな実装がそれを継承できます。あなたの工場はどちらを選択するようになります。

もう一つの方法は作曲です。 TripFeeは、想定されていることを実行する方法を知っているパラメーターを受け取り、そのパラメーターにその作業を委任できます。 3つのオプションのうち、これはほとんどのキーボード入力を含みますが、最も強力です。このメソッドにより、TripFeeを具象クラスのままにすることができますが、固定実装ではなくなりました。

ちなみに、Javaの慣習に従っている場合、TripFeeが抽象化なのか、固定された実装なのかは、まだわかっていません。それはいい。クライアントは、それが何であるかを知るビジネスを持っていません。彼らが知る必要があるのは、それに話しかける方法です。

悲しいことに、Javaの規則に従うことでこの知識をソースに隠すことができますが、クラスであるTripFeeからキーワードインターフェースに変更するには、TripFeeを認識するすべてのものを再コンパイルする必要があります。

小さなパッチでアップデートしたいと思っていたバイナリを公開していない限り、これは通常大きな問題ではありません。

1
candied_orange