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

5
我想知道下面的代码是否违反了开放封闭原则。 AnimalDog 的父类,但是 Animal 拥有 Jackson 注解来帮助 ObjectMapper 序列化和反序列化这些类。任何继承 Animal 的人都只需要编辑 Animal 上存在的注解,以确保序列化和反序列化按预期工作而不会修改该类。
@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 {

}
3个回答

2
确实是这样的。开闭原则的思想是使对象在不必内部修改的情况下可扩展。由于任何一个Animal的新子类都必须对其进行修改才能正常工作,因此它违反了这一原则。
最初的回答: 开闭原则是指在不修改对象内部的情况下使其可扩展。任何继承自Animal的新子类都需要修改它才能正常工作,所以它违反了这个原则。

0

理论观点

开闭原则 就像整个SOLID一样,是一个乌托邦。我们应该不断地朝着这个方向升级我们的代码,但可能永远无法达到这个目标,因为这是不可能的。让我们阅读下面的文章,看看经典的getterannotation构造如何存在争议。

  1. 使用打印机代替Getter方法
  2. Java注释是一个大错误

实践观点

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

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

当我们谈论 Jackson 及其注释时,我认为我们可以在理论和实践之间找到黄金平衡点。这要归功于 MixIn 特性。您可以将模型与其序列化为 JSON 的方式分离。当然,当您添加扩展基类的新类时,您需要使用注释更改 MixIn interface,但这是我们需要付出的代价。

编辑或为什么我忘记回答一个问题?

抱歉,我忘记回答一个问题,即上面的示例是否违反了 开闭原则。首先,从 维基百科文章 中获取定义:

一个类是封闭的,因为它可以被编译、存储在库中、基线化并被客户端类使用。但它也是开放的,因为任何新类都可以将其作为父类使用,添加新功能。当定义了一个派生类时,无需更改原始类或干扰其客户端。
上面的例子违反了“定义派生类时,无需更改原始类”的部分。即使我们使用MixIn,仍然需要更改应用程序的其他部分。更重要的是,如果您的解决方案在99.99%的情况下使用注释,那么您会违反这一部分,因为需要配置某些隐藏在注释后面的功能。

抱歉,我忘记回答基本问题了,因为在我看来,从理论角度来看:_违反了_,而从实际角度来看:_没有_,因为cost不是太高。但每个人都应该为自己定义cost - Michał Ziober
感谢您的编辑!除了MixIn之外,还有另一种解决方案。@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") public abstract class Animal { // fields, constructors, getters and setters } @JsonTypeName(value = "Dog") public class Dog extends Animal { } - Raghavendra M Dani
同样来自维基百科: "如果模块可以供其他模块使用,则称该模块为封闭模块。这需要假定该模块已被给定明确定义的、稳定的描述。" 这个解决方案不是“明确定义”和“稳定”的,因为您可以从根中删除JsonTypeInfo注释,所有子类将突然破裂。 您不能强制要求后代具有此注释,也无法强制要求根类具有此注释,因为接口/类没有被破坏。它包含相同的方法。 API 相同,但某些东西不起作用。超过50%的问题都出现在SO上。 - Michał Ziober
1
我认为SOLID代码是理想状态。我同意我们应该努力达到这个目标。但我不同意它是不可能的。 - aridlehoover
@aridlehoover,当我写“不可能”时,我指的是整个项目。当然,我们可以在“SOLID”模式下实现项目的某些部分,但整个项目不可能或者说非常难以实现目标。 - Michał Ziober

0

开放/关闭原则意味着一个类应该对扩展开放但对修改关闭。

换句话说...如果你想要改变一个类的行为,你应该以某种方式扩展它,但不应该修改它。

你可以通过以下方式扩展一个类

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

从我的观点来看,@JsonSubTypes注释不属于Animal类的行为的一部分。 它改变了另一个类-对象映射器的行为。 因此,这并不真正违反原则。不真正意味着即使您不改变行为,您也必须触及Animal类并重新编译它。

这是一个非常奇怪的注解设计。为什么那些JSON开发人员不允许您在子类上放置注解,例如JPA在层次结构映射方面所做的那样。请参见DiscriminatorValue

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


谢谢分享不同的观点!按照定义,当抽象类的源代码被修改时,开闭原则在所有情况下都会被违反。注释和类文档是否属于源代码的一部分? - Raghavendra M Dani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接