最佳实践/模式:转换Java对象的方法

3

假设我有一个应用程序,负责将供应商消息转换为规范消息。例如:

public class MessageA extends VendorMessage { ... }
public class MessageB extends VendorMessage { ... }

public class MessageX extends CanonicalMessage { ... }
public class MessageY extends CanonicalMessage { ... }

其中MessageA映射到MessageX,MessageB映射到MessageY。

我的方法是为每种消息类型创建一个转换器类来处理此转换。在这个示例中,我将拥有以下转换器:

public class MessageXTransfomer()
{
    public MessageX transform(MessageA message) {...}
}

public class MessageYTransfomer()
{
    public MessageY transform(MessageB message) {...}
}

我的问题实际上是如何最终调用转换���的方式。

由于我的过程需要一些VendorMessage作为输入,所以我需要检查类型,以便知道应该将其指向哪个具体的转换器。例如,一种方法可能是这样的:

public class TransfomerService
{
    MessageXTransformer messageXTransformer = new MessageXTransformer();
    MessageYTransformer messageYTransformer = new MessageYTransformer();

    public CanonicalMessage transform(VendorMessage message)
    {
        if (message instanceOf MessageA)
        {
            return messageXTransformer.transform((MessageA) message);
        }
        else if (message instanceOf MessageB)
        {
            return messageYTransformer.transform((MessageB) message);
        }
    }
}

我不确定为什么,但这种方法让我感到奇怪——好像我做错了什么。是否有最佳实践来解决这类问题,我应该使用哪种方法?

注:我正在寻找最佳方法,而不使用任何转换框架等。理想情况下,该模式应该只需使用基本的Java即可实现。


传统的面向对象编程答案是在VendorMessage中添加一个方法,将其转换为CanonicalMessage...我认为经典的重构类似于“将条件转换为多态”之类的。但实际考虑可能会超越这一点。做“可能起作用的最简单的事情”。不要过度复杂化。专注于简单性和可读性。 - john_omalley
1
@john_omalley 不同意。作为短期解决方案,在消息本身中拥有一个方法是很好的,但从长远来看,1)您正在将供应商消息域与您的规范消息域耦合在一起2)如果您无法修改供应商消息并且它来自单独的系统怎么办?3)当您开始注入转换所需的内容时,您的方法立即停止工作(您不会将注入到消息中,对吧?) - Vadim Kirilchuk
@john_omalley 还有一件事:它违反了单一职责原则 :) 消息不应该负责将自己转换为其他内容。这应该是专门组件的责任。 - Vadim Kirilchuk
2个回答

5

我喜欢@javaguy的回答,但它不完整。当然,如果你能像他后面的示例一样使用特定的transformer就好了,但如果不能,你就必须坚持使用TransformerFacade和某种StrategyPattern:

public class TransformerFacade {

    private Map<Class, VendorMessageToCanonicalMessageTransformer> transformers = new HashMap<>();
    { 
        // this is like strategies, the key may be class, class name, enum value, whatever
        transformers.put(MessageA.class, new MessageXTransformer());
        transformers.put(MessageB.class, new MessageYTransformer());
    }

    public CanonicalMessage transform(VendorMessage message) {
        return transformers.get(message.getClass()).transform(message);
    }
}

1
Java中没有多重分派,因此Dispatch在Java中不起作用。 - fps
@FedericoPeraltaSchaffner 谢谢,我以为它能工作,但刚刚检查了一下,你是对的,已经从答案中删除了那部分! - Vadim Kirilchuk
我现在已经给你的答案点了赞 :) PS 如果你想在Java中实现这种调度,你需要使用一些访问者模式来模拟双重调度。 - fps
我也是这样做的 ;) 是啊,我开始忘记了一些基础知识,因为我一直在做企业级的东西。Visitor模式很好,但不适合这种任务。 - Vadim Kirilchuk
@AshleyFrieze,1没问题,至于2,我更喜欢O(1)的平均值,而不是O(n),在任何应用程序中经常使用转换,因此每次遍历所有内容可能不是最佳解决方案。但我同意这种方法可以根据您的需求进行定制。 - Vadim Kirilchuk
显示剩余3条评论

2

我会简单地通过实现一个接口让每个具体的VendorMessage返回其相应的CanonicalMessage

public interface Mapper<T> {

    T map();
}

然后,MessageA 应该实现这个接口:
public MessageA implements Mapper<MessageX> {

    @Override
    public MessageX map() {
        MessageX message = ...;
        // fill message
        return message;
    }
}

如果您不想在VendorMessage类中进行映射,则可以采用Vadim Kirilchuk在他的答案中建议的策略来解决问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接