5

私は、質問のために、たとえば「果物」と「野菜」を扱うレガシーJavaアプリケーションに取り組んでいます。それらは内部的に異なるものとして扱われます。なぜなら、それらはすべてのメソッド/プロパティを共通に持っているわけではないからですが、多くのことはそれらの両方に非常によく似ています。

したがって、適切なdoOtherStuffWithAFruit(Fruit f) / doOtherStuffWithAVeg(Veg v)使用するdoSomethingWithAFruit(Fruit f )メソッドとdoSomethingWithAVegetable(Veg v)メソッドがたくさんあります。そして、それらは非常に似ていますが、果物を使って物事を行うメソッドは、果物を使って物事を行うメソッドのみを呼び出し、野菜についても同じことを呼び出します。

重複を減らすためにこれをリファクタリングしたいのですが、それを達成するための最良の方法がわかりません。いくつかのデザインパターンについて少し読んだことがありますが、それが私にとってより明確になったのかどうかはわかりません。(使用しているコードのいくつかのパターンを認識できますが、周りの状況を改善するためにパターンをいつ適用する必要があるのか​​本当にわかりません。おそらく、リファクタリング自体についてもっと読む必要があります...)

私はこれらの2つのオプションを考えていました:

1. FruitまたはVegetableのインスタンスを持つことができるクラスを作成し、それをメソッドに渡し、重複を最小限に抑えます。これは次のようになります。

public void doSomething(Plant p) {
   // do the stuff that is  common, and then...
   if (p.hasFruit()) {
       doThingWithFruit(p.getFruit());
   } else {
       doThingWithVegetable(p.getVegetable());
   }
}

これで少し良くなるでしょうが、私にはわかりません...それでも気分が悪いです。

2.私が考えたもう1つの方法は、Fruit and Vegetableに共通のものを含むインターフェースを配置し、それを使用してそれを渡すことでした。これはよりクリーンなアプローチだと思いますが、instanceofFruit / Vegetableに固有のものが必要な場合は、Fruit/Vegetableを使用してキャストする必要があります。

それで、私はここでこれ以上何ができますか?そして、これらのアプローチの欠点は何ですか?

更新:質問は少し単純化されていることに注意してください。私は「植物」を使って物事を行う方法を探しています。つまり、植物に何かをする代わりに、ほとんど「植物」を「使用する」コードを探しています。そうは言っても、私が参照するこれらの同様のメソッドは「Plants」クラス内に含めることはできず、通常、次のような別の引数があります。

public void createSomethingUsingFruit(Something s, Fruit f);
public void createSomethingUsingVegetable(Something s, Vegetable v);

つまり、これらのメソッドには、果物/野菜以外の懸念事項があり、果物/野菜のクラスに含めるのには実際には適していません。

更新2:これらのメソッドのほとんどのコードは、Fruit / Vegetableオブジェクトから状態を読み取り、適切なタイプに従って他のクラスのインスタンスを作成し、データベースに保存するなど、私の回答からコメントの質問への回答までです。それが重要だと思います

4

6 に答える 6

2

2番目のアプローチの方が良いと思います..インターフェースに合わせて設計することは、常により良い設計方法です..そうすれば、実装を簡単に切り替えることができます..

また、インターフェイスを使用する場合は、. の概念を簡単に利用できるため、型キャストを行う必要はありませんpolymorphism

ただし、インターフェイスfruitsと共通のメソッドのみを保持vegetablesし、実装クラスに特定の実装を保持する場合..その場合typecasting、必要になります..

したがって、インターフェースレベルでジェネリックメソッドを持つことができます..そして実装レベルでより具体的なメソッド..

public interface Food {
    public void eat(Food food);
}

public class Fruit implements Food {

    // Can have interface reference as parameter.. But will take Fruit object    
    public void eat(Food food) {
        / ** Fruit specific task **/
    }
}

public class Vegetable implements Food {

    // Can have interface reference as parameter.. But will take Vegetable object
    public void eat(Food food) {
        /** Vegetable specific task **/
    }
}

public class Test {
    public static void main(String args[]) {
         Food fruit = new Fruit();
         fruit.eat(new Fruit());    // Invoke Fruit Version

         Food vegetable = new Vegetable();
         vegetable.eat(new Vegetable());   // Invoke vegetable version

    }
}

OK、コードを変更してeat()、型のパラメータを取るメソッドを作成しFoodました..それは大きな違いはありません..VegetableオブジェクトをFood参照に渡すことができます..

于 2012-10-02T14:26:29.060 に答える
1

使用できる、またはおそらくソリューションの一部として含めることができる別のオプションは、渡すオブジェクトを管理できるかどうかを消費者に尋ねることです。この時点で、送信するオブジェクトの処理方法を確認するのは消費者の責任になります。

