如何使用策略或功能接口处理基于值(枚举)的多项选择?

How to deal with multiple choices based on value (enum) using strategy or functional interfaces?

我正在处理一个案例,我有 enum:

public enum CurrencyType {
    BTC, ETH
}

一个pojo:

public class Challenge {
    private final String walletAddress;
    private final CurrencyType currencyType;
    //getters, constructors, setters
}

一项服务:

@Service
public ChallengeService {
   public Challenge verifyMessage(final Challenge challenge) {
           Challenge verifiedChallenge;
       switch (challenge.getCurrencyType()) {
           case BTC:
               verifiedChallenge = verifyBTCMessage(challenge);
               break;
           case ETH:
               verifiedChallenge = verifyETHMessage(challenge);
               break;
           default:
               throw new IllegalStateException("Unexpected value: " + challenge.getCurrencyType());
       }
    return verifiedChallenge;
    }

    private Challenge verifyBTCMessage(Challenge challenge) { //implementation here }

    private Challenge verifyETHMessage(Challenge challenge) { //implementation here }

}

目前枚举只包含 2 个值,ETHBTC,但有一天这个枚举中的值的数量会增加到 5、10 甚至更多。

此时我认为我的做法是错误的。我在看策略模式,它看起来不错,但似乎在某些时候我将无法逃脱整个 ifology (或 switchology 准确地说)将 Challenge 对象委托给基于 CurrencyType 枚举值的适当方法。

在阅读关于重构大师的策略模式时,我找到了这个信息(在策略模式的 cons 部分)

A lot of modern programming languages have functional type support that lets you implement different versions of an algorithm inside a set of anonymous functions.

这似乎是一种更好的方法,但是...我正处于学习函数式编程的初级阶段,老实说 - 我不知道从哪里开始。或者也许策略模式就足够了,但我遗漏了一些关键的东西?我想要实现的就是尽可能摆脱 switchology

我想这可以按如下方式实现:

interface Verifier {
     boolean isMatched(CurrencyType type);
     CurrencyType currency();
     Challenge verify(Challenge challenge);
}

每个具体 class 将是 used/matched 特定货币。

我建议您创建一个界面如下:

public interface MessageVerifier {
     public CurrencyType handles();
     public Challenge verifyMessage(Challenge challenge);
}

然后对于 BTC,你会得到类似的东西(你将对每种货币类型都有一个实现):

@Component
public class BtcMessageVerifier implements MessageVerifier {
     public CurrencyType handles() {
         return CurrencyType.BTC;
     }

     public Challenge verifyMessage(Challenge challenge) {
         // Your logic here
     }
}

您现在可以将所有 MessageVerifier 的列表注入 ChallengeService 并将其重新排列到一个 Map 中,以便在需要时更容易使用。

@Service
public ChallengeService {
   
   private Map<CurrencyType, MessageVerifier> messageVerifierPerCurrency;

   @Autowired
   public ChallengeService(List<MessageVerifier> messageVerifiers) {
        this.messageVerifierPerCurrency = messageVerifiers.stream().collect(Collectors.toMap(MessageVerifier::handles, Function.identity()));
   }  

   public Challenge verifyMessage(final Challenge challenge) {
       Challenge verifiedChallenge;
       if (messageVerifierPerCurrency.containsKey(challenge.getCurrencyType())) {
          verifiedChallenge = messageVerifierPerCurrency.get(challenge.getCurrencyType()).verifyMessage(challenge);
       } else
          throw new IllegalStateException("Unexpected value: " + challenge.getCurrencyType());
       }
       return verifiedChallenge;
    }

}