开闭原则背后的含义和推理是什么?
开放/封闭原则规定软件实体(类、模块等)应该对扩展开放,但对修改关闭。 这意味着什么?为什么它是良好的面向对象设计的重要原则?
The Open/Closed Principle states that software entities (classes, modules, etc.) should be open for extension, but closed for modification. What does this mean, and why is it an important principle of good object-oriented design?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
这意味着您应该将新代码放入新的类/模块中。 现有代码应仅针对错误修复进行修改。 新类可以通过继承重用现有代码。
开放/封闭原则旨在降低引入新功能时的风险。 由于您不修改现有代码,因此可以放心它不会被破坏。 它降低了维护成本并提高了产品稳定性。
It means that you should put new code in new classes/modules. Existing code should be modified only for bug fixing. New classes can reuse existing code via inheritance.
Open/closed principle is intended to mitigate risk when introducing new functionality. Since you don't modify existing code you can be assured that it wouldn't be broken. It reduces maintenance cost and increases product stability.
具体来说,它是关于 OOP 中设计的“圣杯”,即使实体具有足够的可扩展性(通过其单独的设计或通过其对架构的参与)以支持未来不可预见的更改,而无需重写其代码(有时甚至无需重新编译) **)。
实现此目的的一些方法包括多态性/继承、组合、控制反转(又名 DIP)、面向方面的编程、策略、访问者、模板方法等模式以及 OOAD 的许多其他原则、模式和技术。
** 请参阅 6 个“一揽子原则”,REP、CCP、CRP、ADP、SDP、SAP
Specifically, it is about a "Holy Grail" of design in OOP of making an entity extensible enough (through its individual design or through its participation in the architecture) to support future unforseen changes without rewriting its code (and sometimes even without re-compiling **).
Some ways to do this include Polymorphism/Inheritance, Composition, Inversion of Control (a.k.a. DIP), Aspect-Oriented Programming, Patterns such as Strategy, Visitor, Template Method, and many other principles, patterns, and techniques of OOAD.
** See the 6 "package principles", REP, CCP, CRP, ADP, SDP, SAP
比 DaveK 更具体的是,它通常意味着如果您想要添加附加功能或更改类的功能,请创建一个子类而不是更改原始类。 这样,任何使用父类的人都不必担心它以后会发生变化。 基本上,这都是关于向后兼容性。
面向对象设计的另一个非常重要的原则是通过方法接口进行松耦合。 如果您想要进行的更改不会影响现有界面,那么更改确实非常安全。 例如,使算法更加高效。 面向对象的原则也需要受到常识的锻炼:)
More specifically than DaveK, it usually means that if you want to add additional functionality, or change the functionality of a class, create a subclass instead of changing the original. This way, anyone using the parent class does not have to worry about it changing later on. Basically, it's all about backwards compatibility.
Another really important principle of object-oriented design is loose coupling through a method interface. If the change you want to make does not affect the existing interface, it really is pretty safe to change. For example, to make an algorithm more efficient. Object-oriented principles need to be tempered by common sense too :)
让我们将问题分为三个部分,以便更容易理解各种概念。
开闭原则背后的推理
考虑下面代码中的示例。 不同的车辆以不同的方式进行维修。 因此,我们为
Bike
和Car
提供了不同的类,因为服务Bike
的策略与服务Car 的策略不同
。 Garage 类接受各种类型的车辆进行维修。刚性问题
观察代码,看看
Garage
类在引入新功能时如何表现出刚性的迹象:正如您可能已经注意到的,每当出现某种新车辆时例如要为
Truck
或Bus
提供服务,需要修改Garage
来定义一些新方法,例如serviceTruck()< /code> 和
serviceBus()
。 这意味着Garage
类必须知道每种可能的车辆,例如Bike
、Car
、Bus
、Truck< /代码>等等。 因此,它对修改开放,违反了开闭原则。 而且它不开放扩展,因为要扩展新功能,我们需要修改该类。
开闭原则背后的含义
抽象
为了解决上面代码中的僵化问题,我们可以使用开闭原则。 这意味着我们需要通过去掉 Garage 类所知道的每辆车的服务实现细节来使其变得愚蠢。 换句话说,我们应该抽象出每种具体类型(例如
Bike
和Car
)的服务策略的实现细节。为了抽象各种类型车辆的服务策略的实现细节,我们使用一个名为
Vehicle
的接口,并在其中有一个抽象方法service()
它。多态
同时,我们还希望
Garage
类能够接受多种形式的车辆,例如Bus
、卡车
等等,而不仅仅是自行车
和汽车
。 为此,开闭原则使用了多态性(多种形式)。为了让
Garage
类接受多种形式的Vehicle
,我们将其方法的签名更改为service(Vehiclevehicle) { }
以接受接口Vehicle
而不是实际的实现,如Bike
、Car
等。我们还从类中删除了多个方法,因为只有一个方法会接受多种形式。开闭原则的重要性
对修改关闭
正如您在上面的代码中看到的,现在
Garage
类已经对修改关闭了,因为现在它不知道各种类型车辆的服务策略的实施细节,并且可以接受任何类型的新车辆
。 我们只需从Vehicle
接口扩展新车辆并将其发送到Garage
即可。 就是这样! 我们不需要更改 Garage 类中的任何代码。另一个关闭修改的实体是我们的
Vehicle
接口。我们不必更改界面来扩展软件的功能。
开放扩展
Garage
类现在开放扩展,因为它将支持新类型的Vehicle
,无需进行修改。我们的
Vehicle
接口对于扩展是开放的,因为要引入任何新的车辆,我们可以从Vehicle
接口进行扩展,并提供一个新的实现以及服务该特定车辆的策略。策略设计模式
你注意到我多次使用了策略这个词吗? 那是因为这也是策略设计模式的一个示例。 我们可以通过扩展来实施不同的策略来服务不同类型的车辆。 例如,为
卡车
提供服务与为公共汽车
提供服务的策略不同。 因此,我们在不同的派生类中实现这些策略。策略模式使我们的软件能够随着需求随时间的变化而灵活变化。 每当客户改变策略时,只需为其派生一个新类并将其提供给现有组件,无需更改其他内容! 开闭原则在实现这一模式中发挥着重要作用。
就是这样! 希望有帮助。
Let's break down the question in three parts to make it easier to understand the various concepts.
Reasoning Behind Open-Closed Principle
Consider an example in the code below. Different vehicles are serviced in a different manner. So, we have different classes for
Bike
andCar
because the strategy to service aBike
is different from the strategy to service aCar
. TheGarage
class accepts various kinds of vehicles for servicing.Problem of Rigidity
Observe the code and see how the
Garage
class shows the signs of rigidity when it comes to introducing a new functionality:As you may have noticed, whenever some new vehicle like
Truck
orBus
is to be serviced, theGarage
will need to be modified to define some new methods likeserviceTruck()
andserviceBus()
. That means theGarage
class must know every possible vehicle likeBike
,Car
,Bus
,Truck
and so on. So, it violates the open-closed principle by being open for modification. Also it's not open for extension because to extend the new functionality, we need to modify the class.Meaning Behind Open-Closed Principle
Abstraction
To solve the problem of rigidity in the code above we can use the open-closed principle. That means we need to make the
Garage
class dumb by taking away the implementation details of servicing of every vehicle that it knows. In other words we should abstract the implementation details of the servicing strategy for each concrete type likeBike
andCar
.To abstract the implementation details of the servicing strategies for various types of vehicles we use an
interface
calledVehicle
and have an abstract methodservice()
in it.Polymorphism
At the same time, we also want the
Garage
class to accept many forms of the vehicle, likeBus
,Truck
and so on, not justBike
andCar
. To do that, the open-closed principle uses polymorphism (many forms).For the
Garage
class to accept many forms of theVehicle
, we change the signature of its method toservice(Vehicle vehicle) { }
to accept the interfaceVehicle
instead of the actual implementation likeBike
,Car
etc. We also remove the multiple methods from the class as just one method will accept many forms.Importance of Open-Closed Principle
Closed for modification
As you can see in the code above, now the
Garage
class has become closed for modification because now it doesn't know about the implementation details of servicing strategies for various types of vehicles and can accept any type of newVehicle
. We just have to extend the new vehicle from theVehicle
interface and send it to theGarage
. That's it! We don't need to change any code in theGarage
class.Another entity that's closed for modification is our
Vehicle
interface.We don't have to change the interface to extend the functionality of our software.
Open for extension
The
Garage
class now becomes open for extension in the context that it will support the new types ofVehicle
, without the need for modifying.Our
Vehicle
interface is open for extension because to introduce any new vehicle, we can extend from theVehicle
interface and provide a new implementation with a strategy for servicing that particular vehicle.Strategy Design Pattern
Did you notice that I used the word strategy multiple times? That's because this is also an example of the Strategy Design Pattern. We can implement different strategies for servicing different types of
Vehicle
s by extending it. For example, servicing aTruck
has a different strategy from the strategy of servicing aBus
. So we implement these strategies inside the different derived classes.The strategy pattern allows our software to be flexible as the requirements change over time. Whenever the client changes their strategy, just derive a new class for it and provide it to the existing component, no need to change other stuff! The open-closed principle plays an important role in implementing this pattern.
That's it! Hope that helps.
开闭原则在面向对象编程中非常重要,它是SOLID原则之一。
开闭原则:
Open Closed Principle is very important in object oriented programming and it's one of the SOLID principles.
With Open/Closed Principle:
这意味着任何类或模块都应该以可以按原样使用、可以扩展但不能修改的方式编写,
Javascript 中的错误示例
现在如果你想添加另一个果汁,那么 type,你必须编辑模块本身,这样我们就破坏了 OCP 。
Javascript 的好例子
现在,您可以从模块外部添加新的果汁类型,而无需编辑同一模块。
That means any class or module should be written in a way that it can be used as is, can be extended, but neve modified
Bad Example in Javascript
Now if you want to add Another Juice type, you have to edit the module itself, By this way, we are breaking OCP .
Good Example in Javascript
Now, you can add new juice types from outside the module without editing the same module.
这是脆弱基类问题的答案,即对基类看似无害的修改可能会对依赖于先前行为的继承者产生意想不到的后果。 因此,您必须小心地封装您不希望依赖的内容,以便派生类将遵守基类定义的契约。 一旦存在继承者,您就必须非常小心在基类中所做的更改。
It's the answer to the fragile base class problem, which says that seemingly innocent modifications to base classes may have unintended consequences to inheritors that depended on the previous behavior. So you have to be careful to encapsulate what you don't want relied upon so that the derived classes will obey the contracts defined by the base class. And once inheritors exist, you have to be really careful with what you change in the base class.
SOLID 原则中的O开放原则的目的是
让我通过 AppLogger util 类来解释这一点。
假设我们需要将应用程序范围内的错误记录到名为 Firebase 的在线工具中。 因此,我们创建下面的类,并在数千个地方使用它来记录 API 错误、内存不足错误等。
假设一段时间后,我们将支付功能添加到应用程序中,并且有一个新要求,规定仅针对与支付相关的错误必须使用名为 Instabug 的新报告工具,并像以前一样继续向 Firebase 报告包括付款在内的所有功能的错误。
现在我们可以通过在现有方法中添加 if else 条件来实现此目的。
这种方法的问题是它违反了单一责任原则,该原则规定一个方法应该只做一件事。 另一种表达方式是一个方法应该只有一个改变的理由。 对于这种方法,有两个原因导致该方法发生变化(if 和 else 块)。
更好的方法是通过继承现有 Logger 类来创建新的 Logger 类,如下所示。
现在我们要做的就是在 Payment 功能中使用 InstaBugLogger.logError() 将错误记录到 Instabug 和 Firebase。 通过这种方式,我们可以减少/隔离新错误报告要求的测试,仅针对支付功能,因为代码更改仅在支付功能中完成。 其余的应用程序功能不需要测试,因为没有对现有记录器进行代码更改。
Purpose of the Open closed Principle in SOLID Principles is to
Let me explain this by taking AppLogger util class.
Let's say we have a requirement to log application wide errors to a online tool called Firebase. So we create below class and use it in 1000s of places to log API errors, out of memory errors etc.
Let's say after sometime, we add Payment Feature to the app and there is a new requirement which states that only for Payment related errors we have to use a new reporting tool called Instabug and also continue reporting errors to Firebase just like before for all features including Payment.
Now we can achieve this by putting an if else condition inside our existing method
Problem with this approach is that it violates Single Responsibility Principle which states that a method should do only one thing. Another way of putting it is a method should have only one reason to change. With this approach there are two reasons for this method to change (if & else blocks).
A better approach would be to create a new Logger class by inheriting the existing Logger class like below.
Now all we have to do is use InstaBugLogger.logError() in Payment features to log errors to both Instabug and Firebase. This way we reduce/isolate the testing of new error reporting requirement to only Payment feature as code changes are done only in Payment Feature. The rest of the application features need not be tested as there are no code changes done to the existing Logger.
该原则意味着可以轻松添加新功能,而无需更改现有的、稳定的和经过测试的功能,从而节省时间和金钱。
通常,多态性(例如使用接口)是实现这一目标的好工具。
The principle means that it should easy to add new functionality without having to change existing, stable, and tested functionality, saving both time and money.
Often, polymorhism, for instance using interfaces, is a good tool for achieving this.
符合 OCP 的另一个经验法则是使基类相对于派生类提供的功能进行抽象。 或者正如 Scott Meyers 所说的“使非叶类抽象”。
这意味着基类中具有未实现的方法,并且仅在本身没有子类的类中实现这些方法。 那么基类的客户端就不能依赖基类中的特定实现,因为基类中没有特定的实现。
An additional rule of thumb for conforming to OCP is to make base classes abstract with respect to functionality provided by derived classes. Or as Scott Meyers says 'Make Non-leaf classes abstract'.
This means having unimplemented methods in the base class and only implement these methods in classes which themselves have no sub classes. Then the client of the base class cannot rely on a particular implementation in the base class since there is none.
我只是想强调,“开放/封闭”虽然在面向对象编程中明显有用,但在开发的各个方面都是一种健康的方法。 例如,根据我自己的经验,在使用普通 C 语言时,尽可能多地使用“开/关”是一种很好的止痛药
。/Robert
I just want to emphasize that "Open/Closed", even though being obviously useful in OO programming, is a healthy method to use in all aspects of development. For instance, in my own experience it's a great painkiller to use "Open/Closed" as much as possible when working with plain C.
/Robert
这意味着应该构建面向对象的软件,但不应从本质上进行更改。 这很好,因为它确保了基类的可靠、可预测的性能。
This means that the OO software should be built upon, but not changed intrinsically. This is good because it ensures reliable, predictable performance from the base classes.
最近,我对这一原则的含义有了更多的了解:开闭原则既描述了一种编写代码的方式,也描述了以弹性方式编写代码的最终结果。
我喜欢将开放/封闭分为两个密切相关的部分:
因此,表现出开放/封闭行为(或者,如果您愿意,满足开放/封闭原则)的代码需要进行最少的修改或无需修改,以响应超出其最初构建目的的使用场景。
至于实施方面? 我发现通常所说的解释“开放/封闭是指代码是多态的!” 充其量只是一个不完整的陈述。 代码中的多态性是实现这种行为的一种工具。 继承、实现……实际上,每一个面向对象的设计原则对于编写按照该原则所暗示的方式具有弹性的代码都是必要的。
I was recently given an additional idea of what this principle entails: that the Open-Closed Principle describes at once a way of writing code, as well as an end-result of writing code in a resilient way.
I like to think of Open/Closed split up in two closely-related parts:
Thus, code that exhibits Open/Closed behavior (or, if you prefer, fulfills the Open/Closed Principle) requires minimal or no modification in response to usage scenarios beyond what it was originally built for.
As far as implementation is concerned? I find that the commonly-stated interpretation, "Open/Closed refers to code being polymorphic!" to be at best an incomplete statement. Polymorphism in code is one tool to achieve this sort of behavior; Inheritance, Implementation...really, every object-oriented design principle is necessary to write code that is resilient in the way implied by this principle.
在设计原则中,SOLID——“SOLID”中的“O”代表开/闭原则。
开闭原则是一种设计原则,它规定类、模块和函数应该对扩展开放,对修改封闭。
该原则指出,代码的设计和编写应以在现有代码(经过测试的代码)中进行最少更改的情况下添加新功能的方式完成。 设计的方式应该允许添加新功能作为新类,并尽可能保持现有代码不变。
开放封闭设计原则的好处:
我对此的博客文章:
http://javaexplorer03.blogspot。在/2016/12/open-close-design-principle.html
In Design principle, SOLID – the "O" in "SOLID" stands for the open/closed principle.
Open Closed principle is a design principle which says that a class, modules and functions should be open for extension but closed for modification.
This principle states that the design and writing of the code should be done in a way that new functionality should be added with minimum changes in the existing code (tested code). The design should be done in a way to allow the adding of new functionality as new classes, keeping as much as possible existing code unchanged.
Benefit of Open Closed Design Principle:
My blog post on this:
http://javaexplorer03.blogspot.in/2016/12/open-closed-design-principle.html