面向对象编程的类设计

3
我的问题涉及OOP中的类设计。假设我们有一个称为ItemBase的父类,它是Canceled、Modified和Added类的父类。我们还有一个DueToBase类-Provider和Distributor的父类。
ItemBase可能会因为DueToBase类而发生改变。
假设ItemBase具有类型为DueToBase的属性,并且DueToBase具有名为compute()的接口方法。计算算法与特定的ItemBase派生类相关。因此,我们有六种不同的ItemBase-DueToBase关系组合。
例如。
ItemBase ib = new Added();
ib.changed = new Provider(ib);
ib.changed.compute();

我的问题是在面向对象编程中,应该如何建立ItemBase和DueToBase之间的关系?我在compute方法中看不到用于检查ItemBase实例类型的swich/case或if条件语句。如果DueToBase内部有另一个XXXBase类并且该类具有依赖于DueToBase特定实例(甚至还包括ItemBase)的接口方法YYY(),那么情况就变得更糟了。 如何处理这种情况?是否有任何良好的编程模式可用于解决该问题?也许我所采取的方向是错误的。感谢你的协助。 也许我的图表不够清晰。问题出在以下伪代码中...
doSomething(){
   if(itemBase instanceof Cancelled){
      if(dueToBase instanceof Provider)
         algorithm1();
      else if(dueToBase instanceof Company)
         algorithm2();
   }else if(itemBase instanceof Modified){
      if(dueToBase instanceof Provider)
         algorithm3();
      else if(dueToBase instanceof Company)
         algorithm4();
   }else if(itemBase instanceof Added){
      if(dueToBase instanceof Provider)
         algorithm5();
      else if(dueToBase instanceof Company)
         algorithm6();
   }
}

如果使用更深的if语句,情况会变得更糟。


看看依赖注入的概念。 - tobyodavies
我知道什么是 DI 和 IoC,但这和我的问题无关。我想知道如何处理嵌套条件以选择合适的操作。 - kkris1983
你尝试过策略模式吗?看起来它应该适合。 - GriffinHeart
为了以后的参考,你应该读一本设计模式的书。我推荐的是《Head First 设计模式》。 - LanguagesNamedAfterCofee
@kkris1983,你最终是如何解决这个问题的? - TeaCupApp
4个回答

4
你的 ItemBase 类可以是一个抽象类,其中包含 compute() 方法,所有子类都可以有自己的实现。
因此,稍后你可以这样做,
ItemBase ib = new Added();
ib.changed = new Provider(ib);
ib.changed.compute();

现在,当你调用ib.changed的compute方法时,它将执行Added类的计算实现。


在您的情况下,由于基类,添加一个区分ProviderCompany的实例变量。类似于一个boolean标志或int

然后,不要使用dueToBase instanceof Provider,而是创建一个if语句。因此,您更新的伪代码将减少到几行。类似于这样,

doSomething(){
      if(dueToBase.isProvider) {
         algorithm1(); //execute if Provider
      } else { 
         algorithm2(); //execute if Company
      }
}

现在选择计算的复杂性将由抽象模式处理,而您只需担心它是公司还是提供商。

我理解多态的概念,但我的问题是“如何处理嵌套条件以选择适当的操作”。 - kkris1983
正如您之前提到的,在现实世界中,您不应该检查instanceof来执行某些代码。方法应该自然地被调用。我想ItemBase和DueToBase的设计需要更加清晰。只有当需求摆在我们面前时,我们才能调试设计,所谓需求是指整个系统的描述 :( - TeaCupApp
我认为这解决了问题,你正在使用两种策略模式,在其中你将拥有两个特定类的具体实现。除非你无法在相关部分中抽象算法。 - GriffinHeart
@Owl,这正是我想要避免的 :) 因为如果算法1中有另一层条件,它会检查每个ItemBase/DueToBase派生类的组合以选择正确的操作,那么我将陷入另一个更复杂的条件层次。是的,我同意我必须使用继承和其他面向对象编程带来的方法来避免过程式编程。但在我看来,在这种情况下,继承并不适用 :/ - kkris1983
我不需要尝试它,因为我真正理解它的方向。让我们添加另一个SomethingBase类,它是SomethingA、SomethingB和SomethingC的父类。如果算法涉及到ItemBase、DueToBase和SomethingBase的组合,那么就会变得混乱。 - kkris1983
显示剩余4条评论

1

我会说策略模式。

abstract class ItemBase {
  public DueToBase myDueToBase;
  public void partOfTheAlgorithmThatOnlySpecificIBKnows();
}

class Modified extends ItemBase {
  public void partOfTheAlgorithmThatOnlySpecificIBKnows() {
     //stuff only Modified knows
  }
}


abstract class DueToBase {
    public void partOfTheAlgorithmThatOnlySpecificDTBKnows();
}

