【问题标题】:class/interface design approach for given scenario给定场景的类/接口设计方法
【发布时间】:2014-01-21 14:22:30
【问题描述】:

我有 10-15 种不同类型的结果来计算 AResult、BResult 等。所有结果计算都返回单一类型的值(考虑一个 integer 值)。 为了计算这些,我需要输入参数,这些参数大多是常见的,但有一些需要不同的参数,如下所示:

Input parameters example:
    AResult needs  int x, int y, int a[], int b[]
    BResult needs  int x, int y, int a[], boolean c
    CResult needs  int x, int y, int a[], boolean c, boolean d
    DResult needs  int x, int y, int p[], boolean e
    .....

注意,在某些情况下,一个计算的结果也会用于其他结果计算,但这只是少数情况,而不是全部。

我的做法是通过Approach1如下:

方法一:

我正在定义一个接口,该接口具有单一方法来计算采用InputParameter obj 的结果。在实现类中,我将从InputParameter obj 中提取适用于该特定类型结果的值。这是我的示例代码的 sn-p:

public interface Result {
    int calculate(InputParameter obj);
}

public class AResult implements Result {
    public int calculate(InputParameter obj){
      // code
     }

}

public class BResult implements Result {
    public int calculate(InputParameter obj)){
      // code
     }


}

public class InputParameter {
    int x;
    int y;
    int z;
    int a[];
    int b[];
    int p[];
    boolean c;
    boolean d;
    boolean e;
    ....
    // Getters and Setters
}

例如,假设我创建了一个InputParameter 对象并设置其中所有字段的值。我将此对象传递给 aResultObject.calculate(InputParameter obj) 、 dResultObject.calculate(InputParameter obj) 等等。

AResult 需要我从InputParameter obj 提取的int x, int y, int a[], int b[] 进行计算并返回结果。

DResult 需要int x, int y, int p[], boolean e,我从同一个InputParameter obj 中提取它来进行计算并返回结果。

注意,我可能需要做其他组的结果计算,所以可能会创建InputParameterAResultBResult 等多个对象。只是想表达InputParameter 不必是单例的。


方法2:

拥有不带任何参数的 calculate() 方法并在各个 Result 类构造函数中设置这些值。

正如@Duncan 指出的Approach1 的一个缺点是,在这里我可能会错过在InputParameter 对象中为给定结果计算设置必填字段。而当我实际尝试在calculation(InputParameter obj) 方法中从InputParameter 对象中提取值时,我发现它没有设置。

public interface Result {
   int calculate();
}

public class AResult implements Result {
   public AResult(int x, int y, int a[], int b[]) {
      //set input params
   }
   public int calculate() {
      //calculate and return AResult
   }
}

方法3:

为 Result 创建单个类(或可能只是 2-3 个类),然后使用单独的方法来计算不同的结果。

我也在考虑这种方法,因为在某些情况下,一个计算的结果也会用于其他结果计算(但这只是在少数情况下,而不是全部)。

public class Result {
   // various fields...

   int calculateResultA(){...}
   int calculateResultB(){...}
   int calculateResultC(){...}
}

上面提到的Approach1是否适合给定的情况?还是其他替代品更好?

【问题讨论】:

  • 我现在想知道我的回答是否混淆了问题。输入数据总是相同的,但不同的“结果生产者”对输入数据做不同的事情吗?如果是这样,也许您的原始设计是相当合理的。
  • 正如我在问题中提到的,计算AResult 的输入数据可能与其他结果(比如BResult)几乎没有共同之处,但可能需要比BResult 更多的输入参数。这同样适用于我要计算的各种结果。因此,基本上计算不同结果所需的输入并不完全相同。现在清楚了吗,还是我应该举例说明一下?
  • “int x;int y;int z;int a[];int b[];boolean c;boolean d;”的值所有计算都保持不变?
  • @Learner:您能描述一下您的用例吗?我仍然认为可能存在一些误解......你们每个人在 cmets 中所说的“数据相同”是什么意思?您是说所有计算都有 POJO 类 InputParameter 的 SINGLE 实例(可以是单例),还是说“相同”只是指业务解释?请多谈谈你的真实用例。
  • @Learner 我仍然不完全了解您在做什么,因此很难就正确的方法提出建议。有没有可能你可以解释你真正在做什么,而不是抽象地谈论?

