所以想象一下,
给定一个类层次结构:
S (abstract)
A extends S
B extends S假设我们有一个处理A和B类型对象的连接组件链。例如:K1.handle(A) -> K2.handle(A) -> K3.handle(A)表示K1接受A并使用A调用K2,后者使用A调用K3。
类似地,我们有:K1.handle(B) -> K2.handle(B) -> K3.handle(B)
事情是K2的逻辑不依赖于S的子类型,所以我们可以用K2.handle(S)替换K2.handle(A)和K2.handle(B)。但问题是,在某些情况下,我们需要对实际对象类型进行类型检查,以调用正确的K3方法。
如果不使用if或switch等语句来检查实际对象的类型/属性,如何解决这个问题呢?
这是我需要查看的访问者模式吗?
给出一个具体的例子。假设K1是一个REST控制器,具有A和B的不同POST端点。它反序列化有效负载并验证数据,然后将对象传递给K2进行进一步处理。K2 (以及它调用的其他组件)中包含的所有步骤都可以只处理类型为S的对象。但在K3中,我们最终需要知道S的实际类型。
发布于 2020-06-18 15:54:36
但是在K3中,我们最终需要知道S的实际类型。
因此,K2.handle()中的所有逻辑都在调用K3.handle()之前。
此外,根据任务的措辞,由于K3.handle(A)和K3.handle(B)不能为K3.handle(S),因此K3.handle()似乎是一个重载方法,尽管问题中没有明确说明这一点。
要做到这一点,一种方法是将公共逻辑移动到内部助手方法:
public class K2 {
    public void handle(A a) {
        handleInternal(a);
        K3.handle(a);
    }
    public void handle(B b) {
        handleInternal(b);
        K3.handle(b);
    }
    private void handleInternal(S s) {
        // K2 logic here
    }
}或者,您可以使用方法引用,允许将对K3的调用嵌入到K2逻辑中,而不仅仅是在最后:
public class K2 {
    public void handle(A a) {
        handleInternal(a, K3::handle); // method reference to K3.handle(A)
    }
    public void handle(B b) {
        handleInternal(b, K3::handle); // method reference to K3.handle(B)
    }
    private <T extends S> void handleInternal(S s, Consumer<T> k3_handle) {
        // Some K2 logic here
        k3_handle.accept(s);
        // More K2 logic here
    }
}发布于 2020-06-18 16:09:45
给定示例类:
class S {}
class A extends S {
    public int getAInfo() {
        return 1;
    }
}
class B extends S {
    public int getBInfo() {
        return 2;
    }
}我们可以创建一个利用绑定类型参数的接口K,如下所示:
@FunctionalInterface
interface K<T extends S> {
    void handle(T s);
    @SafeVarargs
    static <FT extends S> K<FT> compose(K<FT> finalK, K<? super FT>... ks) {
        return s -> {
            Stream.of(ks).forEach(k -> k.handle(s));
            finalK.handle(s);
        };
    }
}然后,我们可以使用K.construct来创建一个新的K实例,该实例将在整个K流水线中转发一个s。这个K流水线的结构使得除了最后一个k之外的所有都是通用的处理程序,接受S的任何实例。只有最后一个将接受S的特定子类的实例,即FT (“最终类型”的缩写)。
使用此K定义,将编译以下所有语句:
K<S> k1 = s -> System.out.println("K1 handled.");
K<S> k2 = s -> System.out.println("K2 handled.");
K<A> k3A = a -> System.out.println(a.getAInfo());
K<B> k3B = b -> System.out.println(b.getBInfo());
K.compose(k3A, k1, k2).handle(new A()); // Propagates instance of A through pipeline
K.compose(k3B, k1, k2).handle(new B()); // Propagates instance of B through pipeline然而,管道中不是最后一个k的所有K的即席多态性仍然保持不变,因为这两个语句也会进行编译:
k1.handle(new S());
k2.handle(new S());我在这个解决方案中看到的一个缺点是,与其他答案不同,K处理程序必须严格按顺序执行,并且对下一个k处理程序的调用不能在前一个处理程序的中间发生。
发布于 2020-06-18 16:27:16
是的,这是一个访问者模式。
参观者是
interface K {
    void handle(A a);
    void handle(B b);
}并由K1、K2、K3实现。
S需要的是一个带有实现的方法visit(K)
class A implements S {
    public void visit(K visitor) {
        visitor.handle(this); // calls the handle(A) method
    }
}
class B implements S {
    public void visit(K visitor) {
        visitor.handle(this); // calls the handle(A) method
    }
}你的列表(即你的链条)只是一个List<K>,其内容类似于
List<K> handlers = Arrays.asList(new K1(), new K2(), new K3());
S sample = new A();
for (K visitor : handlers) {
    sample.visit(visitor);
}
sample = new B();
for (K visitor : handlers) {
    sample.visit(visitor);
}这是一个running version。
https://stackoverflow.com/questions/62444663
复制相似问题