【问题标题】:Testing object factory测试对象工厂
【发布时间】:2020-07-01 15:43:03
【问题描述】:

我已经实现了一个工厂,如下所示:

class MessageFactory {
   private static final Map<Integer, MessageBuilder> IdToMessage = new HashMap<Integer, MessageBuilder>() {{
       put(StatusMessage.ID, StatusMessage::new);
       put(ConfigurationMessage.ID, ConfigurationMessage::new);
    }};


    public createMessage(byte[] payload){
       int id = getId(payload);
       return typeToMessage.get(id).create(payload);
    }
}

该工厂接收消息的有效负载(字节),从消息头中获取消息的 ID,然后使用相应消息的构造函数,其引用存储在 HashMap 中。

我想实施一个测试以确保所有消息都在 HashMap 中定义。 所有消息都放在同一个 java 包中,编写一个测试来确保这个 java 包中的所有 java 类都在 Hashmap 中是否有意义? 您是否看到任何其他方法来确保添加新消息后也将其添加到此哈希图中?这是我正在开发的代码中的一个问题,因为这种模式在多个地方使用。

【问题讨论】:

标签: java junit junit5


【解决方案1】:

我建议重构您的代码,以便编译器为您完成这项工作。

选项 1:切换表达式

首先,熟悉 Java 12 中引入的switch expressions

穷举

使用 switch 语句时,是否涵盖所有情况并不重要

但对于 switch 表达式,编译器坚持涵盖所有可能的情况

在你的情况下应用它:

  • 创建新的 MessageType 枚举
  • 允许从 int id 创建 MessageType
  • 重构 crateMessage 以利用 switch 表达式
enum MessageType {
    STATUS_MESSAGE_TYPE(1),
    CONFIGURATION_MESSAGE_TYPE(5);
    
    private final int id;

    MessageType(int id) {
        this.id = id;
    }

    public static MessageType fromId(int id) {
       // iteration used for lookup, 
       // alternatively a static lookup Map can be used
       for (var mt : MessageType.values()) {
           if (mt.id == id) {
               return mt;
           }
       }
       throw new IllegalArgumentException("Unknown message type:" + id);
    }
}

class MessageFactory {
    static Message create(byte[] payload) {
        var id = getId(payload);
        var messageType = MessageType.fromId(id);
        return switch (messageType) {
            case STATUS_MESSAGE_TYPE -> new StatusMessage();
            case CONFIGURATION_MESSAGE_TYPE -> new ConfigurationMessage();
        };
    }
}

如果您添加了新的消息类型,代码将停止编译。

选项 2:枚举方法

enum MessageType {
    STATUS_MESSAGE_TYPE(1, StatusMessage::new),
    CONFIGURATION_MESSAGE_TYPE(5, ConfigurationMessage::new);

    private final int id;
    private final Supplier<Message> messageBuilder;

    MessageType(int id, Supplier<Message> messageBuilder) {
        this.id = id;
        this.messageBuilder = messageBuilder;
    }

    Message create() {
        return messageBuilder.get();
    }

    public static MessageType fromId(int id) {
        // iteration used for lookup,
        // alternatively a static lookup Map can be used
        for (var mt : MessageType.values()) {
            if (mt.id == id) {
                return mt;
            }
        }
        throw new IllegalArgumentException("Unknown message type:" + id);
    }
}

class MessageFactory {
    static Message create(byte[] payload) {
        var id = getId(payload);
        var messageType = MessageType.fromId(id);
        return messageType.create();
    }
}

【讨论】:

  • 不错!遗憾的是我不能使用选项 1,因为我仅限于 java 8(甚至不是全部),因为该库必须与 android 兼容。但是选项 2 似乎确实可以解决问题,我稍后会尝试一下,看看结果如何,谢谢!
  • 还请记住,为了增加灵活性,您可以在枚举中将方法定义为抽象,并在每个常量中覆盖它。
猜你喜欢
  • 1970-01-01
  • 2017-12-10
  • 2017-08-12
  • 2012-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多