假设我们有这样的代码(和你的一样):
public void decide(String someCondition) {
if(someCondition.equals("conditionOne")) {
someMethod("someParameter");
}
else if(someCondition.equals("conditionTwo")) {
someMethod("anotherParameter");
}
else {
someMethod("elseParameter");
}
}
假设您不想重构应用程序的其他部分并且您不想更改方法签名,那么可以通过多种方式对其进行重构:
警告 - 您应该使用上述模式的通用版本。
我展示了非通用的,因为它更容易阅读。
策略 + 工厂方法
我们可以使用策略和工厂方法模式。我们还利用了多态性。
private final StrategyConditionFactory strategyConditionFactory = new StrategyConditionFactory();
public void decide(String someCondition) {
Strategy strategy = strategyConditionFactory.getStrategy(someCondition)
.orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
strategy.apply();
}
最好将其设计为工厂中包含 else 条件,开发人员故意调用它。在这种情况下,当条件不满足时,我们会抛出异常。或者,我们可以完全按照所讨论的那样编写它。如果你想这样而不是.orElseThrow(() -> new IllegalArgumentException("Wrong condition")); 放.orElse(new ElseStrategy());
StrategyConditionFactory(工厂方法):
public class StrategyConditionFactory {
private Map<String, Strategy> conditions = new HashMap<>();
public StrategyConditionFactory() {
conditions.put("conditionOne", new ConditionOneStrategy());
conditions.put("conditionTwo", new ConditionTwoStrategy());
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new ElseStrategy());
//...
}
public Optional<Strategy> getStrategy(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
策略界面:
public interface Strategy {
void apply();
}
实现:
public class ConditionOneStrategy implements Strategy {
@Override
public void apply() {
//someMethod("someParameter");
}
}
public class ConditionTwoStrategy implements Strategy {
@Override
public void apply() {
//someMethod("anotherParameter")
}
}
public class ElseStrategy implements Strategy {
@Override
public void apply() {
//someMethod("elseParameter")
}
}
用法(简体):
public void strategyFactoryApp() {
//...
decide("conditionOne");
decide("conditionTwo");
decide("conditionElse");
//...
}
策略 + 工厂方法 - 这种特殊情况(仅参数更改)
我们可以利用这样一个事实,在这种情况下,我们总是调用相同的方法,只是参数更改
我们使用 getParameter() 方法将基本策略接口更改为抽象类,并对这个抽象类进行新的实现。其他代码保持不变。
public abstract class Strategy {
public abstract String getParameter();
public void apply() {
someMethod(getParameter());
}
private void someMethod(String parameter) {
//someAction
}
}
实现:
public class CondtionOneStrategy extends Strategy {
@Override
public String getParameter() {
return "someParameter";
}
}
public class CondtionTwoStrategy extends Strategy {
@Override
public String getParameter() {
return "anotherParameter";
}
}
public class ElseStrategy extends Strategy {
@Override
public String getParameter() {
return "elseParameter";
}
}
枚举 + 枚举有点“工厂”
我们可以使用 Enum 来实现策略,而不是工厂方法,我们可以使用 enum 中的 valueOf()。
public void decide(String someCondition) {
ConditionEnum conditionEnum = ConditionEnum.valueOf(someCondition);
conditionEnum.apply();
}
条件枚举:
public enum ConditionEnum {
CONDITION_ONE {
@Override
public void apply() {
//someMethod("someParameter");
}
},
CONDITION_TWO {
@Override
public void apply() {
//someMethod("anotherParameter");
}
},
CONDITION_ELSE {
@Override
public void apply() {
//someMethod("elseParameter");
}
};
//...more conditions
public abstract void apply();
}
用法(简体):
public void enumFactoryApp() {
//...
decide("CONDITION_ONE");
decide("CONDITION_TWO");
decide("CONDITION_ELSE");
//...
}
请注意,当枚举类型没有具有指定名称的常量时,您将获得IllegalArgumentException。
命令 + 工厂
策略和命令之间的区别在于命令也保存状态,所以如果你有例如 compute(int a, int b, String someCondition) 并且你想用策略重构它,包括它的签名更改,你可以将它减少到 compute(int a, int b, ComputeStrategy computeStrategy) 使用命令,您可以将其简化为一个参数计算(ComputeCommand computeCommand)。在这种情况下,我们还利用了类似于策略模式案例的多态性。
CommandConditionFactory commandConditionFactory = new CommandConditionFactory();
public void decide(String someCondition) {
Command command = commandConditionFactory.getCommand(someCondition)
.orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
command.apply();
}
最好将其设计为工厂中包含 else 条件,开发人员故意调用它。在这种情况下,当条件不满足时,我们会抛出异常。或者,我们可以完全按照所讨论的那样编写它。如果你想这样而不是.orElseThrow(() -> new IllegalArgumentException("Wrong condition")); 放.orElse(new ElseCommand());
CommandConditionFactory(工厂方法):
public class CommandConditionFactory {
private Map<String, Command> conditions = new HashMap<>();
public CommandConditionFactory() {
conditions.put("conditionOne", new ConditionOneCommand("someParameter"));
conditions.put("conditionTwo", new ConditionTwoCommand("anotherParameter"));
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new ElseCommand("elseParameter"));
//...
}
public Optional<Command> getCommand(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
命令界面:
public interface Command {
void apply();
}
实现(有一些冗余,但它显示了在更一般的情况下命令应该如何看待,而不是 someMethod() 我们有三种不同的方法):
public class ConditionOneCommand implements Command {
private final String parameter;
public ConditionOneCommand(String parameter) {
this.parameter = parameter;
}
@Override
public void apply() {
//someMethod(parameter);
}
}
public class ConditionTwoCommand implements Command {
private final String parameter;
public ConditionTwoCommand(String parameter) {
this.parameter = parameter;
}
@Override
public void apply() {
//someMethod(parameter);
}
}
public class ElseCommand implements Command {
private final String parameter;
public ElseCommand(String parameter) {
this.parameter = parameter;
}
@Override
public void apply() {
//someMethod(parameter);
}
}
用法(简体):
public void commandFactoryApp() {
//...
decide("conditionOne");
decide("conditionTwo");
decide("conditionElse");
//...
}
Command + Factory - 这种特殊情况。
这实际上不是一个真正的命令模式,只是一个衍生物。它利用了这样一个事实,在这种情况下,我们总是调用同一个方法 someMethod(parameter) 并且只有参数发生变化。
抽象类:
public abstract class Command {
abstract void apply();
protected void someMethod(String parameter) {
//someAction
}
}
实现(所有 3 个条件案例都相同):
public class CommandImpl extends Command {
private final String parameter;
public CommandImpl (String parameter) {
this.parameter = parameter;
}
@Override
public void apply(){
someMethod(parameter);
}
}
工厂,请注意只有一个命令实现,只有参数变化:
public class CommandConditionFactory {
Map<String, Command> conditions = new HashMap<>();
public CommandConditionFactory() {
conditions.put("conditionOne", new CommandImpl("someParameter"));
conditions.put("conditionTwo", new CommandImpl("anotherParameter"));
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new CommandImpl("elseParameter"));
//...
}
public Optional<Command> getCommand(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
嵌套 if's
请注意,即使您嵌套了 if,有时也可以重构它们并使用上述技术之一。
假设我们有以下代码:
public void decide2(String someCondition, String nestedCondition) {
if(someCondition.equals("conditionOne")) {
if(nestedCondition.equals("nestedConditionOne")){
someLogic1();
}
else if(nestedCondition.equals("nestedConditionTwo")){
someLogic2();
}
}
else if(someCondition.equals("conditionTwo")) {
if(nestedCondition.equals("nestedConditionThree")){
someLogic3();
}
else if(nestedCondition.equals("nestedConditionFour")){
someLogic4();
}
}
}
您可以使用数学逻辑规则对其进行重构:
public void decide2(String someCondition, String nestedCondition) {
if(someCondition.equals("conditionOne")
&& nestedCondition.equals("nestedConditionOne")) {
someLogic1();
}
else if(someCondition.equals("conditionOne")
&& nestedCondition.equals("nestedConditionTwo")) {
someLogic2();
}
else if(someCondition.equals("conditionTwo")
&& nestedCondition.equals("nestedConditionThree")) {
someLogic3();
}
else if(someCondition.equals("conditionTwo")
&& nestedCondition.equals("nestedConditionFour")) {
someLogic4();
}
}
然后你可以使用策略、枚举或命令。您只有一对字符串 而不是单个字符串。
决策表
当您嵌套了如前所述无法重构的 if 时,您可以实现自己的决策表或使用一些准备就绪的决策表解决方案。我不会在那里给出实现。
规则引擎
当您嵌套了如前所述无法重构的 if 时,您还可以实现自己的简单规则引擎。只有当你有许多嵌套的 if 时才应该使用它,否则它就是形式战胜内容。
对于非常复杂的业务逻辑,有像 Drools 这样的专业规则引擎。
我不会在那里给出实现。
还有一件事
在您给出的示例中,很可能有人引入了这些 if,但它们完全是多余的。我们可以通过尝试重构确定方法签名以使其接受其他参数并重构调用我们方法的周围代码来检查它。通过这样做,我们摆脱了我们的工厂方法。有一些示例展示了当这些 if 是多余的情况下代码的外观。
策略
决定方法:
public void decide(Strategy strategy) {
strategy.apply();
}
用法(简体):
public void strategyApp() {
//...
decide(new ConditionOneStrategy());
decide(new ConditionTwoStrategy());
decide(new ElseStrategy());
//...
}
枚举
决定方法:
public void decide(ConditionEnum conditionEnum) {
conditionEnum.apply();
}
用法(简体):
public void enumApp() {
//...
decide(ConditionEnum.CONDITION_ONE);
decide(ConditionEnum.CONDITION_TWO);
decide(ConditionEnum.CONDITION_ELSE);
//...
}
命令
决定方法:
public void decide(Command command) {
command.apply();
}
用法(简体):
public void commandApp() {
//...
decide(new ConditionOneCommand("someParameter"));
decide(new ConditionTwoCommand("anotherParameter"));
decide(new ElseCommand("elseParameter"));
//...
}
事实上这是非常特殊的情况,在某些情况下,例如我们必须使用像 String 这样的简单类型,因为它来自外部系统或条件基于输入的整数,因此我们无法重构代码这么简单。