开闭原则(Open Closed Principle, OCP)是Bertrand Meyer在1988年提出的,该设计原则认为:
一个设计良好的计算机系统应该易于扩展(对扩展开放),同时抗拒修改(对修改关闭)。
其实这也是我们设计软件架构的根本目的。如果对于小小的需求改动,而需要大幅度地修改整个软件架构,这种牵一发而动全身的设计显然是要避免的。下面,我们通过一个案例来对开闭原则做一些说明。
案例: 电商交易日志系统
假设我们要设计一个电商交易日志系统,用于记录用户的交易行为记录。如:用户在某个电商平台购买商品,购买商品经过了以下步骤或页面操作,浏览商品目录 -> 查看商品详情 -> 加入购物车 -> 提交订单 -> 填写收货信息 -> 支付等。该交易日志系统以日志的方式记录了用户购物的操作步骤,从而可以分析出某个用户购买行为存在的问题,这样就可以优化和调整购物操作流程,提高用户购物的转化率。
最开始用户量较少,存储方式也比较简单,选择了使用Mysql作为存储日志的数据库。用户的每次操作,都写入一笔记录到Mysql,然后使用SQL语句来分析用户购买行为。系统架构如下:
为了方面演示,这里只给出了两个重要组件:业务服务组件(电商业务逻辑处理组件)、日志记录组件(日志存储和分析组件),使用的是Mysql数据库。业务服务组件使用到了日志的存储和分析等操作,所以这里的业务服务组件依赖于日志记录组件。
随着电商系统的用户量的增加,日志量也会大幅度增加,使用Mysql存储和分析日志的瓶颈也越来越明显。传统的关系型数据库在横向扩展和大数据量处理方面比较吃力,为了解决这个问题,开发部门决定使用Hadoop来作为日志的存储和分析。
但是,由于之前的架构设计导致业务服务组件依赖于日志记录组件,要把Mysql替换为Hadoop的话,也需要修改业务服务组件代码。业务服务组件作为最核心的组件,现在为了修改日志记录组件而受到影响,这是开发者最不愿遇到的情况,最核心的组件应该是最稳定的和抗拒修改的。
为了避免核心组件受到不必要的影响,这里我们用到一个设计原则:如果A组件不想被B组件上发生的修改所影响,那么就应该让B组件依赖于A组件。修改后的架构如下:
所以现在的情况是,日志记录组件依赖于业务服务组件,在业务服务组件内部,使用接口的方式抽象出日志存储和分析的操作,日志记录组件负责实现这些接口即可。这样,以后就算还要使用其它方式的日志记录组件,只要实现业务服务组件定义的日志记录接口,而业务服务组件不需做修改。
以上就算我们在软件架构层次上对OCP这一设计原则的应用。开发者可以根据相关函数被修改的原因、修改的方式以及修改的时间来对其进行分组隔离,并将这些互相隔离的函数分组整理成组件结构,使得高阶组件(核心组件)不会因为低阶组件(辅助组件)被修改而受到影响。
总结
OCP是我们进行系统架构设计的主导原则,其主要目标是让系统易于扩展,同时限制其每次被修改所影响的范围。实现方式是通过将系统划分为一系列组件,并将这些组件间的依赖关系按层次结构进行组织,使得高阶组件不会因低阶组件被修改而受到影响。