用于过滤未知类型对象的最干净模式?



我有一个应用程序,它从队列中获取 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;
}
}

对于此类情况,最干净的模式是什么? 我已经读过有关访客模式的信息,但我不确定这是否比像这样投射更好。

就设计模式而言,我认为它是策略模式的类型。我不是在谈论Spring的实现方式。您可能有 n 个过滤器,但您必须根据上下文进行选择。因此,战略模式最适合这里。其他人可以提供其他模式。您可以在下面的链接中制定策略模式。

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

Java 反射的访问者模式怎么样?这是一篇旧文章:

https://www.javaworld.com/article/2077602/java-tip-98--reflect-on-the-visitor-design-pattern.html

当您想要将消息与过滤器分离并且关系是多对多时,您可以随时使用责任链。

@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 的情况下创建的示例,因此可能存在语法问题,但应该让您了解它应该如何工作。

另请参阅:

  • 爪哇中的责任链
  • Java 中的责任链设计模式

如果我正确理解您的问题,那么可以以一种使您的过滤器抛出ClassCastException的方式配置您的 Spring 配置文件。

假设你的配置选项是你想要的方式,那么它展示了你的设计中唯一真正的问题——你的过滤器可以应用于任何Object,这就是接口所说的——确实FilterPass(对象(——但你的过滤器只适用于某些类型的对象

这就是您需要解决的问题。 如果将筛选器应用于奇怪类型的对象,它是通过还是失败? 您可以基于每个过滤器来决定这一点,然后像这样修复它:

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

简单易行。

我知道你不喜欢演员表,但这是你提供的配置选项的直接结果——配置文件可以配置为将过滤器应用于任何类型的对象。 你只需要支持这一点,这意味着必须在某个地方进行铸造。

这在泛型中变得更加干净。 由于我知道每个过滤器可以处理哪种类型的对象,我可以这样做,消除强制转换:

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;
}

最新更新