设计模式学习之开闭原则

学习内容均来自抖音号 【it楠老师教java】课程。

1、原理概述

开闭原则的英文全称是 Open Closed Principle,简写为 OCP。它的英文描述是:software entities (modules, classes, functions, etc.) should be open for extension , but closed for modification。我们把它翻译成中文就是:软件实体(模块、类、方法等)应该“对扩展开放、对修改关闭”。说人话就是,当我们需要添加一个新的功能时,应该在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。以下是一个常见的生产环境中的例子,我们将展示一个简化的电商平台的订单折扣策略。
你觉得下边的代码有问题吗?

class Order {
private double totalAmount ;
public Order ( double totalAmount ) {
this . totalAmount = totalAmount ;
}
// 计算折扣后的金额
public double getDiscountedAmount ( String discountType ) {
double discountedAmount = totalAmount ;
if ( discountType . equals ( "FESTIVAL" )) {
discountedAmount = totalAmount * 0.9 ; // 节日折扣, 9 }
else if ( discountType . equals ( "SEASONAL" )) {
discountedAmount = totalAmount * 0.8 ; // 季节折扣, 8
}
return discountedAmount ;
}
}

上述代码中, Order 类包含一个计算折扣金额的方法,它根据不同的折扣类型应用 折扣。当我们需要添加新的折扣类型时,就不得不需要修改 getDiscountedAmount 方法的代码,这显然是不合理的,这就违反了开闭原则。

遵循开闭原则的代码: 

// 抽象折扣策略接口
interface DiscountStrategy {
double getDiscountedAmount ( double totalAmount );
}
// 节日折扣策略
class FestivalDiscountStrategy implements DiscountStrategy {
@Override
public double getDiscountedAmount ( double totalAmount ) {
return totalAmount * 0.9 ; // 9
}
}
// 季节折扣策略
class SeasonalDiscountStrategy implements DiscountStrategy {
@Override
public double getDiscountedAmount ( double totalAmount ) {
return totalAmount * 0.8 ; // 8
}
}
class Order {
private double totalAmount ;
private DiscountStrategy discountStrategy ;
public Order ( double totalAmount , DiscountStrategy discountStrategy ) {
this . totalAmount = totalAmount ;
this . discountStrategy = discountStrategy ; }

在遵循开闭原则的代码中,我们定义了一个抽象的折扣策略接口 DiscountStrategy ,然后为每种折扣类型创建了一个实现该接口的策略类。 Order 类使用组合的方式,包含一个 DiscountStrategy 类型的成员变量,以便 在运行时设置或更改折扣策略,(可以通过编码,配置、依赖注入等形式)。这样, 当我们需要添加新的折扣类型时,只需实现 DiscountStrategy 接口即可,而无需 修改现有的 Order 代码。这个例子遵循了开闭原则。

这里相信很多初级程序员看到都会有种恍然大悟的感觉,或者某些中级程序员不注重代码风格的人也会有所顿悟,csdn上写设计模式的人有一大堆,但是没人告诉你怎么用,这个写的确实不错,都是写程序的人了,十几二十块真不算什么,这个老师还是有点水平的。

2、修改代码就意味着违背开闭原则吗

开闭原则的核心思想是要尽量减少对现有代码的修改,以降低修改带来的风险和影 响。在实际开发过程中,完全不修改代码是不现实的。当需求变更或者发现代码中的 错误时,修改代码是正常的。然而,开闭原则鼓励我们通过设计更好的代码结构,使 得在添加新功能或者扩展系统时,尽量减少对现有代码的修改。 以下是一个简化的日志记录器的示例,展示了在适当情况下修改代码,也不违背开闭 原则。在这个例子中,我们的应用程序支持将日志输出到控制台和文件。假设我们需 要添加一个新功能,以便在输出日志时同时添加一个时间戳。

原始代码:
interface Logger {
void log ( String message );
}
class ConsoleLogger implements Logger {
@Override
public void log ( String message ) {
System . out . println ( "Console: " + message ); }
}
class FileLogger implements Logger {
@Override
public void log ( String message ) {
System . out . println ( "File: " + message );
// 将日志写入文件的实现省略
}
}

为了添加时间戳功能,我们需要修改现有的 ConsoleLogger 和 FileLogger 类。 虽然我们需要修改代码,但由于这是对现有功能的改进,而不是添加新的功能,所以 这种修改是可以接受的,不违背开闭原则。 

修改后的代码:
interface Logger {
void log ( String message );
}
class ConsoleLogger implements Logger {
@Override
public void log ( String message ) {
String timestamp =
LocalDateTime . now (). format ( DateTimeFormatter . ISO_LOCAL_DATE_TIME );
System . out . println ( "Console [" + timestamp + "]: " + message );
}
}
class FileLogger implements Logger {
@Override
public void log ( String message ) {
String timestamp =
LocalDateTime . now (). format ( DateTimeFormatter . ISO_LOCAL_DATE_TIME );
String logMessage = "File [" + timestamp + "]: " + message ;
System . out . println ( logMessage );
// 将日志写入文件的实现省略
}
}

在这个例子中,我们只是对现有的日志记录器类进行了适当的修改,以添加时间戳功能。这种修改不会影响到其他部分的代码,因此不违背开闭原则。总之,适当的修改 代码并不一定违背开闭原则,关键在于我们如何权衡修改的影响和代码设计。 

当我们遵循开闭原则时,其目的是为了让我们的代码更容易维护、更具可复用性,同时降低了引入新缺陷的风险。但是,在某些情况下,遵循开闭原则可能会导致过度设计,增加代码的复杂性。因此,在实际开发中,我们应该根据实际需求和预期的变化来平衡遵循开闭原则的程度。写代码不是为了设计而设计,脱离需求谈设计都是耍流氓,有些场景,比如项目的使用频率不高,修改的可能性很低,或者代码本来就很简单使用了设计模式可能会增加开发难度,提升开发成本,反而得不偿失。

猜你喜欢

转载自blog.csdn.net/cclovezbf/article/details/132016206