没有通用接口的步骤序列的模式

时间:2014-10-08 06:16:38

标签: java design-patterns

在我的java应用程序中,我得到了一个运行一系列步骤(同步)的方法,其中一步结果是下一步的输入。

例如:

// Step 1
Map<String, SomeObject> objectsMap = someService.createObjectsMap();
if (!objectsMap.isEmpty()) {
    // Step 2
    AnotherObject anotherObject = anotherService.createAnotherObject(objectsMap);
    if (null != anotherObject) {
    // Step 3 that gets anotherObject as input and returns something else
    } else { // Step 2 failed
        // log and handle
    }
} else { // Step 1 failed
    // log and handle
}

所以我在一系列if-else块中编写了这一系列步骤。 由于每个步骤具有不同的签名,因此步骤没有通用接口。我一直在讨论一些不同的问题,并尝试自定义chain-of-responsibilitycommand等模式,但无法获得满意的结果。

我想知道这个丑陋的长if-else部分是否可行,或者是否有一个模式可以帮助使这一系列步骤更加干净和可扩展。

3 个答案:

答案 0 :(得分:5)

你必须回答的一个问题是为什么我要重构我的代码?

你想要它

吗?
  • 更干净的代码?
  • 更模块化?
  • 步骤必须在运行时可配置(可替换)吗?

重构以使代码清洁

如果不需要在运行时配置这些步骤,并且您希望使代码更干净,那么您应该查看您所做的注释。每条评论都是一个提示。

将代码块分解为方法,并在步骤

之后命名它们
/**
 * Explain what step1 does.
 */
private void step1() {
    // Step 1
    Map<String, SomeObject> objectsMap = someService.createObjectsMap();
    if (!objectsMap.isEmpty()) {
        step2(objectsMap);
    } else { // Step 1 failed
        // log and handle
    }
}

/**
 * Explain what step2 does.
 */
private void step2(Map<String, SomeObject> objectsMap) {
    // Step 2
    final AnotherObject anotherObject = anotherService
            .createAnotherObject(objectsMap);
    if (null != anotherObject) {
        step3(anotherObject);
    } else { // Step 2 failed
        // log and handle
    }
}

/**
 * Explain what step3 does.
 */
private void step3(AnotherObject anotherObject) {
    // Step 3 that gets anotherObject as input and returns something
    // else
}

这种方法只是将方法分解为更小的方法。优点是每个较小的方法只负责一件事。因为它是一种方法,你可以添加javadoc。所以不再需要内联评论了。

重构以使步骤在运行时可替换

如果要配置在运行时执行的步骤(例如,由于某些用户输入),则必须将它们封装在对象中,因为应用程序引用了可以替换的对象。

由于你希望所有步骤都有一个共同的api,你必须使它更通用。

从客户的角度开始思考。应该如何执行这些步骤。例如。

for (Step step : steps) {
    boolean executeNext = step.execute();
    if (!executeNext) {
        break;
    }
}

设计步骤界面

public interface Step {
    boolean execute();
}

如何将一步的输出作为输入传递给另一步?

制作界面

public static interface StepInput<T> {
    public T getInput();
}

实施您的步骤。抽象课会帮助你。

public abstract class InputOutputStep<T> implements Step,
        StepInput<T> {

    private T returnValue;

    protected void setReturnValue(T returnValue) {
        this.returnValue = returnValue;
    }

    public T getInput() {
        return returnValue;
    }
}

public class Step1 extends InputOutputStep<Map<String, SomeObject>> {

    private StepInput<Map<String, SomeObject>> stepInput;

    public Step1(StepInput<Map<String, SomeObject>> stepInput) {
        this.stepInput = stepInput;
    }

    public boolean execute() {
        boolean executeNext = false;

        Map<String, SomeObject> objectsMap = stepInput.getInput();
        if (!objectsMap.isEmpty()) {
            // Step 2
            setReturnValue(objectsMap);
            executeNext = true;
        } else { // Step 1 failed
            // log and handle
        }

        return executeNext;
    }
}

public class Step2 extends InputOutputStep<AnotherObject> {

    private StepInput<Map<String, SomeObject>> stepInput;
    private AnotherService anotherService;

    public Step2(AnotherService anotherService,
            StepInput<Map<String, SomeObject>> stepInput) {
        this.anotherService = anotherService;
        this.stepInput = stepInput;
    }

    public boolean execute() {
        boolean executeNext = false;

        Map<String, SomeObject> objectsMap = stepInput.getInput();
        AnotherObject anotherObject = anotherService
                .createAnotherObject(objectsMap);
        if (null != anotherObject) {
            setReturnValue(anotherObject);
            executeNext = true;
        } else { // Step 2 failed
            // log and handle
        }
        return executeNext;
    }
}

public class Step3 extends InputOutputStep<Void> {

    private StepInput<AnotherObject> stepInput;

    public Step3(StepInput<AnotherObject> stepInput) {
        this.stepInput = stepInput;
    }

    public boolean execute() {
        AnotherObject anotherObject = stepInput.getInput();
        setReturnValue(null);
        return false;
    }
}

在运行时配置步骤并执行

Step1 step1 = new Step1(stepInput);
Step2 step2 = new Step2(anotherService, step1);
Step step3 = new Step3(step2);

Step[] steps = new Step[]{step1, step2, step3};

for (Step step : steps) {
    boolean executeNext = step.execute();
    if (!executeNext) {
        break;
    }
}

答案 1 :(得分:0)

这种情况 - 你做了很多事情,想要中止并记录任何一个失败 - 是异常处理的目的。例如:

try {
    // Step 1
    Map<String, SomeObject> objectsMap = someService.createObjectsMap();
    if (objectsMap.isEmpty())
        throw new SomethingWentWrongException("Failed to get object map from service");

    // Step 2
    AnotherObject anotherObject = anotherService.createAnotherObject(objectsMap);
    if(anotherObject == null)
        throw new SomethingWentWrongException("Failed to create another object");

    // Step 3 that gets anotherObject as input and returns something else
} catch(SomethingWentWrongException e) {
    // log and handle
    e.printStackTrace();
}

理想情况下,someService.createObjectsMapanotherService.createAnotherObject会抛出自己的异常,而不是让您检查返回值。然后你只需要写:

try {
    Map<String, SomeObject> objectsMap = someService.createObjectsMap();
    AnotherObject anotherObject = anotherService.createAnotherObject(objectsMap);
    // Step 3 that gets anotherObject as input and returns something else
} catch(Exception e) {
    // log and handle
    e.printStackTrace();
}

(但请注意,如果您真的想要捕捉所有失败,那么您应该只捕获Exception

答案 2 :(得分:-1)

选项:

  1. 纪念品图案: 使用memento,您可以存储对象的状态。就像你想要重做和撤消一样。 这样,当您在步骤1和步骤3中有类似的方法时,您可以简单地使用1种通用方法。 然后,使用撤消和重做,您知道必须保存您的状态。 考虑保存步骤编号的可能性。

  2. 策略模式: 使用策略模式,您将保存IF-ELSE语句。 你只需要去一个功能,战略对象将决定其余的。 将路由器视为策略类。 路由器将确定最佳方式,最佳路由或最佳流程(来自多个流程选项)。

  3. 观察者模式: 这就像MVC。 我一直认为观察者是CCTV。 当一些事情发生变化,一些奇怪的事情发生时,央视管理员就会知道。 所以你有一个控制器类,它监视所有内容,并配置你必须去的地方。

  4. 谢谢,