根据枚举值执行代码的最佳方式 已编辑:原文错误:

问题描述

以下代码是我的问题的示例。 我想简化代码,而不必在不同的 switch 语句上重复调用相同的方法

public void simulate(String given,Status status) {
    switch (status){
        case A:
            simulateA(given);
            break;
        case B:
            simulateA(given);
            simulateB(given);
            break;
        case C:
            simulateA(given);
            simulateB(given);
            simulateC(given);
            break;
    }

PS 1:调用方法的顺序很重要!

PS 2:我不是在寻找进行切换的另一种方式,而是在寻找对问题进行建模的另一种方式,也许在方法中使用某种类组合。

解决方法

假设您的第一个 status 是 A,您可以执行以下操作:

public void simulate(String given,Status status) {
   if (status != Status.A) {
     int indexOfStatus = status.ordinal();
     simulate(given,Status.values()[indexOfStatus - 1]);
   }
    
   switch (status){
      case A:
        simulateA(given);
        break;
      case B:
        simulateB(given);
        break;
      case C:
        simulateC(given);
        break;
    // here you still need to put all your "simulateX" calls but without repetitions
   }
}
,

我不知道您的 enum 的性质,但是如果您有很多模拟调用,您可以放弃 switch 语句并这样做。但是您目前的方法没有任何问题。如果您的方法是静态的而不是实例,这也会略有变化。这样做的一个优点是它具有扩展的潜力。

还有很多其他方法可以做到这一点。您可以有一个方法引用列表,enum 参数可以是通过索引调用哪些方法的变量数组。

public class Simulations {
    
    static List<BiConsumer<Simulations,String>> sims =
            List.of(Simulations::simulateA,Simulations::simulateB,Simulations::simulateC);
    
    enum Status {
        A(1),B(2),C(3);
        private int val;
        private Status(int v) {
            this.val = v;
        }
        public int getVal() {
            return val;
        }
    }
    
    public static void main(String[] args) {
        Simulations simulation = new Simulations();
        simulation.simulate("A",Status.A);
        System.out.println();
        simulation.simulate("B",Status.B);
        System.out.println();
        simulation.simulate("C",Status.C);
    }
    
    public void simulate(String given,Status status) {
        for (int i = 0; i < status.getVal(); i++) {
            sims.get(i).accept(this,given);
        }
    }
    
    public void simulateA(String s) {
        System.out.println(s);
    }
    
    public void simulateB(String s) {
        System.out.println(s);
    }
    
    public void simulateC(String s) {
        System.out.println(s);
    }
    
}
,

在这种情况下,模拟的顺序总是“向下”级联,例如模拟 B 就是模拟 A 加上一些额外的。这匹配继承模式,例如哺乳动物是一种带有一些额外功能的动物。因此,让模拟相互继承可以修复模式:

.git/

请注意,这很脆弱,因为所有继承树都是如此。另一种解决方案可以通过组合和委托来实现。这称为链:

interface Simulation
{
    void simulate( final String given );
}

class ASimulation implements Simulation
{
    @Override
    public void simulate( String given )
    {
        // simulate this given!
    }
}

class BSimulation extends ASimulation
{
    @Override
    public void simulate( String given )
    {
        super.simulate( given );
        // simulate this given some more!
    }
}

class CSimulation extends BSimulation
{
    @Override
    public void simulate( String given )
    {
        super.simulate( given );
        // simulate this given even more!
    }
}

要实例化链,请使用以下顺序:

class LeafSimulation
        implements Simulation
{
    @Override
    public void simulate( String given )
    {
        // simulate this given!
    }
}

class ChainedSimulation
        implements Simulation
{
    private final Simulation delegate;

    ChainedSimulation( final Simulation delegate )
    {
        this.delegate = delegate;
    }

    @Override
    public void simulate( String given )
    {
        delegate.simulate( given );
        // simulate this given some more!
    }
}

这段代码更自然地接近问题陈述并消除了重复,但并不简洁。

,

设置状态值到方法调用的映射后,您可以使用 SortedSetEnumSet.range 获取特定值后的枚举值:

Map<Status,Consumer<String>> simulators = new EnumMap<>(Map.of(
    Status.A,this::simulateA,Status.B,this::simulateB,Status.C,this::simulateC));

if (!simulators.keySet().equals(EnumSet.allOf(Status.class))) {
    throw new RuntimeException(
        "Not all Status values have simulators defined.");
}

// ...

SortedSet<Status> all = new TreeSet<>(EnumSet.allOf(Status.class));
Collection<Status> remainingValues = all.tailSet(status);

// Or:
//Status[] allStatuses = Status.values();
//Status lastStatus = allStatuses[allStatuses.length - 1];
//Collection<Status> remainingValues = EnumSet.range(status,lastStatus);

for (Status s : remainingValues) {
    simulators.get(s).accept(given);
}
,

要考虑的另一个选项,它避免了 switch / if。声明每个 Status 值的操作映射,可与未处理值的 getOrDefault 查找默认值一起使用:

Consumer<String> simA = this::simulateA;
Map<Status,Consumer<String>> actions = new EnumMap<>(Map.of(
        Status.A,simA,simA.andThen(this::simulateB),simA.andThen(this::simulateB).andThen(this::simulateC)
));
actions.getOrDefault(status,s -> {}).accept(given);

如果您想防止丢失/未处理的映射,您应该验证映射(如@VGR 答案)或用异常处理程序交换无操作默认值:

actions.getOrDefault(status,s -> { throw new RuntimeException("Missing action for status: "+status); }
).accept(given);
,

你可以试试switch语句的fallthrough机制。参考this

在您的示例中,代码可以(未测试):

已编辑:

public void simulate(String given,Status status) {
    switch (status){
        case C:
            simulateC(given);
        case B:
            simulateB(given);
        case A:
            simulateA(given);
    }
}

原文(错误):

public void simulate(String given,Status status) {
    switch (status){
        case A:
            simulateA(given);
        case B:
            simulateB(given);
        case C:
            simulateC(given);
    }
}

当读者考虑到fallthrough概念时,上面的代码比相关代码更清晰、更容易阅读。但情况并非总是如此。我的建议是重构您的代码,以消除重复调用和失败。

,

你不需要为你的所有案例写simulateA(given),只需将它移到顶部

public void simulate(String given,Status status) {
   simulateA(given);
   switch (status){
     case C:
       simulateC(given);
     case B:
       simulateB(given);
       break;
     case A:
       break;
 }}