标签: java oop design-patterns class-design


【解决方案1】:

我建议使用工厂方法 2。 模式是(可能)command

工厂方法

package blammy.result;

public interface Result
{
    int calculate();
}

package blammy.result.impl;

public class AResult
implements Result;
{
    /*
     * Protected constructor to hinder naughty construction.
    */
    protected AResult(
        int x,
        int y,
        int a[],
        int b[])
    {
        ... blah;
    }

    public int calculate()
    {
        int returnValue;
        returnValue = ... blah.
        return returnValue;
    }
}
public class BResult
implements Result;
{
    /*
     * Protected constructor to hinder naughty construction.
    */
    protected BResult(
        int x,
        int y,
        boolean c)
    {
        ... blah;
    }

    public int calculate()
    {
        int returnValue;
        returnValue = ... blah.
        return returnValue;
    }
}

public static class ResultFactory
{
    public static Result createResult(
        int x,
        int y,
        int a[],
        int b[])
    {
        return new AResult(x, y, a, b);
    }

    public static Result createResult(
        int x,
        int y,
        boolean c)
    {
        return new BResult(x, y, c);
    )
}

编辑 1

如果不同的计算需要相同的参数,那么你应该在工厂和类名中使用描述性名称。例如:

public static class ResultFactory

    public static Result createBlammy(
        int x,
        int y,
        int a[],
        int b[])
    {
        return new ResultBlammy(x, y, a, b);
    }

    public static Result createHooty(
        int x,
        int y,
        int a[],
        int b[])
    {
        return new ResultHooty(x, y, a, b);
    }

【讨论】:

  • 实际上我不能依靠参数的数量或它们的顺序来决定计算哪个结果。那么如何使用不同的工厂方法名称,例如 createAResultcreateBResult 等。这样也可以吗?
【解决方案2】:

在我看来,使用构造函数并定义接口Result 有一个不带参数的方法public int calculate()

免责声明从这里开始,是个人解释几个好的原则。评估它们,如果你不同意就批评它们,不要把它们当作既定的事实(原则真的很好,你的案例的解释可能不是那么好)。

我会尽量避免使用内聚力低的对象,其中内聚力在书Growing object oriented software guided by tests 中定义为

一个元素的凝聚力是衡量其职责是否形成一个有意义的单元的指标。例如,一个同时解析日期和 URL 的类是不连贯的,因为它们是不相关的概念。想想一台既洗衣服又洗盘子的机器——它不太可能同时做好。2 在另一个极端,一个只解析 URL 中的标点符号的类不太可能是连贯的,因为它不能代表一个完整的概念。为了完成任何事情,程序员必须为协议、主机、资源等寻找其他解析器。具有“高”一致性的特征更容易维护。

在你的情况下,我相信InputParameter 有责任为不同的类保存参数,所以在我看来它的责任不是一个有意义的单元(即使它基本上是一个值对象,在某种程度上它违反了Single Responsibility Principle)

【讨论】:

  • 我对我的问题添加了更详细的解释。
  • 我绝对反对你的第三种方法。如果要添加新的calculate 方法,则必须向Result 添加新方法,因此您违反了Open-Closed Principle。正如我所说,根据我在回答中指出的原因(可能并不详尽),我会选择你的approach 2
  • 我在考虑Approach3,因为我认为为什么要不必要地创建 10-15 个类,因为我的结果计算几乎需要相同的输入集,因此可以使用计算这些结果的方法放入同一个类中。
  • 嗯,Approach3 是我更不喜欢的那个。尝试阅读这些Principles of Object Oriented Design 以全面了解我的动机。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-05
相关资源
最近更新 更多