对 open/closed 校长感到困惑
Confusion about open/closed principal
Open/closed 原则指出 classes 对修改关闭但对扩展开放。假设我们要设计一个支付系统,支付可以由多个处理器处理,如下所示:
class Payment {
void pay(paymentMethod) {
switch (paymentMethod) {
case 'PayPal':
break;
case 'Swift':
break;
default:
break;
}
}
}
class PayPal {
void pay() {
//implement payment1
}
}
class Swift {
void pay() {
//implement payment2
}
}
假设我们是第一次实施这两种支付系统。现在,如果由于某种原因更改了任何支付系统实施流程,我们是否应该修改相关的 class?比如我们实现PayPal,2-3年后PayPal的工作流程发生了变化,修改PayPalclass不违反open/closed原则吗?如果是,解决方案是什么?
Now if for some reason any payment system implementation process is changed, should not we have to modify the relevant class? For example, if we implement PayPal and after 2-3 years PayPal's working process is changed, does not modifying the PayPal class break the open/closed principle?
不,它不会破坏它。如果 PayPal 的工作过程发生变化,它必须反映在扩展 PayPal payment method
.
的 class 中
我给你举个例子:假设明天你想再添加一个 payment method - Transferwise
现在模式告诉我们“软件实体(classes,模块,函数,等)应该对扩展开放,但对修改关闭”,这意味着如果您需要修改任何现有的 class 以添加新的 payment method
,那么您就违反了 open/closed 原则,并且另一方面,如果您可以在新的 Transferwise
class 中扩展 PaymentMethod
,那么您就是在不做任何更改的情况下扩展您的系统,并且您遵守模式
在 Payment
classes 中包含 switch
语句打破了 open/closed 原则,因为它使 Payment
的抽象概念与具体实现 PayPal
和 Swift
。要添加或删除受支持的付款类型,您必须编辑 Payment.pay()
方法。
更好的设计使用 interface
来描述支付提供商的外观。在这种情况下,它必须有一个 void pay()
方法。
不是将 paymentMethod
参数作为 string
,Payment.pay()
应该接受实现支付提供商接口的 class 实例。它可以调用 paymentMethod.pay()
来执行正确的功能。 (根据您的实际设置,将此参数传递给构造函数可能比传递给方法更好)。
通过这种方式,添加或删除支付提供商变得非常容易,因为 Payment
class 不需要任何有关 class 提供商存在的信息。
interface PaymentProvider {
void pay();
}
class Payment {
void pay(paymentMethod: PaymentProvider) {
paymentMethod.pay();
}
class PayPal implements PaymentProvider {
void pay() {
//implement payment1
}
}
class Swift implements PaymentProvider {
void pay() {
//implement payment2
}
}
我认为这里的问题归结为 Open/Closed 原则的定义。具体来说,真的 意味着代码在编写后不应更改吗?
虽然很多人(包括我自己)已经使用该定义来替代 OCP,但它过于简单化了。 OCP 最初由 Bertrand Meyer 在 Object-Oriented Software Construction 中发布。我想这个问题的答案可以在本书的第二版中找到,从第 60 页开始,其中指出了 OCP 的一些“例外”。
- If you have control over the original software and can rewrite it so that it will address the needs of several kinds of client at no extra complication, you should do so.
- Neither the Open-Closed principle nor redefinition in inheritance is a way to address
design flaws, let alone bugs. If there is something wrong with a module, you should
fix it — not leave the original as it is and try to correct the problem in a derived
module... The Open-Closed principle and associated
techniques are intended for the adaptation of healthy modules: modules that,
although they may not suffice for some new uses, meet their own well-defined
requirements, to the satisfaction of their own clients.
显然,Meyer 并没有打算永远不重写遗留代码。如果新的要求使现有逻辑的一部分无效,重写它可能是一种明智的做法,并且从来没有打算被 OCP 禁止。
Open/closed 原则指出 classes 对修改关闭但对扩展开放。假设我们要设计一个支付系统,支付可以由多个处理器处理,如下所示:
class Payment {
void pay(paymentMethod) {
switch (paymentMethod) {
case 'PayPal':
break;
case 'Swift':
break;
default:
break;
}
}
}
class PayPal {
void pay() {
//implement payment1
}
}
class Swift {
void pay() {
//implement payment2
}
}
假设我们是第一次实施这两种支付系统。现在,如果由于某种原因更改了任何支付系统实施流程,我们是否应该修改相关的 class?比如我们实现PayPal,2-3年后PayPal的工作流程发生了变化,修改PayPalclass不违反open/closed原则吗?如果是,解决方案是什么?
Now if for some reason any payment system implementation process is changed, should not we have to modify the relevant class? For example, if we implement PayPal and after 2-3 years PayPal's working process is changed, does not modifying the PayPal class break the open/closed principle?
不,它不会破坏它。如果 PayPal 的工作过程发生变化,它必须反映在扩展 PayPal payment method
.
我给你举个例子:假设明天你想再添加一个 payment method - Transferwise
现在模式告诉我们“软件实体(classes,模块,函数,等)应该对扩展开放,但对修改关闭”,这意味着如果您需要修改任何现有的 class 以添加新的 payment method
,那么您就违反了 open/closed 原则,并且另一方面,如果您可以在新的 Transferwise
class 中扩展 PaymentMethod
,那么您就是在不做任何更改的情况下扩展您的系统,并且您遵守模式
在 Payment
classes 中包含 switch
语句打破了 open/closed 原则,因为它使 Payment
的抽象概念与具体实现 PayPal
和 Swift
。要添加或删除受支持的付款类型,您必须编辑 Payment.pay()
方法。
更好的设计使用 interface
来描述支付提供商的外观。在这种情况下,它必须有一个 void pay()
方法。
不是将 paymentMethod
参数作为 string
,Payment.pay()
应该接受实现支付提供商接口的 class 实例。它可以调用 paymentMethod.pay()
来执行正确的功能。 (根据您的实际设置,将此参数传递给构造函数可能比传递给方法更好)。
通过这种方式,添加或删除支付提供商变得非常容易,因为 Payment
class 不需要任何有关 class 提供商存在的信息。
interface PaymentProvider {
void pay();
}
class Payment {
void pay(paymentMethod: PaymentProvider) {
paymentMethod.pay();
}
class PayPal implements PaymentProvider {
void pay() {
//implement payment1
}
}
class Swift implements PaymentProvider {
void pay() {
//implement payment2
}
}
我认为这里的问题归结为 Open/Closed 原则的定义。具体来说,真的 意味着代码在编写后不应更改吗?
虽然很多人(包括我自己)已经使用该定义来替代 OCP,但它过于简单化了。 OCP 最初由 Bertrand Meyer 在 Object-Oriented Software Construction 中发布。我想这个问题的答案可以在本书的第二版中找到,从第 60 页开始,其中指出了 OCP 的一些“例外”。
- If you have control over the original software and can rewrite it so that it will address the needs of several kinds of client at no extra complication, you should do so.
- Neither the Open-Closed principle nor redefinition in inheritance is a way to address design flaws, let alone bugs. If there is something wrong with a module, you should fix it — not leave the original as it is and try to correct the problem in a derived module... The Open-Closed principle and associated techniques are intended for the adaptation of healthy modules: modules that, although they may not suffice for some new uses, meet their own well-defined requirements, to the satisfaction of their own clients.
显然,Meyer 并没有打算永远不重写遗留代码。如果新的要求使现有逻辑的一部分无效,重写它可能是一种明智的做法,并且从来没有打算被 OCP 禁止。