在软件系统中,应对需求变化是一项永恒的挑战。可扩展性正是解决这一挑战的核心能力——当新需求出现时,系统不需要或仅需少量修改就可以支持,无须整个系统重构或重建。本文深入分析可扩展性复杂度的来源,帮助读者理清预测变化与应对变化之间的微妙关系。
可扩展性(Scalability)指系统为了应对将来需求变化而提供的一种扩展能力。当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。
由于软件系统固有的多变性,新的需求总会不断被提出,因此可扩展性显得尤其重要。在软件开发领域,面向对象思想的提出就是为了解决可扩展性带来的问题;后来的设计模式,更是将可扩展性做到了极致。
设计具备良好可扩展性的系统,有两个基本条件:
但要达成这两个条件,本身也是一件复杂的事情。
为什么预测变化是一件复杂的事情?因为预测本身就意味着不确定性。
如果架构师按照"唯一不变的是变化"这个标准去衡量,每个设计方案都要考虑可扩展性。例如,架构师准备设计一个简单的后台管理系统,需要考虑:用 MySQL 存储数据时,是否要考虑后续用 Oracle?用 HTTP 做接口协议时,是否要支持 ProtocolBuffer?甚至是否要考虑 VR 技术对架构的影响?
这种做法会导致两个极端问题:
问题一:过度考虑,不堪重负
如果每个设计点都考虑可扩展性,架构师会不堪重负,架构设计也会异常庞大且最终无法落地。
问题二:完全不考虑,上线即重构
如果架构师完全不做预测,可能系统刚上线,马上就来新需求需要重构,前期投入的工作量全部白费。
问题三:预测本身可能出错
"预测"这个词本身就暗示了不可能每次都准确。如果预测的事情出错,期望中的需求迟迟不来,甚至被明确否定,那么基于预测做的架构设计就没什么作用。
综合分析,预测变化的复杂性在于:不能每个设计点都考虑可扩展性,不能完全不考虑可扩展性,且所有的预测都存在出错的可能性。对于架构师来说,如何把握预测的程度和提升预测结果的准确性,是一件很复杂的事情,没有通用的标准可以简单套用。
假设架构师经验丰富,看问题非常准,能够准确预测所有的变化,是否就意味着可扩展性很容易实现?也没那么理想!因为预测变化是一回事,采取什么方案来应对变化,又是另外一个复杂的事情。
第一种应对变化的常见方案是将"变化"封装在一个"变化层",将不变的部分封装在一个独立的"稳定层"。
无论采取哪种形式(变化层依赖稳定层,或稳定层依赖变化层),通过剥离变化层和稳定层的方式都会带来复杂性:
系统需要拆分出变化层和稳定层:对于哪些属于变化层,哪些属于稳定层,很多时候并不是像示例中那样明确。不同的人有不同的理解,可能导致架构设计评审时吵翻天。
需要设计变化层和稳定层之间的接口:对于稳定层来说,接口肯定是越稳定越好;但对于变化层来说,在有差异的多个实现方式中找出共同点,并且保证当加入新的功能时原有的接口设计不需要太大修改,这是一件很复杂的事情。
第二种常见的应对变化的方案是提炼出一个"抽象层"和一个"实现层"。抽象层是稳定的,实现层可以根据具体业务需要定制开发,当加入新的功能时,只需要增加新的实现,无须修改抽象层。这种方案典型的实践就是设计模式和规则引擎。
以装饰者模式为例,类关系图如下:
Component(抽象组件) ├── TextView(具体组件) └── Decorator(抽象装饰者) ├── BorderDecorator(具体装饰者) ├── ScrollBarDecorator(具体装饰者) └── BackgroundDecorator(具体装饰者)
装饰者模式的规则包括:
这个规则一旦抽象出来后就固定了,不能轻易修改。装饰者模式相比传统的继承来实现功能,确实灵活很多——能够灵活地给 TextView 增加额外功能(边框、滚动条、背景图片等),这些功能上的组合不影响规则,只需要按照规则实现即可。
但装饰者模式相对普通的类实现模式,明显要复杂多了。本来一个函数或者一个类就能搞定的事情,现在要拆分成多个类,而且多个类之间必须按照装饰者模式来设计和调用。
规则引擎和设计模式类似,都是通过灵活的设计来达到可扩展的目的,但"灵活的设计"本身就是一件复杂的事情。
下面通过一个简单的插件/过滤器系统演示"抽象层+实现层"模式:
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)。这正是开闭原则(对扩展开放,对修改关闭)的体现。