java - 基于运行时提供的字符串查找要使用哪个接口(interface)实现的优雅解决方案

标签 java spring inversion-of-control factory

在重构一些代码时,我发现我们应该在几个地方使用一些多态性,而不是到处都有一堆 if/else block 。

虽然面向对象的类很容易制作,但当我们必须决定使用什么实现时,问题就来了。似乎有很多解决方案,但我想看看是否有更优雅或“最佳实践”的方法来做到这一点。

所以基本上,为了举例,我将选择一张信用卡,并且对于每种信用卡类型,我都有一个实现。因此我有一个这样的类结构:

  • 信用卡(摘要)
    • 签证
    • 万事达卡
    • 美国运通卡

每个子类都将扩展 CreditCard 父类(super class)。

现在在 Web 应用程序中,我将传递一个表示用户选择的卡片类型的字符串。现在我需要将它路由到实际的实现类本身。这就是过多选择发挥作用的地方。

如果有更好的选择或者我是否坚持使用这些选择,请务必告诉我..

工厂:

        @Autowired @Qualifier("visa") private CreditCard visa;
        @Autowired @Qualifier("mastercard") private CreditCard mastercard;
        @Autowired @Qualifier("amex") private CreditCard amex;

        public CreditCard getCreditCard(String input) {
        {
            if ("visa".equals(input)) {
                    return visa;
            } else if ("mastercard".equals(input)) {
                    return mastercard;
            } else if ("amex".equals(input)) {
                    return amex;
            }

            return null;
        }

map :

        @Autowired HashMap<String, CreditCard> creditCardImpls;

        public CreditCard getCreditCard(String input) {
            return creditCardImpls.get(input);
        }

ApplicationContext getBean:

        @Autowired private ApplicationContext applicationContext;

        public CreditCard getCreditCard(String input) {
            return applicationContext.getBean(input);
        }

我在这里看到的问题是,如果我们要在未来添加更多的信用卡类型,我将不得不在可能的几个不同领域 Autowiring 。然后,Map 的问题是我们没有使用 Spring 来获取 bean。对于吨 他从 ApplicationContext 中获取 Bean,我们没有遵循 Spring 提供的 IoC。

这个问题的最佳解决方案或最佳实践是什么?

最佳答案

首先对您的解决方案发表一些评论,然后是我自己的建议。

  1. 这违反了 Open-Closed PrincipleDependency Inversion Principle .不仅工厂需要明确知道所有的 bean 分别和它们对应的信用卡类型(input)映射,而且每次你​​想引入一个新的信用卡类型时,你需要修改工厂类代码。
  2. 这是迄今为止您最好的解决方案,IMO。它动态地发现 bean 并依赖于 CreditCard抽象。与您的评论“它不使用 Spring 来确定要使用哪个 bean”相反,该映射将 bean ID 作为键并由 Spring Autowiring ,因此我们实际上在这里广泛使用了 Spring。但是,由于映射键是 bean ID,这将业务逻辑(信用卡类型 - input)与技术实现(Spring bean ID)混合在一起。如果你想实现一个单独的 bean 来处理“mastercard platinum”信用卡类型怎么办?由于技术原因,您不能拥有这样的 bean ID(由于空间)。或者在某些时候您会希望某个 bean 可以处理多种卡片类型。然后,您需要在业务标识符和 bean ID 之间实现另一个转换映射。请参阅下面我的解决方案以获得增强功能。
  3. 您应该尽可能避免将您的代码与框架代码(Spring 或任何其他代码)耦合。正如您所说,我们应该让 Spring 为我们处理 DI,这样工厂类就会注入(inject)依赖项,而不必显式请求它们。

我的建议是将业务 key 与 bean ID 分开。所以我们需要 CreditCard能够说明它可以处理哪种信用卡类型。

abstract class CreditCard {
    public abstract String getType();
}

然后每个子类返回自己的类型。

或者

abstract class CreditCard {
    private String type;

    protected CreditCard(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

每个子类调用super(<the subclass type>)他们自己的构造函数中的构造函数。

然后,在工厂中,您可以这样实现。

@Autowire private Collection<CreditCard> creditCards;

private Map<String, CreditCard> creditCardsMap = new HashMap<>();

@PostCostruct
public void mapCreditCards() {
    for (CreditCard creditCard : creditCards) {
        creditCardsMap.put(creditCard.getType(), creditCard);
    }
}

public CreditCard getCreditCard(String type) {
    return creditCardsMap.get(type);
}

@PostConstruct方法将在 creditCards 之后运行(所有类型为 CreditCard 的 bean)都已 Autowiring ,因此允许在业务 key 而非技术 key 下进行动态发现和映射。

关于java - 基于运行时提供的字符串查找要使用哪个接口(interface)实现的优雅解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23507079/

相关文章:

c# - 可移植类库的 IoC 容器

c# - MEF 错误,是循环依赖,现在是其他东西

java - 如何将每个句子的第一个字母转换为大写并将所有其他字母转换为小写?

java - 使用 Java 将文本动态添加到 JTextArea

java - 如何从 Spring 字符编码中排除文件?

java - 从选择列表中获取选择值并将其连接到我的表单中的发布操作

java - 使用文件 ..xml 中定义的名称 '..' 创建 bean 时出错

c# - 依赖注入(inject)——几个类方法中需要的新实例

java - 使用 Java Swing 类库制作简单文本编辑器所需的建议

java - Maven 找不到 web.xml