【问题标题】:Template method with return value带返回值的模板方法
【发布时间】:2021-06-21 18:05:58
【问题描述】:

我有一个算法大纲 - 一些必须按特定顺序执行的逻辑步骤。算法的结果必须是某个数字。这自然让我想到了使用模板方法模式。这适用于void 方法,但我的问题来了:算法中的每个步骤都不是void 方法,但允许返回一个数字(所以它们是int 方法) - 如果一个步骤返回一个非零数,这个数是算法执行的结果,如果是零 - 继续执行下一步。

这听起来可能真的微不足道,但我仍然觉得有这样的东西有点难看:

public int algorithm() {
    int resultStep1 = step1();
    if (resultStep1!=0) {
        return resultStep1;
    }
    int resultStep2 = step2();
    if (resultStep2!=0) {
        return resultStep2;
    }
    ...
}

当然step1()step2()等都是抽象方法,在我扩展的相应类中都有自己的具体实现。

我想到的另一个想法是使用异常,但是当我们在这里讨论控制流时,这将是一种反模式。

我在这里遗漏了什么,还是我必须这样写?

【问题讨论】:

  • 我在问题的一半停止了理解。你想返回第一个非零步,真正的问题是什么
  • Iterable 怎么样?您可以将 next() 写入您的 steps() ,它不会返回下一步,而是返回结果,如果它不为零。全部紧密成一个整洁的循环。
  • @RoyalBg 当某些步骤返回非零数字时,这就是算法的返回值,否则继续执行 N 步骤并返回其他东西作为最后的手段
  • 你可以看看ComparisonChain from Google Guava,它正在做你想做的事情。

标签: java algorithm template-method-pattern


【解决方案1】:

Java 7

您可以为您的步骤定义一个界面:

interface Step {
    int step();
}

然后使用步骤列表:

ArrayList<Step> steps = new ArrayList<Step>();

像这样迭代它:

public int algorithm() {
    for (Step step : steps) {
        int result = step.step();
        if (result != 0)
            return result;
    }
    return 0;
}

要初始化该列表,您可以使用匿名实现类来执行此操作:

steps.add(new Step() {
    @Override
    public int step() {
        return step1(); //or the code directly
    }
});

steps.add(new Step() {
    @Override
    public int step() {
        return step2();
    }
});

或者为每个步骤创建命名良好的实现类:

public class Step1 implements Step {
    @Override
    public int step() {
        // TODO Auto-generated method stub
        return 0;
    }       
}

并像这样添加到列表中:

steps.add(new Step1());
steps.add(new Step2());

在 Java8 中使用 lambda

不需要接口。

列表:

ArrayList<Supplier<Integer>> steps = new ArrayList<Supplier<Integer>>();

设置:

steps.add(()-> step1());
steps.add(()-> step2());

算法:

public int algorithm() {
    for (Supplier<Integer> step : steps) {
        int result = step.get();
        if (result != 0)
            return result;
    }
    return 0;
}

【讨论】:

  • 这需要做很多工作,因为有很多 step()s
  • + 如果你使用 java8,则使用 lambda 而不是匿名
  • @user2504380 很好,我只是想安装 Java 8 来做到这一点。我是 Android 程序员,所以我还不需要!
  • @weston 我认为step() 可能需要将上下文作为参数传递,因为原始步骤在同一个类中完成,您可以将上下文保留为局部变量,但是如果您开始使用外部类,您可能希望将上下文传递给每个步骤(由前面的步骤更新)。
  • @FlorentBayle 很可能,但我不想过于复杂。但是如果需要,可以使接口中的step 方法接受参数,并且可以使Java 8 lambda 列表为Function&lt;Integer, ArgType&gt;。而不是无参数的Supplier&lt;Integer&gt;
【解决方案2】:

您可以执行以下操作:

if(setResultAndCheckIsNonZero(step1())) {
    return result;
} else if(setResultAndCheckIsNonZero(step2())) {
    return result;
} else if ...

地点:

private int result;

private boolean setResultAndCheckIsNonZero(int x) {
    result = x;
    if(result != 0) 
        return true;
    return false;
}

【讨论】:

  • 这使用了“副作用”。名为is... 的方法不仅仅是一个测试,它设置了一个字段。这有点恶心。
  • 修复名称是一种方法。如果你调用了setResultAndCheckIsNonZero这个方法,那么副作用就很明显了。
  • 我认为设置一个字段非常棒,因为我们在这里讨论的是同一个类,它已经加载到内存中。
  • 我在这里所做的只是一个例子。 OP 可以将其重命名为他/她喜欢的任何名称
  • 好的,是的,OP 可以为所欲为。但如果想要变得更好,请自学副作用:en.wikipedia.org/wiki/Side_effect_%28computer_science%29
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多