【问题标题】:How to implement pattern matching in java and avoid instanceof?如何在java中实现模式匹配并避免instanceof?
【发布时间】:2020-01-10 05:38:39
【问题描述】:

目前我不知道如何避免我的代码中出现代码异味。 我尝试了几种模式(策略、访问者),但它们没有提供干净且可维护的解决方案。这是我的策略模式代码示例:

public interface Strategy {
  <T> T foo(FirstParam firstParam, SecondParam secondParam);
}

public class StrategyOne implements Strategy {
  FirstReturnType foo(FirstParam firstParam, SecondParam secondParam);
}

public class StrategyTwo implements Strategy {
  SecondReturnType foo(FirstParam firstParam, SecondParam secondParam);
}

@Setter
public class Context {
    private Strategy strategy;
    public void execute(FirstParam firstParam, SecondParam secondParam) {
        if (strategy != null) {
            strategy.fo(firstParam, secondParam);
        }
    }
}

还有一个对象的例子。

public abstract class Action {
 abstract void bar();
} 

public class ActionOne extends Action {
  void bar() {}
}

public class ActionTwo extends Action {
  void bar() {}
}

我想让这段代码更干净

public class ActionExecutor {
   private Context context;
   private FirstParam firstParam;
   private SecondParam secondParam;
   public ActionExecutor(FirstParam firstParam, SecondParam secondParam) {
    this.context = new Context();
    this.firstParam = firstParam;
    this.secondParam = secondParam;
   }

  public void doSmth(Item item) {
    Action action = item.getAction();
    if(action instanceof ActionOne) {
     context.setStrategy(new StrategyOne());
    }
    if(action instanceof ActionTwo) {
     context.setStrategy(new StrategyTwo());
    }
    context.execute(firstParam, secondParam);
  }
}

这个想法是为特定的对象类型执行特定的操作。但我不知道如何避免在这种情况下使用 instanceof。

【问题讨论】:

  • 这个人知道设计模式和泛型,但不足以正确使用它们。当您将泛型划分为 FirstReturnTypeSecondReturnType 时,使用泛型有什么意义。看看 Strategy Pattern,你的 ActionExecutor 应该是这样的。你应该编程接口而不是类。
  • 其实Action Executor是一个业务逻辑类,不能成为Strategy Pattern的一部分。我使用泛型是因为这些方法有不同的返回类型。
  • 也许你可以通过 1) 解释你为什么不喜欢使用 instanceof 来改进你的问题 2) 提到你不能修改 Action 类
  • 我会避免使用 instanceof,因为它不是一个可扩展且清晰的解决方案。如果Action 类型的数量增加,我应该添加另一个if (action instanceof NewType),这很混乱。
  • @privalou 你收到一个动作,根据动作是什么,你需要设置上下文。动作本身能够设置上下文是有意义的。

标签: java if-statement design-patterns polymorphism instanceof


【解决方案1】:

我的头顶有两种方式。

public abstract class Action {
 public Strategy strategy;
 abstract void bar();
} 

public class ActionOne extends Action {
  void bar() {}
   // set strategy here,  possibly
}

public class ActionTwo extends Action {
  void bar() {}
}

public void doSmth(Item item) {
    Action action = item.getAction();
    action.strategy.execute(firstParam, secondParam);
  }

第二种方式,在你的所有动作中都有一个枚举,并通过在你的抽象类构造函数中将它声明为参数来强制它。然后只需使用 switch in 而不是 instanceof

【讨论】:

  • 我想将战略付诸行动会增加耦合。这就是我试图在我的代码中避免的。这就是我拒绝访问者模式的原因,因为这种方法会使我的 Action 类变得过于具体和沉重。
  • 不是真的,它只是一个接口而不是强类型,所以无论如何它都不是紧耦合。
  • 嗯,问题是,我无法修改 Action 类,因为它位于另一个模块中,而我只是它的消费者。我可能会在里面创建一个带有 StrategyPattern 的新抽象类,但它会迫使我将一个传入对象强制转换为我的新类。但我希望这不会是一个问题:)
  • 你能告诉我如何优雅地设置每个Action 特定的策略吗?
【解决方案2】:

可能是这样的:

   public void doSmth(Item item) {

    Action action = item.getAction();

    Map<String,Strategy> strategies = new HashMap<>();
    strategies.put(ActionOne.getClass().getSimpleName(),new StrategyOne());
    strategies.put(ActionTwo.getClass().getSimpleName(),new StrategyTwo());
    ..
    strategies.put(ActionHundred.getClass().getSimpleName(),new StrategyHundred());

    if(strategies.containsKey(action.getClass().getSimpleName())) {
     context.setStrategy(strategies.get(action.getClass().getSimpleName()));
    }
    context.execute(firstParam, secondParam);  }

【讨论】:

  • 我认为用这种方法完成地图并不值得。但我不知道在哪里可以做到这一点。
  • 为什么不呢?如果您只有 2-3 种策略类型,则只需保留“instanceOf”,如果更多 - 肯定需要一些映射,如我的示例中所示。
  • 因为对我来说它看起来不是最佳解决方案。但我会尝试类似的方法:)
  • 这里的问题与使用 instanceof 的原始解决方案相同。如果添加了新的操作类型,则不会出现编译时错误。只有在运行时才会注意到未添加检查新操作的失败。
  • 缺少大小写的编译器错误?没有外部工具是不可能的。
【解决方案3】:

这似乎是Factory Method 模式的教科书用例。您可以使用您现在拥有的相同代码(或另一个答案中的 Map 示例),但将其放在工厂中 - 然后它是针对特定目的的,并且与使用它的代码分离。

类似的东西。

public class StrategyFactory {

    public static Stategy getStrategy(Action action) {
        if(action instanceof ActionOne) {
            return new StrategyOne();
        } else if(action instanceof ActionTwo) {
            return new StrategyTwo();
        }
    }
}

然后,像这样。

Action action = item.getAction();
action.setStrategy(StrategyFactory.getStrategy(action));

这里还有一个例子:https://dzone.com/articles/design-patterns-the-strategy-and-factory-patterns

【讨论】:

  • 工厂方法永远不是静态的。据我所知,任何 GoF 模式中都没有静态方法。
  • @jaco0646 静态工厂方法是一回事。 Joshua Bloch 在他的《Effective Java》一书中建议使用它们来代替构造函数。是GoF模式吗?没关系,这仍然是一个解决方案。
  • @VinceEmigh,我同意所有这些。我的观点是,工厂方法和静态工厂方法是两个完全独立的模式。不幸的是,由于他们的名字,这两者经常混为一谈。这个答案发表了关于工厂方法的声明,但随后实现了静态工厂方法。这令人困惑。
猜你喜欢
  • 2015-06-10
  • 1970-01-01
  • 2011-09-03
  • 2013-03-05
  • 2020-09-08
  • 1970-01-01
  • 1970-01-01
  • 2013-03-25
相关资源
最近更新 更多