编辑
2026-05-09
记录知识
0

目录

前言
什么是可扩展性
预测变化的复杂性
应对变化方案一:变化层与稳定层分离
应对变化方案二:抽象层+实现层
Python 示例:抽象层+实现层模式
总结
参考

前言

在软件系统中,应对需求变化是一项永恒的挑战。可扩展性正是解决这一挑战的核心能力——当新需求出现时,系统不需要或仅需少量修改就可以支持,无须整个系统重构或重建。本文深入分析可扩展性复杂度的来源,帮助读者理清预测变化与应对变化之间的微妙关系。

什么是可扩展性

可扩展性(Scalability)指系统为了应对将来需求变化而提供的一种扩展能力。当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。

由于软件系统固有的多变性,新的需求总会不断被提出,因此可扩展性显得尤其重要。在软件开发领域,面向对象思想的提出就是为了解决可扩展性带来的问题;后来的设计模式,更是将可扩展性做到了极致。

设计具备良好可扩展性的系统,有两个基本条件:

  • 正确预测变化
  • 完美封装变化

但要达成这两个条件,本身也是一件复杂的事情。

预测变化的复杂性

为什么预测变化是一件复杂的事情?因为预测本身就意味着不确定性。

如果架构师按照"唯一不变的是变化"这个标准去衡量,每个设计方案都要考虑可扩展性。例如,架构师准备设计一个简单的后台管理系统,需要考虑:用 MySQL 存储数据时,是否要考虑后续用 Oracle?用 HTTP 做接口协议时,是否要支持 ProtocolBuffer?甚至是否要考虑 VR 技术对架构的影响?

这种做法会导致两个极端问题:

问题一:过度考虑,不堪重负

如果每个设计点都考虑可扩展性,架构师会不堪重负,架构设计也会异常庞大且最终无法落地。

问题二:完全不考虑,上线即重构

如果架构师完全不做预测,可能系统刚上线,马上就来新需求需要重构,前期投入的工作量全部白费。

问题三:预测本身可能出错

"预测"这个词本身就暗示了不可能每次都准确。如果预测的事情出错,期望中的需求迟迟不来,甚至被明确否定,那么基于预测做的架构设计就没什么作用。

综合分析,预测变化的复杂性在于:不能每个设计点都考虑可扩展性,不能完全不考虑可扩展性,且所有的预测都存在出错的可能性。对于架构师来说,如何把握预测的程度和提升预测结果的准确性,是一件很复杂的事情,没有通用的标准可以简单套用。

应对变化方案一:变化层与稳定层分离

假设架构师经验丰富,看问题非常准,能够准确预测所有的变化,是否就意味着可扩展性很容易实现?也没那么理想!因为预测变化是一回事,采取什么方案来应对变化,又是另外一个复杂的事情。

第一种应对变化的常见方案是将"变化"封装在一个"变化层",将不变的部分封装在一个独立的"稳定层"。

无论采取哪种形式(变化层依赖稳定层,或稳定层依赖变化层),通过剥离变化层和稳定层的方式都会带来复杂性:

  1. 系统需要拆分出变化层和稳定层:对于哪些属于变化层,哪些属于稳定层,很多时候并不是像示例中那样明确。不同的人有不同的理解,可能导致架构设计评审时吵翻天。

  2. 需要设计变化层和稳定层之间的接口:对于稳定层来说,接口肯定是越稳定越好;但对于变化层来说,在有差异的多个实现方式中找出共同点,并且保证当加入新的功能时原有的接口设计不需要太大修改,这是一件很复杂的事情。

应对变化方案二:抽象层+实现层

第二种常见的应对变化的方案是提炼出一个"抽象层"和一个"实现层"。抽象层是稳定的,实现层可以根据具体业务需要定制开发,当加入新的功能时,只需要增加新的实现,无须修改抽象层。这种方案典型的实践就是设计模式和规则引擎。

以装饰者模式为例,类关系图如下:

Component(抽象组件) ├── TextView(具体组件) └── Decorator(抽象装饰者) ├── BorderDecorator(具体装饰者) ├── ScrollBarDecorator(具体装饰者) └── BackgroundDecorator(具体装饰者)

装饰者模式的规则包括:

  1. Component 和 Decorator 类
  2. Decorator 类继承 Component 类
  3. Decorator 类聚合了 Component 类

这个规则一旦抽象出来后就固定了,不能轻易修改。装饰者模式相比传统的继承来实现功能,确实灵活很多——能够灵活地给 TextView 增加额外功能(边框、滚动条、背景图片等),这些功能上的组合不影响规则,只需要按照规则实现即可。

但装饰者模式相对普通的类实现模式,明显要复杂多了。本来一个函数或者一个类就能搞定的事情,现在要拆分成多个类,而且多个类之间必须按照装饰者模式来设计和调用。

