过滤未知类型对象的最干净模式是什么?

4
我有一个应用程序,从队列中接收JSON对象,将它们反序列化为模型,应用一系列过滤器,然后将通过所有过滤器的对象发送到另一个队列。
复杂的两个条件是:
1. 过滤器集合在启动时通过Spring配置文件确定并注入。 2. JSON反序列化为对象的类型也在启动时通过Spring配置文件确定。
以下解决方案很丑陋,因为涉及到类型转换:
public class MessageTypeOne {

    public int someField;
}

public class MessageTypeTwo {

    public int otherField;
}

public interface MessageFilter {

    boolean doesFilterPass(Object object);
}

@Component
@Profile("ProfileOne")
public class OneOfMyMessageFilters implements MessageFilter {

    public boolean doesFilterPass(Object object) {
        MessageTypeOne message = (MessageTypeOne)object;

        if (message.someField == something) {
            return false;
        } else return true;
    }
}

@Component
@Profile("ProfileTwo")
public class AnotherOneOfMyMessageFilters implements MessageFilter {

    public boolean doesFilterPass(Object object) {
        MessageTypeTwo message = (MessageTypeTwo)object;

        if (message.otherField == something) {
            return false;
        } else return true;
    }
}

@Service
public class MessageFilterService {

    // injected at runtime via Spring profile
    private Set<MessageFilter> messageFilters

    @AutoWired
    public MessageFilterService(Set<MessageFilter> messageFilters) {
        this.messageFilters = messageFilters;
    }

    public boolean passesAllFilters(Object object) throws IOException {
        for (MessageFilter filter : messageFilters) {
            if (!filter.doesFilterPass(object)) {
                return false;
            }
        }

        return true;
    }
}

这种情况下最清晰的模式是什么?我已经了解了访问者模式,但不确定它是否比这样的强制转换更好。

我可以给你两个建议。关于过滤,你可以使用责任链模式。关于消息类型,你可以使用一个接口MessageType,由MessageTypeOne和MessageTypeTwo实现,并将接口作为参数传递以隐藏实现(这被称为策略模式)。 - Paul
那不起作用。过滤器将需要使用特定消息类型实现的字段来决定是否过滤它。 - b15
5个回答

1
就设计模式而言,我认为它属于策略模式。我不是在谈论Spring的实现方式。您可能有无数个过滤器,但必须根据上下文进行选择。因此,在这里最适合使用策略模式。其他人可以提供其他模式。您可以在以下链接中了解策略模式。

https://en.wikipedia.org/wiki/Strategy_pattern


我不知道这对我的情况如何适用,因为策略模式涉及在相同类型的参数上执行不同的操作。 我在编译时不知道类型。 - b15
您可以查看以下链接:https://dev59.com/BWMm5IYBdhLWcg3wbuc1,https://www.javacodegeeks.com/2013/11/strategy-pattern-aint-meant-for-spring.html - Sambit

1

1
当您想要将消息与过滤器解耦并且关系是多对多时,您可以始终使用职责链模式
@Service
public class MessageFiltersAggregator {

    private MessageFilter chainEntryNode;

    @AutoWired
    public MessageFilterService(Set<MessageFilter> messageFilters) {
        this.chainEntryNode = buildChain(messageFilters);
    }

    public boolean passesAllFilters(Object object) throws IOException {
        return chainEntryNode.doesFilterPass(object);
    }
}

你需要实现buildChain方法,它可以从集合中创建链。当然,链中的每个元素都应该有一个next属性。在这种情况下,MessageFilter可能如下所示:
public abstract class MessageFilter {
    private MessageFilter next;

    //constructors, setters, etc

    public boolean doesFilterPass(Object object) {
        boolean res = true;
        if (canHandle(object)) {
            res = validate(object);
        }
        return res && next.doesFilterPass(object);
    }
    public abstract boolean validate(Object object);
    public abstract boolean canHandle(Object object);
}

抽象类包含链式逻辑,您只需要在每个子类中实现两种方法之一。其中一种实现可能如下所示:

public class AnotherOneOfMyMessageFilters extends MessageFilter {
    public boolean canHandle(Object object) {
        return object instanceof MessageTypeTwo;
    }
    public boolean validate(Object object) {
        MessageTypeTwo message = (MessageTypeTwo)object;

        return message.otherField == something;
    }
}

以上所有的类都是没有使用IDE创建的示例,因此语法可能存在问题,但应该可以给您一个想法,它应该如何工作。

另请参阅:


这是很多工程来替换 if (!(message instanceOf ...。我们 Java 程序员必须停止认为设计不好,直到它变得非常麻烦。 - Matt Timmermans

0

泛型使得代码变得更加简洁。由于我知道每个过滤器可以处理的对象类型,因此我可以直接这样做,消除了强制转换:

public class MessageTypeOne {

    public int someField;
}

public class MessageTypeTwo {

    public int otherField;
}

public interface MessageFilter<T> {

    boolean doesFilterPass(T message);
}

@Component
@Profile("ProfileOne")
public class OneOfMyMessageFilters<T extends MessageTypeOne> implements MessageFilter<T> {

    public boolean doesFilterPass(MessageTypeOne message) {
        if (message.someField == something) {
            return false;
        } else return true;
    }
}

@Component
@Profile("ProfileTwo")
public class AnotherOneOfMyMessageFilters<T extends MessageTypeTwo> implements MessageFilter<T> {

    public boolean doesFilterPass(MessageTypeTwo message) {
        if (message.otherField == something) {
            return false;
        } else return true;
    }
}

@Service
public class MessageFilterServiceImpl<T> implements MessageFilterService<T> {

    // injected at runtime via Spring profile
    private Set<MessageFilter<T>> messageFilters

    @AutoWired
    public MessageFilterService(Set<MessageFilter<T>> messageFilters) {
        this.messageFilters = messageFilters;
    }

    public boolean passesAllFilters(T message) throws IOException {
        for (MessageFilter filter : messageFilters) {
            if (!filter.doesFilterPass(message)) {
                return false;
            }
        }

        return true;
    }
}

public interface MessageFilterService<T> {

    boolean passesAllFilters(T rawEvent) throws IllegalArgumentException;
}

0

如果我正确理解了您的问题,那么可以通过配置Spring配置文件来使您的过滤器抛出ClassCastException异常。

假设您的配置选项是您想要的方式,那么它展示了您设计中唯一的真正问题--您的过滤器可以应用于任何Object,这就是接口所说的--doesFilterPass(Object)--但是您的过滤器实际上只能与某些类型的对象一起使用。

这就是您需要解决的问题。如果将过滤器应用于奇怪类型的对象,它会通过还是失败?您可以根据每个过滤器的情况进行决定,然后像这样修复它:

public boolean doesFilterPass(Object object) {
    if (!(object instanceOf MessageTypeTwo)) {
        return true;
    }
    MessageTypeTwo message = (MessageTypeTwo)object;

    if (message.otherField == something) {
        return false;
    } else return true;
}

非常简单。

我知道你不喜欢强制类型转换,但这是由您提供的配置选项直接导致的——配置文件可以配置应用于任何类型对象的过滤器。您只需要支持它,这意味着必须在某处进行类型转换。


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