web-dev-qa-db-ja.com

インターフェースを実装するが他のクラスを実装しない1つのクラスでのみ使用可能なメソッドを呼び出すためのより良い方法はどれですか?

基本的に、特定の条件を前提として、さまざまなアクションを実行する必要があります。既存のコードはこのように書かれています

基本インターフェース

// DoSomething.Java
interface DoSomething {

   void letDoIt(String info);
}

最初のワーカークラスの実装

class DoItThisWay implements DoSomething {
  ...
}

2番目のワーカークラスの実装

class DoItThatWay implements DoSomething {
   ...
}

メインクラス

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          worker = new DoItThisWay();
        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

このコードは問題なく動作し、簡単に理解できます。

さて、新しい要件のため、DoItThisWayにのみ意味のある新しい情報を渡す必要があります。

私の質問は、次のコーディングスタイルはこの要件を処理するのに適していますか。

新しいクラス変数とメソッドを使用

// Use new class variable and method

class DoItThisWay implements DoSomething {
  private int quality;
  DoSomething() {
    quality = 0;
  }

  public void setQuality(int quality) {
    this.quality = quality;
  };

 public void letDoIt(String info) {
   if (quality > 50) { // make use of the new information
     ...
   } else {
     ...
   }
 } ;

}

このようにすると、呼び出し側に対応する変更を加える必要があります。

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          int quality = obtainQualityInfo();
          DoItThisWay tmp = new DoItThisWay();
          tmp.setQuality(quality)
          worker = tmp;

        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

それは良いコーディングスタイルですか?それともキャストできますか

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          int quality = obtainQualityInfo();
          worker = new DoItThisWay();
          ((DoItThisWay) worker).setQuality(quality)
        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }
11
Anthony Kong

letDoIt()DoItThisWay()を呼び出すたびにqualityを設定する必要があると想定しています。

ここで発生する問題は次のとおりです。 時間結合 を導入しています(つまり、DoItThisWaysetQuality()を呼び出す前にletDoIt()を呼び出すのを忘れた場合はどうなりますか? )。また、DoItThisWayDoItThatWayの実装は異なっています(一方はsetQuality()を呼び出す必要があり、もう一方は呼び出さない必要があります)。

これは今のところ問題を引き起こさないかもしれませんが、最終的にあなたを悩ませるようになるかもしれません。 letDoIt()をもう一度見て、品質情報を、通過するinfoの一部にする必要があるかどうかを検討することは価値があるかもしれません。しかし、これは詳細に依存します。

6
CharonX

それは良いコーディングスタイルですか?

私の観点からは、どちらのバージョンも違います。最初にsetQualityを呼び出す前にletDoItを呼び出す必要があるのは 時間結合 です。あなたはDoItThisWayDoSomethingの派生物として見ているのに行き詰まっていますが、それは(少なくとも機能的には)ではなく、むしろ

interface DoSomethingWithQuality {
   void letDoIt(String info, int quality);
}

これはMainのようなものになります

class Main {
    // omitting the creation
    private DoSomething doSomething;
    private DoSomethingWithQuality doSomethingWithQuality;

    public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
            int quality = obtainQualityInfo();
            doSomethingWithQuality.letDoIt(info, quality);
        } else {
            doSomething.letDoIt(info);
        }
    }
}

一方、パラメーターを直接クラスに渡し(これが可能であると想定)、どちらを使用するかの決定をファクトリーに委任することもできます(これによりインスタンスが再び交換可能になり、両方ともDoSomethingから派生できます) )。これにより、Mainは次のようになります。

class Main {
    private DoSomethingFactory doSomethingFactory;

     public doingIt(String info) {
         int quality = obtainQualityInfo();
         DoSomething doSomethingWorker = doSomethingFactory.Create(info, quality);
         doSomethingWorker.letDoIt();
     }
}

あなたが書いたことを知っています

パフォーマンス上の理由から、DoItThisWayおよびDoItThatWayの構築はMainのコンストラクターで1回実行されるためです。

しかし、ファクトリで作成するのにコストのかかるパーツをキャッシュして、コンストラクターに渡すこともできます。

15
Paul Kertscher

qualityをコンストラクターに渡すことができず、setQualityの呼び出しが必要であると仮定します。

現在、次のようなコードスニペット

    int quality = obtainQualityInfo();
    worker = new DoItThisWay();
    ((DoItThisWay) worker).setQuality(quality);

それにあまりにも多くの考えを投資するには小さすぎます。私見それは少し見苦しく見えますが、理解するのはそれほど難しくありません。

このようなコードスニペットが大きくなり、リファクタリングが原因で次のような問題が発生すると、問題が発生します。

    int quality = obtainQualityInfo();
    worker = CreateAWorkerForThisInfo();
    ((DoItThisWay) worker).setQuality(quality);

さて、そのコードがまだ正しいかどうかはすぐにはわかりませんし、コンパイラーも教えてくれません。したがって、正しい型の一時変数を導入し、キャストを回避することで、実際に余分な労力をかけることなく、型セーフになります。

ただし、実際にはtmpにもっと良い名前を付けます。

      int quality = obtainQualityInfo();
      DoItThisWay workerThisWay = new DoItThisWay();
      workerThisWay.setQuality(quality)
      worker = workerThisWay;

このような名前を付けると、 間違ったコードが間違って見えるようになります

10
Doc Brown

ランタイムデータに基づいて初期化が変化する一般的なインターフェイスは、通常、ファクトリパターンに適しています。

_DoThingsFactory factory = new DoThingsFactory(thingThatProvidesQuality);
DoSomething doSomething = factory.getDoer(info);

doSomething.letDoIt();
_

次に、DoThingsFactoryは、DoSomethingインターフェイスにキャストされた具体的なDoThingsWithQualityオブジェクトを返す前に、getDoer(info)メソッド内の品質情報の取得と設定について心配することができます。

2
Greg Burghardt