首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >从子类到超类,然后再返回

从子类到超类,然后再返回
EN

Stack Overflow用户
提问于 2020-06-18 15:31:47
回答 4查看 60关注 0票数 2

所以想象一下,

给定一个类层次结构:

代码语言:javascript
运行
复制
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方法。

如果不使用ifswitch等语句来检查实际对象的类型/属性,如何解决这个问题呢?

这是我需要查看的访问者模式吗?

给出一个具体的例子。假设K1是一个REST控制器,具有A和B的不同POST端点。它反序列化有效负载并验证数据,然后将对象传递给K2进行进一步处理。K2 (以及它调用的其他组件)中包含的所有步骤都可以只处理类型为S的对象。但在K3中,我们最终需要知道S的实际类型。

EN

回答 4

Stack Overflow用户

发布于 2020-06-18 15:54:36

但是在K3中,我们最终需要知道S的实际类型。

因此,K2.handle()中的所有逻辑都在调用K3.handle()之前。

此外,根据任务的措辞,由于K3.handle(A)K3.handle(B)不能为K3.handle(S),因此K3.handle()似乎是一个重载方法,尽管问题中没有明确说明这一点。

要做到这一点,一种方法是将公共逻辑移动到内部助手方法:

代码语言:javascript
运行
复制
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逻辑中,而不仅仅是在最后:

代码语言:javascript
运行
复制
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
    }
}
票数 0
EN

Stack Overflow用户

发布于 2020-06-18 16:09:45

给定示例类:

代码语言:javascript
运行
复制
class S {}

class A extends S {
    public int getAInfo() {
        return 1;
    }
}

class B extends S {
    public int getBInfo() {
        return 2;
    }
}

我们可以创建一个利用绑定类型参数的接口K,如下所示:

代码语言:javascript
运行
复制
@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定义,将编译以下所有语句:

代码语言:javascript
运行
复制
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的即席多态性仍然保持不变,因为这两个语句也会进行编译:

代码语言:javascript
运行
复制
k1.handle(new S());
k2.handle(new S());

我在这个解决方案中看到的一个缺点是,与其他答案不同,K处理程序必须严格按顺序执行,并且对下一个k处理程序的调用不能在前一个处理程序的中间发生。

票数 0
EN

Stack Overflow用户

发布于 2020-06-18 16:27:16

是的,这是一个访问者模式。

参观者是

代码语言:javascript
运行
复制
interface K {
    void handle(A a);
    void handle(B b);
}

并由K1K2K3实现。

S需要的是一个带有实现的方法visit(K)

代码语言:javascript
运行
复制
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>,其内容类似于

代码语言:javascript
运行
复制
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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62444663

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档