class Provider extends DueToBase {
 //relevant code
  public ItemBase myItemBase;
  public void partOfTheAlgorithmThatOnlySpecificDTBKnows(){
       //stuff only provider knows
  }

  public void compute() {
       //you can also pass this but pointless since you all ready have the reference
       myItemBase.partOfTheAlgorithmThatOnlySpecificIBKnows();
       //some more code
  }
}

ItemBase ib = new Added();
ib.changed = new Provider(ib);
ib.changed.compute();

在这个例子中,它将调用:
1. compute on Provider
2. part of the algorithm that Modified knows how to calculate
3. rest of the code for the algorithm

因此,在compute函数中,您拥有所有继承类所需的特定方法,并且可以在compute()函数中变化算法。

如果这不能解决您的问题,您可能需要根据Owl所说的要求考虑新的设计方案。


同样也没有if语句,但有更多的方法可以实现与if相同的功能,只是让继承机制来为你完成。


这将是一个完美的解决方案,如果ItemBase有独立的算法部分,并且我们不关心哪种类型的实例被分配给myItemBase,那么我们可以只需点火并忘记。
在我的情况下,重要的是要知道哪个ItemBase派生类被分配给了myItemBase属性。 ...
public void compute() { if(this.myItemBase is Cancelled) doA(); else if(this.myItemBase is Modified) doB(); else doC(); }
- kkris1983
嗯...你可以通过将this传递给DueToBase中的has,在this.myItemBase.figureWhatToDo()中解决它。如果在figureWhatToDo()上被canceled,则调用myDueToBase.doA(),对于其他情况重复此过程。 - GriffinHeart

1
你可以这样做:
interface Algorithm {
    void executeAlgorithm(DueToBase dueToBase);
}

并且有地图

Map<Class, Algorithm> algorithmMap = new HashMap<Class, Algorithm>();

您将为每个算法拥有一个类。
例如:
/**
  * this class is, for example, when itemBase instanceof Cancelled 
  */
class Algorithm1 implements Algorithm {

    public void executeAlgorithm(DueToBase dueToBase) {
        if ( dueToBase instanceof Provider ) {
            someAlgorithm();  // the algorithm specified somewhere, for provider
        } else if ( dueToBase instanceof Company ) {
            anotherAlgorithm(); // the algorithm or Company.
    }

    // this someAlgorithm() and anotherAlgorithm() are not same for different Algorithm classes
}

在某处你必须构建算法映射表。

algorithmMap.add( Cancelled.class , new Algorithm1() );
algorithmMap.add( Modified.class , new Algorithm2() );
algorithmMap.add( Added.class , new Algorithm3() );

在 doSomething 方法中,您将不必编写 if-else 块。
doSomething() {
    Algorithm algorithm = algorithmMap.get( itemBase.getClass() );

    algorithm.executeAlgorithm(dueToBase);
}

在我看来,这是解决此问题的良好面向对象编程解决方案之一。

如果dueToBase的实例变量很多,那么这个解决方案也会很糟糕。在这种情况下,您可以在doSomething方法中执行相同的逻辑(另一个HashMap和算法)。或者您可以使用责任链模式(Chain of Responsibility),每个处理程序将检查一个dueToBase实例。 - irakli2692

1

the better way will be:

interface Algorithm {

    void executeAlgorithm();

}

需要实现Algorithm接口的类,而不是函数。例如Algorithm1、Algorithm2、Algorithm3等。

还有一个表格:

Algorithm[,] algorithmTable = { { new Algorithm1(), new Algorithm2() },
                                { new Algorithm3(), new Algorithm4() },
                                { new Algorithm5(), new Algorithm6() }
                              };

并且拥有地图

Map< Class<?> , Integer > itemBaseMap = new HashMap<>();

Map< Class<?> , Integer > dueToBaseMap = new HashMap<>();

并在某处构建这些地图

itemBaseMap.add( Canceled.class , 0 );
itemBaseMap.add( Modified.class , 1 );
itemBaseMap.add( Added.class , 2 );

dueToBaseMap.add( Provider.class, 0 );
dueToBaseMap.add( Company.class, 1 );

在doSomething方法中,您可以编写以下内容:
void doSomething( ItemBase itemBase, DueToBase dueToBase ) {
    Integer itemBaseIndex = itemBaseMap.get( itemBase.getClass() );
    Integer dueToBaseIndex = dueToBaseMap.get( dueToBase.getClass() );

    Algorithm algorithm = algorithmTable[ itemBaseIndex, dueToBaseIndex ];

    algorithm.executeAlgorithm();
}

这种方式比我在这里写的更好,因为在这个答案中,算法接口不依赖于dueToBase或任何其他东西。 - irakli2692

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接