5

我正在开发一个遗留 Java 应用程序,它处理“水果”和“蔬菜”,比如说,为了这个问题。它们在内部被视为不同的事物,因为它们没有共同的所有方法/属性,但是很多事情都与它们非常相似。

所以,我们有很多方法doSomethingWithAFruit(Fruit f)doSomethingWithAVegetable(Veg v),它们使用正确的doOtherStuffWithAFruit(Fruit f) / doOtherStuffWithAVeg(Veg v)。和那些非常相似,除了用水果做事情的方法只调用水果做事情的方法,蔬菜也一样。

我想重构它以减少重复,但我不确定实现这一目标的最佳方法是什么。我读过一些关于一些设计模式的文章,但我不知道它是否让我更清楚。(我可以在我使用的代码中识别出一些模式,但我真的不知道什么时候应该应用模式来改进周围的事情。也许我应该阅读更多关于重构本身的内容......)

我在考虑这两个选项:

1.创建一个类,该类可以有一个水果或蔬菜的实例,并将其传递给方法,尽量减少重复。它会是这样的:

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

这会让事情变得更好,但我不知道......它仍然感觉不对。

2.我认为的另一种选择是在水果和蔬菜中放置一个界面,其中包含它们共有的东西,并使用它来传递它。instanceof我觉得这是更清洁的方法,尽管当需要特定于它们的东西时,我将不得不使用并转换为 Fruit/Vegetable。

那么,我还能在这里做什么呢?这些方法的缺点是什么?

更新:请注意,这个问题有点简化,我正在寻找用“植物”做事的方法,也就是说,主要是“使用”它们而不是对它们做事的代码。话虽如此,我所指的那些类似方法不能在“植物”类中,而且它们通常还有另一个参数,例如:

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

也就是说,这些方法除了 Fruits/Vegetables 之外还有其他问题,并且不适合放在任何 Fruit/Vegetable 类中。

更新 2:这些方法中的大多数代码仅从 Fruit/Vegetable 对象读取状态,并根据适当的类型创建其他类的实例,存储在数据库中等等——从我对评论中问题的回答,我认为这很重要

4

6 回答 6

2

我认为第二种方法会更好..设计接口始终是一种更好的设计方式..这样您就可以轻松地在实现之间切换..

如果您使用接口,则不需要进行类型转换,因为您可以轻松利用polymorphism.. 的概念。也就是说,您将拥有指向子类对象的 `Base class reference..

但是,如果您只想保留接口通用的方法fruitsvegetables接口中的方法,以及实现类中的特定实现..那么在这种情况下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

    }
}

好的,我已经修改了一个代码来制作eat()方法来获取类型的参数Food..这不会有太大的区别..您可以将Vegetable对象传递给Food引用..

于 2012-10-02T14:26:29.060 回答
1

如果您具有分别特定于水果和蔬菜的功能,并且使用这两种类型的客户端必须区分(使用instanceof) - 这是一个连贯与耦合问题。

也许考虑是否将所述功能放置在水果和蔬菜本身附近而不是与客户一起更好。然后客户端可能会以某种方式被引用(通过通用接口)而不关心他正在处理的实例。至少从客户的角度来看,多态性将被保留。

但这是理论上的,对于您的用例可能不实用或过度设计。或者你最终可能只是隐藏instanceof在你设计的其他地方。instanceof当你开始在水果和蔬菜旁边有更多的继承兄弟姐妹时,这将是一件坏事。然后你就会开始违反开放封闭原则

于 2012-10-02T18:53:02.553 回答
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 类,所以你自己创建了一个工厂来决定你想要哪个消费者。您的工厂基本上执行 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;
   }
}

因此,您最终将责任从您的类转移到工厂类,以确定可以处理哪些对象。当然,诀窍是所有消费者都必须实现一个通用接口。

我意识到我的伪代码是非常基础的,但这只是为了指出总体思路。根据您的类的结构,这可能会或可能不会起作用和/或变得过大,但如果设计得当,可以显着提高代码的可读性,并真正将每种类型的所有逻辑都保留在自己的类中没有 instanceof 和 if/then 分散在各处。

于 2012-10-02T20:52:14.810 回答
0

我会创建和抽象基类(比如说 - 食物,但我不知道你的领域,其他东西可能更适合)并开始一个接一个地将方法迁移到它。

如果您看到 'doSomethingWithVeg' 和 'doSomthingWithFruit' 略有不同 - 在基类中创建 'doSomething' 方法并使用抽象方法仅执行不同的部分(我猜主要业务逻辑可以统一,并且只有像写入数据库/文件这样的小问题不同)。

当您准备好一种方法时 - 测试它。在你确定没问题之后 - 去另一个。完成后,Fruit 和 Veg 类不应该有任何方法,而是抽象方法的实现(它们之间的微小差异)。

希望能帮助到你..

于 2012-10-02T14:27:58.477 回答
0

这是一个基本的OOD问题。因为水果和蔬菜属于植物类型。我会建议:

interface Plant {
    doSomething();
}

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

水果也是如此。

在我看来,doOtherStuff 方法对于相关类应该是私有的。

于 2012-10-02T14:29:00.240 回答
0

您还可以考虑让它们都实现多个接口,而不仅仅是一个。这样你就可以根据情况对最有意义的接口进行编码,这将有助于避免强制转换。有什么Comparable<T>作用。它有助于方法(例如对对象进行排序的方法),它们不关心对象是什么,唯一的要求是它们必须具有可比性。例如,在您的情况下,两者都可以实现一些名为 的接口Edible,然后将它们都作为Edible预期的位置Edible

于 2012-10-02T14:45:16.143 回答