我正在浏览许多教程,这些教程对我证明几乎没有帮助,因为生产代码不是动物,鸟类或人类。不是一种削减或射击武器,它的推理要复杂得多。
所以回到现实,场景: 服务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是最好的之一:
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
AuhenticationManager拥有这样的AuthenticationProviders列表,并根据上述方法选择正确的AuthenticationProviders。这违反多态吗?有时这一切都只是一种炒作...
答案 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()
方法,并在此接口的所有实现中实现该方法。