

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 解決方案
<a name="java-flow-making-changes-solutions"></a>

您可以使用下列解決方案來避免回溯不相容變更。如需詳細資訊，請參閱「[變更決策者程式碼](java-flow-making-changes-decider-code.md)」和「[範例藍本](java-flow-making-changes-example-scenario.md)」。

## 使用版本控制
<a name="use-versioning"></a>

在此解決方案中，您可以將決策者複製至新類別，並修改決策者，然後在新的工作流程版本下註冊決策者。

`VersionedDecider.java`

```
package sample.v2;
 
import com.amazonaws.services.simpleworkflow.flow.DecisionContext;
import com.amazonaws.services.simpleworkflow.flow.DecisionContextProviderImpl;
import com.amazonaws.services.simpleworkflow.flow.WorkflowClock;
import com.amazonaws.services.simpleworkflow.flow.annotations.Execute;
import com.amazonaws.services.simpleworkflow.flow.annotations.Workflow;
import com.amazonaws.services.simpleworkflow.flow.annotations.WorkflowRegistrationOptions;
 
import sample.Input;
 
@Workflow
@WorkflowRegistrationOptions(defaultExecutionStartToCloseTimeoutSeconds = 60, defaultTaskStartToCloseTimeoutSeconds = 5)
public interface Foo {
 
    @Execute(version = "2")
    public void sample(Input input);
 
    public static class Impl implements Foo {
 
        private DecisionContext decisionContext = new DecisionContextProviderImpl().getDecisionContext();
        private WorkflowClock clock = decisionContext.getWorkflowClock();
 
        @Override
        public void sample(Input input) {
            System.out.println("Decision (V2) WorkflowId: " + decisionContext.getWorkflowContext().getWorkflowExecution().getWorkflowId());
            clock.createTimer(5);
        }
 
    }
 
}
```

在更新過的 Java 程式碼中，第二個決策者工作者會同時執行兩個版本的工作流程，讓進行中的執行得以單獨繼續，無關乎第 `2` 版中的變更。

`RunVersionedDecider.java`

```
package sample;
 
import com.amazonaws.services.simpleworkflow.flow.WorkflowWorker;
 
public class VersionedChange extends SampleBase {
 
    public static void main(String[] args) throws Exception {
        new VersionedChange().run();
    }
 
    public void run() throws Exception {
        // Start the first version of the decider, with workflow version 1
        WorkflowWorker before = new WorkflowWorker(service, domain, taskList);
        before.addWorkflowImplementationType(sample.v1.Foo.Impl.class);
        before.start();
 
        // Start a few executions with version 1
        startFiveExecutions("Foo.sample", "1", new Input());
 
        // Stop the first decider worker and wait a few seconds 
        // for its pending pollers to match and return
        before.suspendPolling();
        sleep(2000);
 
        // At this point, three executions are still open, with more decisions to make
 
        // Start a worker with both the previous version of the decider (workflow version 1) 
        // and the modified code (workflow version 2)
        WorkflowWorker after = new WorkflowWorker(service, domain, taskList);
        after.addWorkflowImplementationType(sample.v1.Foo.Impl.class);
        after.addWorkflowImplementationType(sample.v2.Foo.Impl.class);
        after.start();
 
        // Start a few more executions with version 2
        startFiveExecutions("Foo.sample", "2", new Input());
 
        printExecutionResults();
    }
 
}
```

當您執行程式時，所有執行會順利完成。

## 使用功能標記
<a name="use-feature-flags"></a>

回溯相容性問題的另一種解決方案是分支處理程式碼，以支援根據輸入資料 (而非工作流程版本) 來分支處理相同類別中的兩個實作。

當您採取這種方法時，可以在每次引進敏感變更時將欄位新增至輸入物件 (或修改輸入物件的現有欄位)。針對在遷移之前啟動的執行，輸入物件不會有欄位 (或有不同值)。因此，您不需要增加版本編號。

**注意**  
如果您新增欄位，則請確定 JSON 還原序列化程序具有回溯相容。引進欄位之前序列化的物件仍然應該在遷移之後成功還原序列化。因為 JSON 只要欄位遺漏就會設定 `null` 值，所以一律會使用已封箱的類型 (`Boolean`，而非 `boolean`)，並處理值為 `null` 的情況。

`FeatureFlagDecider.java`

```
package sample.v1.featureflag;
 
import com.amazonaws.services.simpleworkflow.flow.DecisionContext;
import com.amazonaws.services.simpleworkflow.flow.DecisionContextProviderImpl;
import com.amazonaws.services.simpleworkflow.flow.WorkflowClock;
import com.amazonaws.services.simpleworkflow.flow.annotations.Execute;
import com.amazonaws.services.simpleworkflow.flow.annotations.Workflow;
import com.amazonaws.services.simpleworkflow.flow.annotations.WorkflowRegistrationOptions;
 
import sample.Input;
 
@Workflow
@WorkflowRegistrationOptions(defaultExecutionStartToCloseTimeoutSeconds = 60, defaultTaskStartToCloseTimeoutSeconds = 5)
public interface Foo {
 
    @Execute(version = "1")
    public void sample(Input input);
 
    public static class Impl implements Foo {
 
        private DecisionContext decisionContext = new DecisionContextProviderImpl().getDecisionContext();
        private WorkflowClock clock = decisionContext.getWorkflowClock();
 
        @Override
        public void sample(Input input) {
            System.out.println("Decision (V1 feature flag) WorkflowId: " + decisionContext.getWorkflowContext().getWorkflowExecution().getWorkflowId());
            clock.createTimer(5);
            if (!input.getSkipSecondTimer()) {
                clock.createTimer(5);
            }
        }
 
    }
}
```

在更新過的 Java 程式碼中，仍然會針對第 `1` 版註冊兩個工作流程版本的程式碼。不過，在遷移之後，會啟動輸入資料的 `skipSecondTimer` 欄位設為 `true` 的新執行。

`RunFeatureFlagDecider.java`

```
package sample;
 
import com.amazonaws.services.simpleworkflow.flow.WorkflowWorker;
 
public class FeatureFlagChange extends SampleBase {
 
    public static void main(String[] args) throws Exception {
        new FeatureFlagChange().run();
    }
 
    public void run() throws Exception {
        // Start the first version of the decider
        WorkflowWorker before = new WorkflowWorker(service, domain, taskList);
        before.addWorkflowImplementationType(sample.v1.Foo.Impl.class);
        before.start();
 
        // Start a few executions
        startFiveExecutions("Foo.sample", "1", new Input());
 
        // Stop the first decider worker and wait a few seconds 
        // for its pending pollers to match and return
        before.suspendPolling();
        sleep(2000);
 
        // At this point, three executions are still open, with more decisions to make
 
        // Start a new version of the decider that introduces a change 
        // while preserving backwards compatibility based on input fields
        WorkflowWorker after = new WorkflowWorker(service, domain, taskList);
        after.addWorkflowImplementationType(sample.v1.featureflag.Foo.Impl.class);
        after.start();
 
        // Start a few more executions and enable the new feature through the input data
        startFiveExecutions("Foo.sample", "1", new Input().setSkipSecondTimer(true));
 
        printExecutionResults();
    }
 
}
```

當您執行程式時，所有執行會順利完成。