たとえば、消費者が Eat という名前の場合、次のようにします。

Consumer e = new Eat();
Consumer w = new Water();
if( e.canProcess( myFruit ) )
   e.doSomethingWith( myFruit );
else if ( w.canProcess( myFruit ) )
   w.doSomethingWith( myFruit );

.... etc

しかし、その後、多くの it/else クラスになってしまうため、必要なコンシューマーを決定する Factory を自分で作成します。Factory は基本的に if/else 分岐を行って、渡されたオブジェクトを処理できる消費者を決定し、消費者を返します。

だから、それは次のように見えます

public class Factory {
   public static Consumer getConsumer( Object o ){
    Consumer e = new Eat();
    Consumer w = new Water();
    if( e.canProcess( o ) )
       return e;
    else if ( w.canProcess( o ) )
       return w;
   }
}

次に、コードは次のようになります。

Consumer c = Factory.getConsumer( myFruit );
c.doSomethingWith( myFruit );

もちろんcanProcess、消費者のメソッドでは、それは基本的に instanceof または派生した他の関数であり、クラスを処理できるかどうかを判断します。

public class Eat implements Consumer{
   public boolean canProcess(Object o ){
     return o instanceof Fruit;
   }
}

そのため、責任を自分のクラスから Factory クラスに移して、処理できるオブジェクトを決定することになります。もちろん、秘訣は、すべての Consumer が共通のインターフェースを実装しなければならないということです。

私の疑似コードが非常に基本的なものであることは理解していますが、それは単に一般的な考え方を指摘するためのものです。これは、クラスの構造に応じて、ケースで機能する場合と機能しない場合、および/または過剰になる場合がありますが、適切に設計されていれば、コードの可読性が大幅に向上し、各タイプのすべてのロジックを独自のクラスに自己完結型に保つことができますinstanceof と if/then なしでどこにでも散らばっています。

于 2012-10-02T20:52:14.810 に答える
1

果物と野菜にそれぞれ固有の機能があり、両方のタイプを使用するクライアントが ( を使用してinstanceof) 区別する必要がある場合、これは一貫性と結合の問題です。

上記の機能が、クライアントではなく果物や野菜自体の近くに配置されていないかどうかを検討してください。クライアントは、どのインスタンスを扱っているかを気にせずに、何らかの方法で (汎用インターフェースを介して) 機能を参照することができます。ポリモーフィズムは、少なくともクライアントの観点からは保持されます。

ただし、これは理論的なものであり、実用的ではないか、ユース ケースに対して過度に設計されている可能性があります。または、実際にはデザインの別の場所に隠れてしまう可能性instanceofもあります。instanceof果物や野菜の隣に継承兄弟が増え始めると、悪いことになります. 次に、 Open Closed Principleに違反し始めます。

于 2012-10-02T18:53:02.553 に答える
0

私は基本クラスを作成して抽象化し(たとえば、食べ物ですが、あなたのドメインはわかりません。他のものがより適している可能性があります)、メソッドを次々と移行し始めます。

「doSomethingWithVeg」と「doSomthingWithFruit」がわずかに異なる場合は、基本クラスで「doSomething」メソッドを作成し、抽象メソッドを使用して異なる部分のみを実行します (主なビジネス ロジックは統合できると思いますが、 DB/ファイルへの書き込みなどの小さな問題のみが異なります)。

1 つのメソッドの準備ができたら、それをテストします。大丈夫だと確信したら、もう一方に進みます。完了したら、Fruit クラスと Veg クラスにはメソッドがなく、抽象的なクラスの実装が必要です (それらの小さな違いです)。

それが役に立てば幸い..

于 2012-10-02T14:27:58.477 に答える
0

1 つだけではなく、両方に複数のインターフェイスを実装することも検討できます。そうすれば、キャストを回避するのに役立つ状況に応じて、最も意味のあるインターフェイスに対してコーディングできます。何の種類Comparable<T>。オブジェクトが何であるかを気にしないメソッド(オブジェクトをソートするメソッドなど)を支援します。唯一の要件は、それらが比較可能でなければならないことです。たとえば、あなたの場合、両方とも と呼ばれるいくつかのインターフェースを実装でき、両方をanが期待さEdibleれる場所として使用できます。EdibleEdible

于 2012-10-02T14:45:16.143 に答える
0

これは基本的な OOD の質問です。果物と野菜は植物型であるためです。私は提案します:

interface Plant {
    doSomething();
}

class Vegetable {
    doSomething(){
    ....
    }
}

そして果物も同じ。

doOtherStuff メソッドは、関連するクラスに対してプライベートにする必要があるようです。

于 2012-10-02T14:29:00.240 に答える