一文搞懂开放封闭原则

开放封闭原则(Open-Closed Principle, OCP)是面向对象设计(Object-Oriented Design)的基本原则之一,最早由 Bertrand Meyer 在其1988年的著作《Object-Oriented Software Construction》中提出。该原则的主要内容如下:

开放封闭原则定义

开放封闭原则表明:

软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。这意味着当需求发生变化时,我们应该能够在不修改原有代码的基础上,通过新增代码的方式来满足新的需求。

主要要点

  1. 扩展性: 软件实体应该容易扩展,以便在不触及原有结构和功能的基础上增加新的功能。设计时应预见到未来可能的变化,并创建抽象层和扩展点,使新功能可以通过扩展这些点来实现。
  2. 稳定性: 已经部署并经过验证的软件实体(尤其是稳定的底层组件和基础架构)应该尽可能避免修改。频繁修改既有的代码不仅增加了出错的风险,还可能引发连锁反应,影响到依赖于该代码的其他部分。

实现方式

  • 抽象类与接口:定义抽象类或接口来描述共同行为,然后通过创建新的派生类或实现接口来增加功能,而不是修改原有类。
  • 策略模式:定义一组算法族,分别封装在不同的类中,使得它们之间可以互相替换。策略模式允许算法独立于使用它的客户代码变化。
  • 装饰者模式:动态地给对象添加职责,提供了比继承更为灵活的替代方案,允许在运行时改变对象的行为。
  • 工厂方法模式:创建对象的接口,但让子类决定实例化哪一个类。这样,系统就能在不修改自身的情况下扩展产品类系列。

示例说明

下面我将以JavaScript为例,展示如何运用开放封闭原则(OCP)设计一个简单的计费系统。假设我们开始时有一个处理普通订单的计费服务,之后又需要支持VIP用户的折扣计费。

初始版本:普通订单计费服务
javascript 复制代码
// 假设有一个基础的订单类
class Order {
  constructor(totalPrice) {
    this.totalPrice = totalPrice;
  }
}

// 普通订单计费服务
class BillingService {
  calculateOrderCost(order) {
    if (!(order instanceof Order)) {
      throw new Error('Invalid order');
    }
    
    return order.totalPrice;
  }
}

// 创建一个普通订单
const regularOrder = new Order(100);

// 创建并使用计费服务
const billing = new BillingService();
console.log(billing.calculateOrderCost(regularOrder)); // 输出:100
扩展版本:支持VIP订单
javascript 复制代码
// 新增VIP订单类,它继承自Order
class VIPOrder extends Order {
  constructor(totalPrice, vipDiscount) {
    super(totalPrice);
    this.vipDiscount = vipDiscount;
  }

  applyVipDiscount() {
    return this.totalPrice * (1 - this.vipDiscount);
  }
}

// 创建一个VIP订单计费策略类,遵循策略模式
class VipBillingStrategy {
  calculateOrderCost(order) {
    if (order instanceof VIPOrder) {
      return order.applyVipDiscount();
    } else {
      throw new Error('This is not a VIP order');
    }
  }
}

// 修改BillingService,使其支持不同的计费策略
class BillingService {
  constructor(strategy = new DefaultBillingStrategy()) {
    this.strategy = strategy;
  }

  setStrategy(strategy) {
    this.strategy = strategy;
  }

  calculateOrderCost(order) {
    return this.strategy.calculateOrderCost(order);
  }
}

// 创建一个默认计费策略,用于处理普通订单
class DefaultBillingStrategy {
  calculateOrderCost(order) {
    if (order instanceof Order) {
      return order.totalPrice;
    } else {
      throw new Error('Invalid order');
    }
  }
}

// 使用VIP订单并切换计费策略
const vipOrder = new VIPOrder(200, 0.2); // 假设VIP有20%的折扣

const billing = new BillingService(new DefaultBillingStrategy());
billing.setStrategy(new VipBillingStrategy());

console.log(billing.calculateOrderCost(vipOrder)); // 输出:160

// 对于普通订单,仍然可以使用同样的计费服务
console.log(billing.calculateOrderCost(regularOrder)); // 输出:100

在这个例子中,我们遵循开放封闭原则:

  • 对扩展开放 :我们通过创建VIPOrder类以及VipBillingStrategy类,对VIP订单的处理进行了扩展,没有修改原有的Order类或BillingService的基础功能。
  • 对修改封闭 :原有的Order类和最初版本的BillingService在面对新的需求时没有被直接修改,而是通过添加新的类和策略来实现功能扩展。

为何重要

遵循开放封闭原则有助于:

  • 提高复用性:已经测试过的、稳定的代码更容易重复利用。
  • 降低耦合度:减少各个模块之间的相互依赖,使得系统更具灵活性。
  • 维护性提升:变更时只需关注新增代码,大大降低了潜在的bug引入风险。
  • 遵循开闭原则是实现软件系统持续演进的关键手段,它使得系统能够应对不断变化的需求,同时保持系统整体的稳定和可控性。
相关推荐
长风清留扬6 分钟前
小程序开发实战项目:构建简易待办事项列表
javascript·css·微信小程序·小程序·apache
程序员_三木7 分钟前
从 0 到 1 实现鼠标联动粒子动画
javascript·计算机外设·webgl·three.js
点点滴滴的记录13 分钟前
Java的CompletableFuture实现原理
java·开发语言·javascript
程序猿online16 分钟前
nvm安装使用,控制node版本
开发语言·前端·学习
web Rookie26 分钟前
React 中 createContext 和 useContext 的深度应用与优化实战
前端·javascript·react.js
lijiachang03071829 分钟前
设计模式(一):单例模式
c++·笔记·学习·程序人生·单例模式·设计模式·大学生
男孩1230 分钟前
react高阶组件及hooks
前端·javascript·react.js
m0_748251721 小时前
DataOps驱动数据集成创新:Apache DolphinScheduler & SeaTunnel on Amazon Web Services
前端·apache
珊珊来吃1 小时前
EXCEL中给某一列数据加上双引号
java·前端·excel
抓哇FullStack-Junior1 小时前
设计模式——适配器模式
java·设计模式·适配器模式