规则引擎和设计模式类似,都是通过灵活的设计来达到可扩展的目的,但"灵活的设计"本身就是一件复杂的事情。

Python 示例:抽象层+实现层模式

下面通过一个简单的插件/过滤器系统演示"抽象层+实现层"模式:

python
""" 抽象层 + 实现层模式示例:插件过滤器系统 用于演示如何通过抽象层和实现层分离,实现可扩展性 """ from abc import ABC, abstractmethod from typing import List # ============ 抽象层(稳定层) ============ class PluginFilter(ABC): """抽象过滤器接口 - 稳定层""" @abstractmethod def process(self, data: str) -> str: """处理数据""" pass @abstractmethod def get_name(self) -> str: """获取过滤器名称""" pass class FilterPipeline: """过滤器管道 - 组合多个过滤器""" def __init__(self): self._filters: List[PluginFilter] = [] def add_filter(self, filter_impl: PluginFilter): """添加过滤器实现(无需修改抽象层)""" self._filters.append(filter_impl) def process(self, data: str) -> str: """按顺序执行所有过滤器""" result = data for filter_impl in self._filters: result = filter_impl.process(result) return result # ============ 实现层(变化层) ============ class UpperCaseFilter(PluginFilter): """具体实现:大写转换过滤器""" def process(self, data: str) -> str: return data.upper() def get_name(self) -> str: return "UpperCaseFilter" class TrimFilter(PluginFilter): """具体实现:去首尾空格过滤器""" def process(self, data: str) -> str: return data.strip() def get_name(self) -> str: return "TrimFilter" class ReplaceFilter(PluginFilter): """具体实现:替换过滤器""" def __init__(self, old: str, new: str): self._old = old self._new = new def process(self, data: str) -> str: return data.replace(self._old, self._new) def get_name(self) -> str: return f"ReplaceFilter('{self._old}'->'{self._new}')" # ============ 新增功能(扩展实现层,无需修改抽象层) ============ class MarkdownFilter(PluginFilter): """新增的 Markdown 转 HTML 过滤器 - 演示可扩展性""" def process(self, data: str) -> str: # 简单 Markdown 转换 import re data = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', data) data = re.sub(r'\*(.+?)\*', r'<em>\1</em>', data) return data def get_name(self) -> str: return "MarkdownFilter" # ============ 使用示例 ============ if __name__ == "__main__": # 创建管道 pipeline = FilterPipeline() # 添加过滤器实现 pipeline.add_filter(TrimFilter()) pipeline.add_filter(UpperCaseFilter()) pipeline.add_filter(ReplaceFilter(" ", "_")) # 处理数据 data = " hello world " result = pipeline.process(data) print(f"原始数据: '{data}'") print(f"处理结果: '{result}'") # 动态添加新的过滤器(无需修改抽象层) pipeline.add_filter(MarkdownFilter()) data2 = "**bold** and *italic*" result2 = pipeline.process(data2) print(f"\n原始数据: '{data2}'") print(f"处理结果: '{result2}'") # 预期输出: # 原始数据: ' hello world ' # 处理结果: 'HELLO_WORLD' # # 原始数据: '**bold** and *italic*' # 处理结果: '<strong>bold</strong> and <em>italic</em>'

运行结果:

原始数据: ' hello world ' 处理结果: 'HELLO_WORLD' 原始数据: '**bold** and *italic*' 处理结果: '<strong>BOLD</strong> AND <EM>ITALIC</EM>'

这个示例展示了"抽象层+实现层"模式的核心优势:当需要新增功能时(如 MarkdownFilter),只需要增加新的实现类,无需修改抽象层(PluginFilter 接口)和管道类(FilterPipeline)。这正是开闭原则(对扩展开放,对修改关闭)的体现。

总结

  • 可扩展性是系统应对未来需求变化的能力,新需求出现时系统不需要或仅需少量修改就可以支持
  • 实现良好可扩展性需要满足两个基本条件:正确预测变化、完美封装变化
  • 预测变化的复杂性体现在三个方面:不能每个点都考虑(不堪重负)、不能完全不考虑(上线即重构)、预测本身可能出错
  • 方案一"变化层与稳定层分离":通过剥离变化层和稳定层来应对变化,但需要解决接口设计和分层边界的问题
  • 方案二"抽象层+实现层":抽象层稳定,实现层可定制扩展,典型实践是设计模式和规则引擎
  • 装饰者模式通过 Component+Decorator 规则实现灵活组合,但相比普通类实现复杂度更高
  • 可扩展性的"灵活设计"本身就是复杂的事情,需要在灵活性和复杂度之间取得平衡
  • 过度追求可扩展性会导致架构臃肿,过度忽视可扩展性会导致频繁重构
  • 架构师需要根据业务实际情况判断可扩展性设计的代价,选择合适的设计方案
  • 好的可扩展性设计应该遵循开闭原则:对扩展开放,对修改关闭

参考

  • 软件架构基础