这段代码是否违反开闭原则?

Does this code violate open-closed principle?

我想知道下面的代码是否违反了开闭原则。

AnimalDog 的父级 class,但是 Animal 具有有助于 ObjectMapper(反)序列化 class 的杰克逊注释]是的。任何扩展 Animal 的人都必须只编辑 Animal 上的注释,以确保(反)序列化按预期工作,而 class 保持不变。

@JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME, 
  include = JsonTypeInfo.As.PROPERTY, 
  property = "type")
@JsonSubTypes({ 
  // all subclasses
  @Type(value = Dog.class, name = "dog")
})
public abstract class Animal {
    // fields, constructors, getters and setters
}

public class Dog extends Animal {

}

理论观点

Open/closed principle like the whole SOLIDUtopia。我们应该朝那个方向不断升级我们的代码,但我们可能永远不会在那里结束,因为这是不可能的。让我们阅读下面的文章,看看 classical gettersannotation 结构如何值得商榷。

  1. Printers Instead of Getters
  2. Java Annotations Are a Big Mistake

实用观点

像每个实用的程序员一样,我喜欢使用好的工具来解决问题,而不是自己实现新的东西。当我被要求将给定模型序列化为 JSON 文件时,我正在检查它是否是:

  1. 开源
  2. 正在积极开发中
  3. 易于使用

当我们谈论 Jackson 及其注释时,我认为,我们可以找到理论与实践之间的黄金中间道路。这要归功于 MixIn 功能。您可以将模型与其序列化为 JSON 的方式分开。当然,当你添加新的 class 扩展基础 class 时,你需要用注释更改 MixIn interface 但这是我们需要付出的代价。

编辑或为什么我忘了回答问题?

抱歉,我忘了回答一个问题,上面的例子是否违反了Open/Closed principle。首先,从 Wikipedia article:

获取定义

A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.

上面的例子违反了When a descendant class is defined, there is no need to change the original部分。即使我们使用 MixIn 也需要更改应用程序的其他部分。更重要的是,如果你的解决方案在 99.99% 的情况下使用 annotations,你就违反了这一部分,因为需要以某种方式配置隐藏在它们后面的功能。

确实如此。 open-close 原则的想法是使 objects 可扩展,而无需在内部修改它们。由于 Animal 的任何新 child 都必须对其进行修改才能正常工作,这违反了原则。

Open/closed 表示 class 应该对扩展开放,但对修改关闭。

换句话说...如果您想更改 class 的行为,您应该以某种方式扩展它,但不应该修改它。

您可以 class 扩展

  • 创建子class。这通常使用例如模板方法模式。
  • 定义一个 class A 使用的接口,这样它的行为就可以通过传递该接口的另一个实例来扩展,例如一种策略模式。 TreeSet(Comparator<? super E> comparator) 是一个很好的现实生活示例,因为它的排序行为可以在不修改 TreeSet 本身的情况下进行更改。

从我的角度来看,@JsonSubTypes 注释不是 Animal class 行为的一部分。它改变了另一个 class - 对象映射器的行为。因此,这并不是真正的违规行为。不是真的意味着即使你不改变行为,你也必须触摸 Animal class 并重新编译它。

注解的设计真是奇葩。为什么 json 开发人员不允许您在子 class 上添加注释,例如就像 JPA 在层次结构映射方面所做的那样。参见 DiscriminatorValue

超类型引用子类型是一个奇怪的设计。 抽象类型不应依赖于具体类型。在我看来,这是一个应该始终应用的原则。