如何处理抽象类作为微服务的入口api合同并同时解决多态性?

时间:2018-11-01 22:02:51

标签: java spring polymorphism microservices

我正在浏览许多教程,这些教程对我证明几乎没有帮助,因为生产代码不是动物,鸟类或人类。不是一种削减或射击武器,它的推理要复杂得多。

所以回到现实,场景: 服务1 通过Kafka与服务2 交换消息,消息通过Jackson进行序列化/反序列化,模型类在jar之间共享为服务。

现在是瘟疫,邪恶的顶点:

@JsonTypeInfo(
        use = Id.NAME,
        property = "type",
        visible = true
    )
    @JsonSubTypes({@Type(
        value = InternalTextContent.class,
        name = "text"
    ), @Type(
        value = InternalImageContent.class,
        name = "image"
    ), @Type(
        value = InternalAudioContent.class,
        name = "audio"
    ), @Type(
        value = InternalCustomContent.class,
        name = "custom"
    )})
    public abstract class InternalContent {
        @JsonIgnore
        private ContentType type;

        public InternalContent() {
        }

很显然,当需要使用此内容的时候,我们会得到类似的东西:

message.getInternalContent

这将导致大量switch语句,if条件,instanceof并等待它……到处都是向下转换

这只是包装对象包含的一个属性示例。显然,我无法将多态行为添加到InternalContent,因为您好,它在一个jar中。

这里出了什么问题?甚至错了吗? 如何添加多态行为?要添加新的缓解层,我仍然需要在某个工厂中使用instanceof来创建一种新型的多态对象系列,该系列可编辑以添加所需的行为?甚至不确定它是否会变得更好,它只是闻起来,让我想射击那些鼓吹像instanceof这样的盲目宣告的拥护者,这是一种代码气味。“折磨着像我这样真正关心的人,这使我感到奇怪如果他们曾经在一个真实的项目中工作过,我特意添加了系统环境详细信息,以了解如何不仅对代码建模,而且还对系统之间的交互进行建模。为实现“按书”解决方案,有哪些可能的重新设计选项?

到目前为止,我可以认为共享域模型是一种罪过。但是,如果我使用不同的包含自助服务的类来表示序列化/反序列化的相同内容,则我会获得灵活性,但会失去合同并增加不可预测性。从技术上讲,HTTP合同就是这种情况。

我应该沿着电线发送具有不同结构的不同类型的消息,而不是尝试在单个消息类型中适合常见的部分和子类型吗?

要向OO扔更多的沙子,我认为Pivotal是最好的之一:

https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java

public boolean supports(Class<?> authentication) {
        return (UsernamePasswordAuthenticationToken.class
                .isAssignableFrom(authentication));
    }

AuhenticationManager拥有这样的AuthenticationProviders列表,并根据上述方法选择正确的AuthenticationProviders。这违反多态吗?有时这一切都只是一种炒作...

1 个答案:

答案 0 :(得分:1)

使用访客模式。

示例(我将限制为两个子类,但是您应该了解一下):

interface InternalContentVisitor<T> {
    T visitText(InternalTextContent c);
    T visitImage(InternalImageContent c);
}

public abstract class InternalContent {
    public abstract <T> T accept(InternalContentVisitor<T> visitor);
    // ...
}

public class InternalTextContent {
    @Override
    public <T> T accept(InternalContentVisitor<T> visitor) {
        return visitor.visitText(this);
    }
}

public class InternalImageContent {
    @Override
    public <T> T accept(InternalContentVisitor<T> visitor) {
        return visitor.visitImage(this);
    }
}

此代码是完全通用的,可以由使用这些类的任何应用程序共享。

因此,现在,如果要使用InternalContent多态地在project1中执行某项操作,您要做的就是创建一个访问者。该访问者不在InternalContent类之外,因此可以包含特定于project1的代码。例如,假设project1的类Copier可用于创建文本或图像的副本,则可以使用

InternalContent content = ...; // you don't know the actual type 
Copier copier = new Copier();
Copy copy = content.accept(new InternalContentVisitor<Copy>() {
    @Override
    public Copy visitText(InternalTextContent c) {
        return copier.copyText(c.getText());
    }

    @Override
    public Copy visitImage(InternalImageContent c) {
        return copier.copyImage(c.getImage());
    }
});

因此,如您所见,不需要开关盒。即使InternalContent类及其子类完全不依赖于仅存在于project1中的Copier类,所有操作仍以多态方式完成。

如果出现一个新的InternalSoundContent类,您要做的就是在访问者接口中添加一个visitSound()方法,并在此接口的所有实现中实现该方法。