【问题标题】:Open/Closed principle query开/关原则查询
【发布时间】:2021-05-02 15:48:15
【问题描述】:

只是一个快速的,所以遵循开放封闭原则,如果你有这样的课程:

public class Employee extends Person {
    int age;
    String name;

    Employee(int age, String name) {
        super(age, name)
    }
    
    // Getters and setters follow
}

如果你想添加一个额外的字段,比如说一个地址,这会不会违反 Open/Closed 原则?

只是好奇你这样做是否违反了这个原则,你将如何创建类来解决这个问题?

谢谢

【问题讨论】:

    标签: java solid-principles open-closed-principle


    【解决方案1】:

    简短的回答是“是”。这将违反OCP。所以,为了回答你的问题(你将如何创建类来解决这个问题),让我们先看看 OCP 是什么:

    开闭原则

    OCP 规定软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭

    你清楚地理解了最后一部分,但你似乎错过了第一部分。同样,对您的问题的简短回答是简单地扩展您的原始课程。

    public class Person {
       int age;
       String name;
       // rest of class omitted
    }
    

    // This class already obeyed OCP by extending Person and adding new attributes and behaviors (took the liberty to change your original class for a good reason - See Person class)
    public class Employee extends Person {
       String empID;
       // Rest of the class omitted
    }
    

    // Simply add one more (corny class name, sorry)
    public class AddressableEmployee extends Employee {
       private String address;
       // Rest of the class omitted
    }
    

    这是添加新功能时如何遵守 OCP。显然,如果这被滥用,它会导致类爆炸,这也是不好的。作为开发人员(和设计师),您必须盲目地尊重这一原则,而不是创建深层层次链。

    要记住的是,OCP(与 SOLID 的其余部分一样)只是建议。最后,如果您发现自己处于这种情况,这可能表明设计决策仓促(直接实施而没有花足够的时间来巩固您的设计)。因此,不要再草率地做出实施决定,而是停下来考虑重新设计您最初创建的内容(即考虑组合而不是继承)。

    【讨论】:

    • 是的。在添加 crud 层之前始终考虑重构(而不是 CRUD :-)...如果不这样做,您最终将不得不重构或重写,以后会更加痛苦和昂贵。
    • 啊,好吧,所以有些东西大到足以保证改变,你应该有一个扩展当前的新类。完美的描述,非常感谢:)
    • 我不会编辑我的答案,而是简单地评论一下这个“解决方案”,现在你对 Liskov 替换原则有问题。这是我回答的核心:在实施解决方案之前停止并重新评估。
    【解决方案2】:

    简短的回答是“是”。 @hfontanez 对 OCP 进行了很好的高级介绍,您会在 SO 上大多数与 OCP 相关的答案中看到这一点。

    但长的答案是“不”。因为 OCP 不适用于此处。
    要了解什么样的场景我们需要担心 OCP,我们必须从适当的编程范式开始。与所有 SOLID 原则一样,OCP 是面向对象的。它适用于面向对象的代码。

    Employee 不是一个对象,在这个词的 OO 意义上,因为对象有行为。 Employee 是一种数据结构:它纯粹是状态的表示。数据结构是过程和函数式编程的基础,但不是面向对象的。试图将 OCP 应用到 Employee 会使我们从一开始就走上错误的轨道,因为我们处于错误的范式中。

    解决范式问题似乎微不足道。我们可以在Employee 中实现一个doWork() 方法,然后我们就有了一些行为,对吧?但这是 SOLID 原则开始相互作用的地方。因为谁会调用doWork() 方法?当然不是Employee 模块之外的任何客户端!毕竟Employee是一个具体的实现,依赖倒置原则规定了对抽象的依赖,而不是具体的。

    同样,我们可能会寻求一个简单的解决方案。将doWork() 方法提取到Employee 实现的接口。现在客户可以在遵守 DIP 的同时调用它。 (而且由于我们在考虑 SOLID 原则的交互,还假设 doWork() 的实现遵守其抽象声明的约定,这样我们就不会违反 LSP。)

    当然,现在 OCP 适用于Employee,对吧?它同时具有状态和行为(使其成为成熟的 OO 对象),并通过适当的抽象公开其行为。但是 OCP 仍然不适用……因为 Employee 现在是一个实现细节。根据 OCP,“关闭”的定义包括发布供客户(即公众)使用。实施细节不属于此定义。

    所以通过结合 SOLID 原则,我们得出了一个重要的结论。

    OCP 严格适用于面向对象的应用程序或库的公共、抽象、API。

    随意编辑您的实施细节。 OCP 不介意。

    【讨论】:

    • 好点!!这里有两件事在起作用,我希望我能对这个问题提供更详细的答案。第一点,“员工”是“人”的角色。因此,应该注入“Employee”行为,而不是通过继承强制执行。第二点是,如果你的设计一开始就有缺陷,那么修改一个糟糕的实现不仅没问题,而且实际上是最好的做法。仅仅说我遵循了 SOLID 原则,这种“SOLID 或破产”的态度是没有意义的。
    • 事实上,继续我之前的观点,创建一个继承自“Person”的“Employee”类是非常糟糕的设计。为什么?您将如何处理退休或被解雇并在一段时间内失业的“人”?你将无法做到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多