一、为什么RPA需要设计模式?
在回答这个问题前,我们先看一个典型的复杂RPA场景:企业财务自动化需要从多个系统获取数据(ERP、CRM、银行),经过清洗、验证、转换,然后生成报表并上传至OA系统,期间涉及多次人工审批、异常处理、重试和日志记录。
如果采用传统脚本方式:
- 所有逻辑堆砌在单个函数中,上千行代码难以阅读理解;
- 增加一个新数据源,需要修改核心流程,极易引入bug;
- 状态变更散落在各处,无法保证一致性;
- 无法复用相同的数据处理逻辑。
而设计模式正是为了解决这类问题而生。它们将变化的部分封装,将稳定的部分抽象,让代码结构清晰,变更局部化。下面,我们将逐一深入三种模式。
二、流程控制模式:让流程灵活可控
2.1 模式定义
流程控制模式并不是单一的设计模式,而是一组用于组织业务流程执行逻辑的模式组合,常见的有:
- 模板方法模式(Template Method):定义流程的骨架,将可变步骤延迟到子类实现。
- 策略模式(Strategy):封装不同的业务规则,使它们可以互相替换。
- 责任链模式(Chain of Responsibility):为请求创建处理链,每个处理者决定是否处理或传递给下一个。
在RPA中,一个完整业务流程往往由多个步骤组成,步骤可能因客户、环境、数据类型而异。流程控制模式可以帮助我们将步骤的"顺序结构"与"具体实现"分离。
2.2 模板方法模式实现流程骨架
假设我们有一个通用的数据导入流程:读取数据 → 清洗 → 验证 → 转换 → 写入目标系统。不同场景下,清洗、验证、转换的具体规则可能不同,但流程顺序固定。
python
from abc import ABC, abstractmethod
class DataImportPipeline(ABC):
"""模板类:定义流程骨架"""
def run(self, source):
"""模板方法,定义了算法骨架"""
data = self.extract(source)
cleaned = self.clean(data)
validated = self.validate(cleaned)
transformed = self.transform(validated)
self.load(transformed)
def extract(self, source):
"""默认实现,子类可覆盖"""
print(f"从 {source} 提取原始数据")
return {"raw": source}
@abstractmethod
def clean(self, data):
"""子类必须实现清洗逻辑"""
pass
@abstractmethod
def validate(self, data):
"""子类必须实现验证逻辑"""
pass
@abstractmethod
def transform(self, data):
"""子类必须实现转换逻辑"""
pass
def load(self, data):
"""默认实现,子类可覆盖"""
print(f"加载数据到目标系统: {data}")
class ErpInvoicePipeline(DataImportPipeline):
"""ERP发票导入流程"""
def clean(self, data):
print("清洗ERP发票数据:去空格、格式统一")
# 具体清洗代码
return data
def validate(self, data):
print("验证发票必需字段")
# 验证逻辑
return data
def transform(self, data):
print("转换为财务系统格式")
return {"finance": data}
优点:
- 流程结构稳定,复用性强;
- 新增一种数据源只需实现新的子类,不影响已有流程;
- 符合开闭原则。
2.3 策略模式实现可插拔的步骤
有时流程的某一步有多种算法可选(如不同的数据加密方式、不同的文件解析器),使用策略模式可以避免大量的if-else。
python
from typing import Protocol
class ValidationStrategy(Protocol):
def validate(self, data):
...
class StrictValidation:
def validate(self, data):
print("严格校验:所有字段必填")
return all(data.values())
class LooseValidation:
def validate(self, data):
print("宽松校验:只检查关键字段")
return data.get("id") is not None
class DataProcessor:
def __init__(self, validation_strategy: ValidationStrategy):
self._strategy = validation_strategy
def process(self, data):
if not self._strategy.validate(data):
raise ValueError("数据校验失败")
# 继续处理
print("数据处理成功")
# 使用
processor = DataProcessor(StrictValidation())
processor.process({"id": 1, "name": "test"}) # 通过
2.4 责任链模式实现多级审批流
在RPA中常遇到需要多级审批的业务,如采购订单:部门主管 → 财务 → 总经理。责任链模式让每个审批者独立,易于添加或调整顺序。
python
from abc import ABC, abstractmethod
class Approver(ABC):
def __init__(self):
self._next = None
def set_next(self, approver):
self._next = approver
return approver
@abstractmethod
def process(self, request):
pass
class ManagerApprover(Approver):
def process(self, request):
if request.amount <= 10000:
print(f"部门经理审批通过:金额{request.amount}")
return True
elif self._next:
return self._next.process(request)
return False
class FinanceApprover(Approver):
def process(self, request):
if request.amount <= 50000:
print(f"财务审批通过:金额{request.amount}")
return True
elif self._next:
return self._next.process(request)
return False
class CeoApprover(Approver):
def process(self, request):
print(f"CEO最终审批:金额{request.amount}")
return True
# 构建责任链
manager = ManagerApprover()
finance = FinanceApprover()
ceo = CeoApprover()
manager.set_next(finance).set_next(ceo)
# 发起请求
class Request:
def __init__(self, amount):
self.amount = amount
manager.process(Request(8000)) # 经理审批
manager.process(Request(30000)) # 财务审批
manager.process(Request(80000)) # CEO审批
流程控制模式的RPA应用场景:
- 多系统数据同步流程(模板方法)
- 根据不同客户配置不同的业务规则(策略)
- 采购订单审批、工单流转(责任链)
三、数据管道模式:打造可复用的数据处理链
3.1 模式概述
数据管道模式(Pipeline Pattern)源自Unix管道哲学,它将数据处理过程分解为一系列独立的处理单元(过滤器),每个单元接收输入、处理后输出,并传递给下一个单元。这种模式非常适合RPA中常见的数据抽取-转换-加载(ETL)场景。
3.2 核心要素
- 数据源:提供原始数据(文件、数据库、API)
- 过滤器:对数据进行操作(清洗、转换、验证、增强)
- 管道:连接过滤器的通道,控制数据流动
- 数据汇:最终存储或输出
3.3 Python实现一个轻量级管道
我们可以用装饰器或链式调用来实现管道。
python
from typing import Callable, Any
class Pipeline:
"""通用管道类,支持多个处理器"""
def __init__(self):
self._handlers = []
def add(self, handler: Callable[[Any], Any]):
self._handlers.append(handler)
return self # 支持链式调用
def execute(self, initial_data):
data = initial_data
for handler in self._handlers:
data = handler(data)
return data
# 定义各个处理函数
def extract_from_csv(file_path):
print(f"从{file_path}读取CSV")
return [{"id": 1, "name": "A"}, {"id": 2, "name": "B"}]
def clean_data(records):
print("清洗数据:去除空格")
for r in records:
r["name"] = r["name"].strip()
return records
def enrich_with_timestamp(records):
print("添加时间戳")
from datetime import datetime
ts = datetime.now().isoformat()
for r in records:
r["processed_at"] = ts
return records
def load_to_db(records):
print(f"加载{len(records)}条记录到数据库")
return True
# 构建管道
pipeline = Pipeline()
pipeline.add(extract_from_csv).add(clean_data).add(enrich_with_timestamp).add(load_to_db)
# 执行
pipeline.execute("data.csv")
输出:
从data.csv读取CSV
清洗数据:去除空格
添加时间戳
加载2条记录到数据库
3.4 管道模式的进阶:支持异步、条件分支
复杂场景下,可能需要根据数据内容走不同分支,或并行处理。我们可以对管道进行扩展,引入条件过滤、异常处理等。
python
class ConditionalHandler:
def __init__(self, condition, true_handler, false_handler=None):
self.condition = condition
self.true_handler = true_handler
self.false_handler = false_handler
def __call__(self, data):
if self.condition(data):
return self.true_handler(data)
elif self.false_handler:
return self.false_handler(data)
return data
应用场景:
- 从多个数据源抽取并统一格式
- 数据质量校验与修复
- 实时数据流处理(如监控日志并触发RPA动作)
3.5 数据管道模式的优势
- 解耦:每个处理器独立开发、测试、维护
- 复用:相同处理器可在不同管道中使用
- 可测性:每个单元可单独测试
- 可观测性:可在管道中插入日志、监控节点
四、状态机模式:让业务流程状态清晰可控
4.1 为什么需要状态机?
在RPA中,很多业务流程具有明确的状态,例如:
- 订单处理:待支付 → 已支付 → 已发货 → 已完成
- 工单审批:待提交 → 主管审批 → 经理审批 → 归档
- 数据同步:待同步 → 同步中 → 同步成功 / 同步失败 → 重试
如果使用简单的变量和if-else管理状态,当状态增多、转换条件复杂时,代码极易出错。有限状态机(FSM) 将状态、事件、转换规则显式定义,确保系统始终处于合法状态。
4.2 状态机的核心元素
- 状态(State):系统可能处于的稳定阶段
- 事件(Event):触发状态转换的输入
- 转换(Transition):定义在某个状态下发生某个事件后,转移到哪个新状态,并执行相应动作
- 动作(Action):状态转换时执行的业务逻辑
4.3 使用Python实现状态机
我们可以使用transitions库快速构建,也可以手动实现轻量级版本。
示例:使用transitions库(推荐)
python
pip install transitions
python
from transitions import Machine
class InvoiceOrder:
states = ['pending', 'paid', 'shipped', 'completed', 'cancelled']
def __init__(self, name):
self.name = name
self.machine = Machine(model=self, states=InvoiceOrder.states, initial='pending')
# 定义转换
self.machine.add_transition('pay', 'pending', 'paid', after='after_pay')
self.machine.add_transition('ship', 'paid', 'shipped', after='after_ship')
self.machine.add_transition('complete', 'shipped', 'completed', after='after_complete')
self.machine.add_transition('cancel', ['pending', 'paid'], 'cancelled', after='after_cancel')
def after_pay(self):
print(f"订单{self.name}已支付,记录财务信息")
def after_ship(self):
print(f"订单{self.name}已发货,更新物流")
def after_complete(self):
print(f"订单{self.name}已完成")
def after_cancel(self):
print(f"订单{self.name}已取消,释放库存")
# 使用
order = InvoiceOrder("ORD001")
order.pay() # 支付
order.ship() # 发货
order.complete() # 完成
order.cancel() # 此时状态已是completed,不允许取消,会触发异常
手动实现轻量级状态机(用于理解原理)
python
class StateMachine:
def __init__(self):
self.state = 'initial'
self.transitions = {
('initial', 'start'): 'processing',
('processing', 'success'): 'completed',
('processing', 'fail'): 'error',
('error', 'retry'): 'processing',
}
def trigger(self, event):
key = (self.state, event)
if key in self.transitions:
old_state = self.state
self.state = self.transitions[key]
self.on_transition(old_state, event)
else:
raise Exception(f"非法转换:{self.state} -> {event}")
def on_transition(self, old, event):
print(f"{old} 通过 {event} 转为 {self.state}")
4.4 状态机在RPA中的高级应用
场景:一个需要与用户交互的RPA任务,例如自动填报系统,可能被用户暂停、恢复、手动干预。状态机可以清晰定义这些交互状态。
python
class RpaTask:
states = ['idle', 'running', 'paused', 'error', 'completed']
def __init__(self, task_id):
self.task_id = task_id
self.machine = Machine(model=self, states=RpaTask.states, initial='idle')
self.machine.add_transition('start', 'idle', 'running')
self.machine.add_transition('pause', 'running', 'paused')
self.machine.add_transition('resume', 'paused', 'running')
self.machine.add_transition('fail', 'running', 'error')
self.machine.add_transition('retry', 'error', 'running')
self.machine.add_transition('complete', 'running', 'completed')
def on_enter_running(self):
# 启动实际业务流程
pass
优点:
- 状态流转显式化,易于理解和测试
- 避免非法状态转换
- 可以轻松添加持久化(将状态保存到数据库),实现断点续传
五、三种模式的协同应用
在实际RPA项目中,这三种模式往往同时使用,互相配合。下面通过一个综合案例展示它们的协同:
场景:一个企业RPA需要定期从多个ERP系统抽取销售数据,经过清洗、验证、转换,然后通过审批流程,最后同步到数据仓库。同时,整个过程需要记录状态,支持断点续传和重试。
- 流程控制模式:使用模板方法定义整体流程骨架(抽取→清洗→转换→审批→加载)。其中审批环节使用责任链模式实现多级审批。
- 数据管道模式:在数据处理阶段,构建数据管道,将清洗、验证、转换作为独立的过滤器,方便复用和测试。
- 状态机模式:为每个批次任务维护状态(待抽取、抽取中、清洗中、审批中、同步中、完成、失败),使用状态机管理,确保任务状态转换正确,并支持重试。
通过这种组合,代码结构清晰,每个模块职责单一,系统整体健壮性大幅提升。