概述
结构性设计模式(Structural Patterns)是软件工程中专注于类与对象组合的核心方法论,其核心价值在于通过灵活的组合关系构建复杂系统结构,降低模块间的耦合度,提升代码的可复用性与可维护性。在Rust语言环境下,这一模式体系被赋予了独特的技术内涵——通过所有权系统、trait组合与零成本抽象特性,结构性模式不仅保留了传统设计模式的解耦能力,更实现了编译期类型安全与运行时性能的完美平衡。
Rust结构性模式的核心优势:相较于传统面向对象语言中依赖继承导致的脆弱基类问题(如Java的继承层次僵化)和运行时类型转换风险(如C++的dynamic_cast),Rust通过trait组合实现行为复用,结合枚举与模式匹配处理变体结构,利用组合优于继承原则构建灵活的类型关系,从根本上重构了系统结构的表达边界。
本文聚焦七种经典结构性模式在Rust中的实现与演化:适配器模式通过trait实现接口转换;桥接模式利用泛型与trait对象分离抽象与实现;组合模式借助递归枚举构建树形结构;装饰器模式通过包装类型动态增强行为;外观模式提供简化接口隐藏复杂子系统;享元模式结合Rc/Arc实现状态共享;代理模式利用智能指针控制对象访问。这些模式共同构成了Rust生态下系统结构设计的完整解决方案,为复杂系统开发提供了兼顾灵活性与安全性的工程实践范式。
核心概念
什么是结构性模式?
- 适配器模式:将一个类的接口转换成客户期望的另一个接口,使原本不兼容的类可以协同工作
- 桥接模式:将抽象部分与实现部分分离,使它们可以独立变化
- 组合模式:将对象组合成树形结构以表示”部分-整体”的层次结构
- 装饰器模式:动态地给对象添加额外的职责,比生成子类更灵活
- 外观模式:为子系统中的一组接口提供一个一致的界面,简化客户端使用
- 享元模式:通过共享技术有效支持大量细粒度对象的复用
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问
为什么需要结构性模式?
Rust的组合优先原则默认提供了强大的类型组合能力,但在某些复杂场景中,直接组合会导致代码结构混乱:
- 接口兼容需求:需要集成第三方库或遗留代码时进行接口适配
- 抽象与实现解耦:需要在不修改抽象层的情况下扩展多种实现
- 层次结构表达:需要递归表达树形或图形的复合对象关系
- 行为动态增强:需要在运行时灵活组合功能而不产生类型爆炸
- 子系统简化:需要为复杂模块提供简洁统一的访问入口
- 资源复用优化:需要高效管理大量相似对象以减少内存开销
- 访问控制需求:需要在对象访问前后插入日志、缓存、权限等横切逻辑
适配器模式详解
定义与作用
适配器模式(Adapter Pattern)是一种结构性设计模式,其核心定义是”将一个类的接口转换成客户期望的另一个接口,使原本接口不兼容的类可以协同工作”。在Rust语境下,这一模式通过trait抽象与类型包装实现:定义目标trait规范客户端期望的接口,创建适配器结构体包装适配者类型,通过实现目标trait完成接口转换。
// 目标trait:客户端期望的接口
pub trait Target {
fn request(&self) -> String;
}
// 适配者:已有的不兼容接口
pub struct Adaptee {
pub data: String,
}
impl Adaptee {
pub fn specific_request(&self) -> String {
format!("Adaptee: {}", self.data)
}
}
// 适配器:实现目标接口并委托给适配者
pub struct Adapter {
adaptee: Adaptee,
}
impl Adapter {
pub fn new(adaptee: Adaptee) -> Self {
Adapter { adaptee }
}
}
impl Target for Adapter {
fn request(&self) -> String {
// 接口转换逻辑
self.adaptee.specific_request()
}
}
Rust适配器的两种实现策略
1. 结构体包装(Composition)
通过持有适配者实例实现接口转换,适合需要访问适配者内部状态的场景:
pub struct FileLoggerAdapter {
legacy_logger: LegacyFileLogger,
}
impl Logger for FileLoggerAdapter {
fn log(&self, level: LogLevel, msg: &str) {
// 转换参数并委托
self.legacy_logger.write(
level.to_legacy_string(),
msg.as_bytes()
);
}
}
2. Trait对象适配(Dynamic Dispatch)
通过Box<dyn Trait>实现运行时多态适配,适合需要动态选择适配策略的场景:
pub trait Database {
fn query(&self, sql: &str) -> Result<Vec<Row>, Error>;
}
pub struct LegacyDbAdapter {
inner: Box<dyn LegacyDatabase>,
}
impl Database for LegacyDbAdapter {
fn query(&self, sql: &str) -> Result<Vec<Row>, Error> {
// 转换查询结果
self.inner.execute(sql)
.map(|rows| rows.into_iter().map(Row::from_legacy).collect())
}
}
代码示例
日志系统接口适配
// 1. 目标接口:现代日志系统期望的trait
pub trait ModernLogger {
fn log(&self, level: LogLevel, message: &str, context: &Context);
}
#[derive(Clone, Copy)]
pub enum LogLevel {
Debug, Info, Warn, Error,
}
pub struct Context {
pub module: String,
pub line: u32,
}
// 2. 适配者:遗留日志库的接口
pub struct LegacyLogger {
prefix: String,
}
impl LegacyLogger {
pub fn new(prefix: String) -> Self {
LegacyLogger { prefix }
}
// 遗留接口:参数顺序和类型都不同
pub fn write(&self, msg: &str, severity: i32, module: &str) {
let level_str = match severity {
0 => "DEBUG", 1 => "INFO", 2 => "WARN", 3 => "ERROR",
_ => "UNKNOWN",
};
println!("[{}][{}][{}] {}", self.prefix, level_str, module, msg);
}
}
// 3. 适配器实现
pub struct LegacyLoggerAdapter {
legacy: LegacyLogger,
}
impl LegacyLoggerAdapter {
pub fn new(legacy: LegacyLogger) -> Self {
LegacyLoggerAdapter { legacy }
}
}
impl ModernLogger for LegacyLoggerAdapter {
fn log(&self, level: LogLevel, message: &str, context: &Context) {
// 接口转换:枚举→整数,结构体→字符串
let severity = match level {
LogLevel::Debug => 0,
LogLevel::Info => 1,
LogLevel::Warn => 2,
LogLevel::Error => 3,
};
self.legacy.write(message, severity, &context.module);
}
}
// 4. 使用示例
fn main() {
let legacy = LegacyLogger::new("App".to_string());
let adapter = LegacyLoggerAdapter::new(legacy);
let ctx = Context {
module: "main".to_string(),
line: 42,
};
adapter.log(LogLevel::Info, "Application started", &ctx);
// 输出: [App][INFO][main] Application started
}
应用场景
- 第三方库集成:适配不同版本的API或不同供应商的实现
- 遗留系统迁移:在不修改旧代码的前提下接入新架构
- 测试桩实现:为接口创建模拟实现用于单元测试
- 多后端支持:通过适配器统一不同存储/网络后端的接口
Rust优势:通过trait系统实现零成本抽象,适配器逻辑在编译期确定,无运行时开销;所有权系统确保适配过程中的内存安全。
桥接模式详解
定义与作用
桥接模式(Bridge Pattern)是一种结构性设计模式,其核心定义是”将抽象部分与实现部分分离,使它们可以独立变化”。在传统面向对象实现中,该模式通过继承层次分离抽象与实现,但容易导致类爆炸问题。在Rust语境下,桥接模式通过trait组合、泛型参数与trait对象实现更灵活的解耦。
// 实现部分:定义具体行为接口
pub trait Renderer {
fn render_circle(&self, x: f64, y: f64, radius: f64);
fn render_square(&self, x: f64, y: f64, size: f64);
}
// 抽象部分:持有实现引用并提供高层接口
pub struct Shape<R: Renderer> {
renderer: R,
}
impl<R: Renderer> Shape<R> {
pub fn new(renderer: R) -> Self {
Shape { renderer }
}
pub fn draw_circle(&self, x: f64, y: f64, r: f64) {
self.renderer.render_circle(x, y, r);
}
}
Rust桥接的核心优势
- 编译期多态:通过泛型参数
<R: Renderer>实现零开销抽象 - 运行时多态:通过
Box<dyn Renderer>支持动态插件加载 - 类型安全:关联类型确保抽象与实现的正确配对
- 组合灵活:可在运行时切换实现而不修改抽象层
泛型桥接 vs Trait对象桥接
泛型桥接(静态分发)
pub trait Device {
fn is_enabled(&self) -> bool;
fn enable(&mut self);
fn disable(&mut self);
}
pub struct Remote<D: Device> {
device: D,
}
impl<D: Device> Remote<D> {
pub fn toggle_power(&mut self) {
if self.device.is_enabled() {
self.device.disable();
} else {
self.device.enable();
}
}
}
优势:
- 编译期确定类型,无虚表查找开销
- 可内联优化,性能最佳
- 类型错误在编译期捕获
劣势:
- 每种组合生成独立代码,可能增加二进制体积
- 无法在运行时动态切换实现
Trait对象桥接(动态分发)
pub trait Device {
fn is_enabled(&self) -> bool;
fn enable(&mut self);
fn disable(&mut self);
}
pub struct Remote {
device: Box<dyn Device>,
}
impl Remote {
pub fn new(device: Box<dyn Device>) -> Self {
Remote { device }
}
pub fn toggle_power(&mut self) {
if self.device.is_enabled() {
self.device.disable();
} else {
self.device.enable();
}
}
// 运行时切换设备
pub fn set_device(&mut self, device: Box<dyn Device>) {
self.device = device;
}
}
优势:
- 支持运行时动态替换实现
- 插件系统友好,可扩展性强
- 代码体积更紧凑
劣势:
- 动态分发有约1-2纳秒开销
- 需要堆分配,增加内存管理复杂度
代码示例
跨平台图形渲染桥接
// ========== 实现部分:渲染器trait ==========
pub trait Renderer {
fn render_circle(&self, x: f64, y: f64, radius: f64);
fn render_square(&self, x: f64, y: f64, size: f64);
fn api_name(&self) -> &str;
}
// OpenGL实现
pub struct OpenGLRenderer;
impl Renderer for OpenGLRenderer {
fn render_circle(&self, x: f64, y: f64, radius: f64) {
println!("[OpenGL] Circle at ({}, {}) r={}", x, y, radius);
}
fn render_square(&self, x: f64, y: f64, size: f64) {
println!("[OpenGL] Square at ({}, {}) size={}", x, y, size);
}
fn api_name(&self) -> &str { "OpenGL 4.6" }
}
// Vulkan实现
pub struct VulkanRenderer;
impl Renderer for VulkanRenderer {
fn render_circle(&self, x: f64, y: f64, radius: f64) {
println!("[Vulkan] Circle at ({}, {}) r={}", x, y, radius);
}
fn render_square(&self, x: f64, y: f64, size: f64) {
println!("[Vulkan] Square at ({}, {}) size={}", x, y, size);
}
fn api_name(&self) -> &str { "Vulkan 1.3" }
}
// ========== 抽象部分:形状trait ==========
pub trait Shape {
fn draw(&self);
fn resize(&mut self, factor: f64);
fn info(&self) -> String;
}
// ========== 桥接实现:具体形状 ==========
pub struct Circle<R: Renderer> {
renderer: R,
x: f64,
y: f64,
radius: f64,
}
impl<R: Renderer> Circle<R> {
pub fn new(renderer: R, x: f64, y: f64, radius: f64) -> Self {
Circle { renderer, x, y, radius }
}
}
impl<R: Renderer> Shape for Circle<R> {
fn draw(&self) {
self.renderer.render_circle(self.x, self.y, self.radius);
}
fn resize(&mut self, factor: f64) {
self.radius *= factor;
}
fn info(&self) -> String {
format!("Circle@{}: ({}, {}) r={}",
self.renderer.api_name(), self.x, self.y, self.radius)
}
}
// ========== 使用示例 ==========
fn main() {
// 静态桥接:编译期确定OpenGL
let mut gl_circle = Circle::new(OpenGLRenderer, 10.0, 20.0, 5.0);
gl_circle.draw(); // [OpenGL] Circle at (10, 20) r=5
println!("{}", gl_circle.info());
// 动态桥接:运行时选择Vulkan
let vk_renderer: Box<dyn Renderer> = Box::new(VulkanRenderer);
let mut vk_circle = Circle {
renderer: vk_renderer,
x: 30.0,
y: 40.0,
radius: 8.0,
};
vk_circle.draw(); // [Vulkan] Circle at (30, 40) r=8
}
应用场景
- 跨平台UI框架:抽象层定义Widget接口,实现层适配Windows/macOS/Linux原生控件
- 多数据库支持:抽象层定义Repository接口,实现层适配MySQL/PostgreSQL/SQLite
- 插件系统:核心逻辑通过trait定义扩展点,插件实现具体功能动态加载
Rust优势:通过泛型与trait对象的灵活组合,既支持编译期优化的静态桥接,也支持运行时扩展的动态桥接,满足不同场景的性能与灵活性需求。
组合模式详解
定义与作用
组合模式(Composite Pattern)是一种结构性设计模式,其核心定义是”将对象组合成树形结构以表示’部分-整体’的层次结构,使得客户端可以统一处理单个对象和组合对象”。在Rust语境下,这一模式通过递归枚举、智能指针与trait对象实现类型安全的树形结构。
// 组件trait:统一叶子与组合节点的接口
pub trait Component {
fn operation(&self) -> String;
fn add(&mut self, _child: Box<dyn Component>) {
// 叶子节点默认不支持添加
}
fn remove(&mut self, _index: usize) {
// 叶子节点默认不支持删除
}
}
// 叶子节点
pub struct Leaf {
pub name: String,
}
impl Component for Leaf {
fn operation(&self) -> String {
format!("Leaf: {}", self.name)
}
}
// 组合节点:持有子组件集合
pub struct Composite {
pub name: String,
pub children: Vec<Box<dyn Component>>,
}
impl Composite {
pub fn new(name: String) -> Self {
Composite { name, children: Vec::new() }
}
}
impl Component for Composite {
fn operation(&self) -> String {
let child_ops: Vec<String> = self.children
.iter()
.map(|c| c.operation())
.collect();
format!("Composite: {} [{}]", self.name, child_ops.join(", "))
}
fn add(&mut self, child: Box<dyn Component>) {
self.children.push(child);
}
}
Rust组合模式的核心优势
- 类型安全递归:通过
Box<dyn Trait>打破递归类型的大小限制 - 统一接口处理:客户端无需区分叶子与组合节点,简化遍历逻辑
- 内存安全:所有权系统确保树形结构的正确生命周期管理
- 零成本抽象:trait对象的虚表调用开销可控,适合中等规模树结构
递归枚举 vs Trait对象
方案1:递归枚举(Enum-based)
适合组件类型固定、编译期已知的场景:
pub enum FileSystemNode {
File { name: String, size: u64 },
Directory {
name: String,
children: Vec<FileSystemNode>
},
}
impl FileSystemNode {
pub fn total_size(&self) -> u64 {
match self {
FileSystemNode::File { size, .. } => *size,
FileSystemNode::Directory { children, .. } => {
children.iter().map(|c| c.total_size()).sum()
}
}
}
}
优势:
- 编译期类型检查,无动态分发开销
- 模式匹配使逻辑清晰
- 内存布局紧凑
劣势:
- 新增组件类型需修改枚举定义
- 不支持运行时动态扩展
方案2:Trait对象(Dynamic Dispatch)
适合组件类型开放、需要运行时扩展的场景:
pub trait Node {
fn name(&self) -> &str;
fn size(&self) -> u64;
fn as_file(&self) -> Option<&FileNode> { None }
fn as_dir(&self) -> Option<&DirNode> { None }
}
pub struct FileNode {
name: String,
size: u64,
}
pub struct DirNode {
name: String,
children: Vec<Box<dyn Node>>,
}
优势:
- 支持运行时动态添加新组件类型
- 插件系统友好
- 接口统一,客户端代码简洁
劣势:
- 动态分发有轻微性能开销
- 需要堆分配管理生命周期
代码示例
文档编辑器组合结构
// ========== 组件trait ==========
pub trait DocumentElement {
fn render(&self, indent: usize) -> String;
fn word_count(&self) -> usize;
}
// ========== 叶子节点:文本段落 ==========
pub struct Paragraph {
pub content: String,
}
impl DocumentElement for Paragraph {
fn render(&self, indent: usize) -> String {
let prefix = " ".repeat(indent);
format!("{}<p>{}</p>\n", prefix, self.content)
}
fn word_count(&self) -> usize {
self.content.split_whitespace().count()
}
}
// ========== 叶子节点:图片 ==========
pub struct Image {
pub url: String,
pub alt: String,
}
impl DocumentElement for Image {
fn render(&self, indent: usize) -> String {
let prefix = " ".repeat(indent);
format!("{}<img src=\"{}\" alt=\"{}\" />\n",
prefix, self.url, self.alt)
}
fn word_count(&self) -> usize {
self.alt.split_whitespace().count()
}
}
// ========== 组合节点:章节 ==========
pub struct Section {
pub title: String,
pub level: u8,
pub children: Vec<Box<dyn DocumentElement>>,
}
impl Section {
pub fn new(title: String, level: u8) -> Self {
Section { title, level, children: Vec::new() }
}
pub fn add(&mut self, element: Box<dyn DocumentElement>) {
self.children.push(element);
}
}
impl DocumentElement for Section {
fn render(&self, indent: usize) -> String {
let prefix = " ".repeat(indent);
let heading = "#".repeat(self.level as usize);
let mut result = format!("{}{} {}\n", prefix, heading, self.title);
for child in &self.children {
result.push_str(&child.render(indent + 1));
}
result
}
fn word_count(&self) -> usize {
self.title.split_whitespace().count() +
self.children.iter().map(|c| c.word_count()).sum::<usize>()
}
}
// ========== 使用示例 ==========
fn main() {
let mut doc = Section::new("Rust设计模式".to_string(), 1);
// 添加段落
doc.add(Box::new(Paragraph {
content: "结构性模式关注类与对象的组合关系".to_string(),
}));
// 添加嵌套章节
let mut adapter_section = Section::new("适配器模式".to_string(), 2);
adapter_section.add(Box::new(Paragraph {
content: "适配器将一个接口转换为另一个接口".to_string(),
}));
adapter_section.add(Box::new(Image {
url: "adapter-diagram.png".to_string(),
alt: "适配器模式结构图".to_string(),
}));
doc.add(Box::new(adapter_section));
// 渲染文档
println!("{}", doc.render(0));
println!("总字数: {}", doc.word_count());
}
应用场景
- UI组件树:React/Vue等框架的虚拟DOM本质是组合模式的应用
- 文件系统抽象:统一处理文件与目录的遍历、统计操作
- 表达式求值:算术表达式树中叶子是数字,节点是运算符
- 组织架构:员工与部门的层级管理
Rust优势:通过Box<dyn Trait>实现类型安全的递归结构,所有权系统确保树形结构的正确销毁顺序,避免内存泄漏。
装饰器模式详解
定义与作用
装饰器模式(Decorator Pattern)是一种结构性设计模式,其核心定义是”动态地给对象添加额外的职责,比生成子类更灵活地扩展功能”。在Rust语境下,这一模式通过类型包装、trait委托与方法链实现行为的动态组合。
// 基础组件trait
pub trait DataSource {
fn write_data(&mut self, data: &str) -> std::io::Result<()>;
fn read_data(&mut self) -> std::io::Result<String>;
}
// 具体组件
pub struct FileDataSource {
filepath: String,
}
impl FileDataSource {
pub fn new(filepath: String) -> Self {
FileDataSource { filepath }
}
}
impl DataSource for FileDataSource {
fn write_data(&mut self, data: &str) -> std::io::Result<()> {
std::fs::write(&self.filepath, data)
}
fn read_data(&mut self) -> std::io::Result<String> {
std::fs::read_to_string(&self.filepath)
}
}
// 装饰器:持有组件引用并增强行为
pub struct CompressionDecorator<D: DataSource> {
wrapped: D,
}
impl<D: DataSource> CompressionDecorator<D> {
pub fn new(wrapped: D) -> Self {
CompressionDecorator { wrapped }
}
}
impl<D: DataSource> DataSource for CompressionDecorator<D> {
fn write_data(&mut self, data: &str) -> std::io::Result<()> {
// 增强:压缩后写入
let compressed = compress(data);
self.wrapped.write_data(&compressed)
}
fn read_data(&mut self) -> std::io::Result<String> {
// 增强:读取后解压
let compressed = self.wrapped.read_data()?;
decompress(&compressed)
}
}
fn compress(data: &str) -> String {
// 简化:实际应使用zstd等库
format!("[COMPRESSED]{}", data)
}
fn decompress(data: &str) -> std::io::Result<String> {
if data.starts_with("[COMPRESSED]") {
Ok(data[12..].to_string())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid compressed data"
))
}
}
Rust装饰器的核心优势
- 零成本抽象:泛型装饰器在编译期展开,无运行时开销
- 组合灵活:通过方法链动态组合多个装饰器
- 类型安全:编译期确保装饰器链的类型正确性
- 无侵入扩展:无需修改原有组件代码即可添加新功能
装饰器链的构建方式
方式1:嵌套构造(静态组合)
let source = EncryptionDecorator::new(
CompressionDecorator::new(
FileDataSource::new("data.txt".to_string())
)
);
优势:类型明确,编译期优化充分 劣势:装饰顺序固定,灵活性较低
方式2:Trait对象链(动态组合)
let mut source: Box<dyn DataSource> =
Box::new(FileDataSource::new("data.txt".to_string()));
if enable_compression {
source = Box::new(CompressionDecorator { wrapped: source });
}
if enable_encryption {
source = Box::new(EncryptionDecorator { wrapped: source });
}
优势:运行时动态决定装饰策略 劣势:动态分发有轻微开销,类型信息部分丢失
方式3:Builder模式构建
pub struct DataSourceBuilder {
base: FileDataSource,
compress: bool,
encrypt: bool,
log: bool,
}
impl DataSourceBuilder {
pub fn new(filepath: String) -> Self {
DataSourceBuilder {
base: FileDataSource::new(filepath),
compress: false,
encrypt: false,
log: false,
}
}
pub fn with_compression(mut self) -> Self {
self.compress = true;
self
}
pub fn with_encryption(mut self) -> Self {
self.encrypt = true;
self
}
pub fn build(self) -> Box<dyn DataSource> {
let mut source: Box<dyn DataSource> = Box::new(self.base);
if self.log {
source = Box::new(LoggingDecorator::new(source));
}
if self.compress {
source = Box::new(CompressionDecorator::new(source));
}
if self.encrypt {
source = Box::new(EncryptionDecorator::new(source));
}
source
}
}
代码示例
HTTP请求处理装饰器链
// ========== 基础trait ==========
pub trait HttpRequestHandler {
fn handle(&self, request: &HttpRequest) -> HttpResponse;
}
pub struct HttpRequest {
pub url: String,
pub headers: HashMap<String, String>,
pub body: Option<String>,
}
pub struct HttpResponse {
pub status: u16,
pub headers: HashMap<String, String>,
pub body: Option<String>,
}
// ========== 基础处理器 ==========
pub struct EchoHandler;
impl HttpRequestHandler for EchoHandler {
fn handle(&self, request: &HttpRequest) -> HttpResponse {
HttpResponse {
status: 200,
headers: HashMap::new(),
body: request.body.clone(),
}
}
}
// ========== 装饰器:日志记录 ==========
pub struct LoggingDecorator<H: HttpRequestHandler> {
inner: H,
}
impl<H: HttpRequestHandler> LoggingDecorator<H> {
pub fn new(inner: H) -> Self {
LoggingDecorator { inner }
}
}
impl<H: HttpRequestHandler> HttpRequestHandler for LoggingDecorator<H> {
fn handle(&self, request: &HttpRequest) -> HttpResponse {
println!("[LOG] Request: {} {:?}", request.url, request.headers);
let response = self.inner.handle(request);
println!("[LOG] Response: {}", response.status);
response
}
}
// ========== 装饰器:认证校验 ==========
pub struct AuthDecorator<H: HttpRequestHandler> {
inner: H,
required_token: String,
}
impl<H: HttpRequestHandler> AuthDecorator<H> {
pub fn new(inner: H, token: String) -> Self {
AuthDecorator { inner, required_token: token }
}
}
impl<H: HttpRequestHandler> HttpRequestHandler for AuthDecorator<H> {
fn handle(&self, request: &HttpRequest) -> HttpResponse {
match request.headers.get("Authorization") {
Some(token) if token == &self.required_token => {
self.inner.handle(request)
}
_ => HttpResponse {
status: 401,
headers: HashMap::new(),
body: Some("Unauthorized".to_string()),
}
}
}
}
// ========== 装饰器:响应缓存 ==========
use std::collections::HashMap;
use std::sync::Mutex;
pub struct CachingDecorator<H: HttpRequestHandler> {
inner: H,
cache: Mutex<HashMap<String, HttpResponse>>,
}
impl<H: HttpRequestHandler> CachingDecorator<H> {
pub fn new(inner: H) -> Self {
CachingDecorator {
inner,
cache: Mutex::new(HashMap::new())
}
}
}
impl<H: HttpRequestHandler> HttpRequestHandler for CachingDecorator<H> {
fn handle(&self, request: &HttpRequest) -> HttpResponse {
let key = format!("{}:{:?}", request.url, request.body);
// 尝试读取缓存
if let Some(cached) = self.cache.lock().unwrap().get(&key) {
println!("[CACHE] Hit for {}", request.url);
return cached.clone();
}
// 执行并缓存
let response = self.inner.handle(request);
self.cache.lock().unwrap().insert(key, response.clone());
response
}
}
// ========== 使用示例 ==========
fn main() {
// 构建装饰器链:日志 → 认证 → 缓存 → 基础处理
let handler = LoggingDecorator::new(
AuthDecorator::new(
CachingDecorator::new(EchoHandler),
"secret-token".to_string(),
)
);
let request = HttpRequest {
url: "/api/echo".to_string(),
headers: [("Authorization".to_string(), "secret-token".to_string())]
.into_iter().collect(),
body: Some("Hello, Rust!".to_string()),
};
let response = handler.handle(&request);
println!("Status: {}, Body: {:?}", response.status, response.body);
}
应用场景
- 中间件系统:Web框架的请求/响应处理链
- 数据流处理:压缩、加密、编码等管道式转换
- 性能监控:在不修改业务逻辑的前提下添加埋点
- 权限控制:动态组合多种校验策略
Rust优势:通过泛型装饰器实现零成本抽象,编译期展开装饰逻辑;通过trait对象支持运行时动态组合,满足不同场景需求。
外观模式详解
定义与作用
外观模式(Facade Pattern)是一种结构性设计模式,其核心定义是”为子系统中的一组接口提供一个一致的界面,简化客户端使用”。在Rust语境下,这一模式通过模块封装、trait抽象与委托实现隐藏复杂子系统的内部细节。
// 复杂子系统:视频转换库(简化示例)
mod video_codec {
pub struct Decoder {
pub format: String,
}
impl Decoder {
pub fn new(format: &str) -> Self {
Decoder { format: format.to_string() }
}
pub fn decode(&self, data: &[u8]) -> Vec<u8> {
// 实际解码逻辑...
data.to_vec()
}
}
pub struct Encoder {
pub target_format: String,
}
impl Encoder {
pub fn new(format: &str) -> Self {
Encoder { target_format: format.to_string() }
}
pub fn encode(&self, data: &[u8]) -> Vec<u8> {
// 实际编码逻辑...
data.to_vec()
}
}
pub struct BitrateController {
pub max_bitrate: u32,
}
impl BitrateController {
pub fn adjust(&mut self, data: &mut [u8]) {
// 码率控制逻辑...
}
}
}
// 外观:提供简化接口
pub struct VideoConverter;
impl VideoConverter {
pub fn convert(input: &[u8], from: &str, to: &str) -> Vec<u8> {
// 隐藏子系统复杂交互
let decoder = video_codec::Decoder::new(from);
let mut raw = decoder.decode(input);
let mut controller = video_codec::BitrateController { max_bitrate: 5000 };
controller.adjust(&mut raw);
let encoder = video_codec::Encoder::new(to);
encoder.encode(&raw)
}
}
Rust外观模式的核心优势
- 模块边界清晰:通过
pub/pub(crate)控制子系统可见性 - 编译期优化:外观方法可内联,消除委托开销
- 错误处理统一:在外观层集中处理子系统的错误类型转换
- 测试友好:客户端只需测试外观接口,无需了解内部细节
外观与委托的实现策略
策略1:简单委托(直接调用)
适合子系统稳定、接口变化少的场景:
pub struct DatabaseFacade {
connection: DbConnection,
query_builder: QueryBuilder,
cache: RedisCache,
}
impl DatabaseFacade {
pub fn query(&self, sql: &str) -> Result<Vec<Row>, AppError> {
// 统一错误转换
self.connection.execute(sql)
.map_err(|e| AppError::from(e))
}
}
策略2:Trait抽象(可替换实现)
适合需要测试或动态替换子系统的场景:
pub trait StorageBackend {
fn read(&self, key: &str) -> Result<Vec<u8>, StorageError>;
fn write(&mut self, key: &str, value: &[u8]) -> Result<(), StorageError>;
}
pub struct StorageFacade<B: StorageBackend> {
backend: B,
serializer: JsonSerializer,
}
impl<B: StorageBackend> StorageFacade<B> {
pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<T, AppError> {
let data = self.backend.read(key)?;
self.serializer.deserialize(&data)
.map_err(|e| AppError::from(e))
}
}
代码示例
智能家居控制外观
// ========== 子系统:灯光控制 ==========
mod lighting {
pub struct SmartBulb {
pub id: u32,
pub brightness: u8,
pub color_temp: u16,
}
impl SmartBulb {
pub fn new(id: u32) -> Self {
SmartBulb { id, brightness: 100, color_temp: 4000 }
}
pub fn set_brightness(&mut self, level: u8) {
self.brightness = level.min(100);
}
pub fn set_color_temp(&mut self, temp: u16) {
self.color_temp = temp.clamp(2700, 6500);
}
}
}
// ========== 子系统:温控系统 ==========
mod climate {
pub struct Thermostat {
pub target_temp: f32,
pub mode: Mode,
}
pub enum Mode { Heat, Cool, Auto, Off }
impl Thermostat {
pub fn new() -> Self {
Thermostat { target_temp: 22.0, mode: Mode::Auto }
}
pub fn set_mode(&mut self, mode: Mode) {
self.mode = mode;
}
pub fn set_target(&mut self, temp: f32) {
self.target_temp = temp.clamp(16.0, 30.0);
}
}
}
// ========== 子系统:安防系统 ==========
mod security {
pub struct AlarmSystem {
pub armed: bool,
pub sensors: Vec<String>,
}
impl AlarmSystem {
pub fn new() -> Self {
AlarmSystem {
armed: false,
sensors: vec!["door".into(), "window".into()]
}
}
pub fn arm(&mut self) {
self.armed = true;
}
pub fn disarm(&mut self) {
self.armed = false;
}
}
}
// ========== 外观:统一智能家居接口 ==========
pub struct SmartHome {
living_room_light: lighting::SmartBulb,
thermostat: climate::Thermostat,
alarm: security::AlarmSystem,
}
impl SmartHome {
pub fn new() -> Self {
SmartHome {
living_room_light: lighting::SmartBulb::new(1),
thermostat: climate::Thermostat::new(),
alarm: security::AlarmSystem::new(),
}
}
// 场景模式:回家
pub fn arrive_home(&mut self) {
self.living_room_light.set_brightness(80);
self.living_room_light.set_color_temp(3000);
self.thermostat.set_mode(climate::Mode::Heat);
self.thermostat.set_target(23.0);
self.alarm.disarm();
}
// 场景模式:离家
pub fn leave_home(&mut self) {
self.living_room_light.set_brightness(0);
self.thermostat.set_mode(climate::Mode::Off);
self.alarm.arm();
}
// 场景模式:睡眠
pub fn sleep_mode(&mut self) {
self.living_room_light.set_brightness(10);
self.living_room_light.set_color_temp(2700);
self.thermostat.set_target(20.0);
}
// 统一状态查询
pub fn status(&self) -> String {
format!(
"Light: {}% {}K | Climate: {:?} {}°C | Alarm: {}",
self.living_room_light.brightness,
self.living_room_light.color_temp,
self.thermostat.mode,
self.thermostat.target_temp,
if self.alarm.armed { "ARMED" } else { "DISARMED" }
)
}
}
// ========== 使用示例 ==========
fn main() {
let mut home = SmartHome::new();
println!("初始状态: {}", home.status());
// 用户只需调用简单接口,无需了解子系统细节
home.arrive_home();
println!("回家模式: {}", home.status());
home.sleep_mode();
println!("睡眠模式: {}", home.status());
home.leave_home();
println!("离家模式: {}", home.status());
}
应用场景
- 库的公共接口:为复杂库提供简洁易用的API入口
- 微服务网关:聚合多个后端服务,提供统一前端接口
- 硬件抽象层:隐藏不同厂商设备的底层驱动差异
- 配置管理:统一处理多源配置的加载、合并与校验
Rust优势:通过模块系统精确控制子系统可见性,外观层集中处理错误转换与资源管理,客户端代码简洁且类型安全。
享元模式详解
定义与作用
享元模式(Flyweight Pattern)是一种结构性设计模式,其核心定义是”通过共享技术有效支持大量细粒度对象的复用,减少内存消耗”。在Rust语境下,这一模式通过引用计数智能指针、不可变数据共享与内部/外部状态分离实现高效的对象复用。
use std::sync::Arc;
// 内部状态:可共享的不变数据
#[derive(Clone, Debug)]
pub struct CharacterStyle {
pub font_family: Arc<str>,
pub font_size: u16,
pub color: Arc<str>,
}
impl CharacterStyle {
pub fn new(font: &str, size: u16, color: &str) -> Self {
CharacterStyle {
font_family: font.into(),
font_size: size,
color: color.into(),
}
}
}
// 外部状态:每个对象独有的可变数据
pub struct Character {
pub symbol: char,
pub position: (usize, usize),
pub style: Arc<CharacterStyle>, // 共享样式
}
// 享元工厂:管理共享对象池
pub struct StyleFactory {
styles: std::collections::HashMap<String, Arc<CharacterStyle>>,
}
impl StyleFactory {
pub fn new() -> Self {
StyleFactory { styles: std::collections::HashMap::new() }
}
pub fn get_style(&mut self, font: &str, size: u16, color: &str) -> Arc<CharacterStyle> {
let key = format!("{}:{}:{}", font, size, color);
self.styles
.entry(key)
.or_insert_with(|| Arc::new(CharacterStyle::new(font, size, color)))
.clone()
}
pub fn memory_usage(&self) -> usize {
self.styles.len() * std::mem::size_of::<CharacterStyle>()
}
}
Rust享元模式的核心优势
- 零拷贝共享:
Arc::clone()仅增加引用计数,无数据复制开销 - 线程安全:
Arc天然支持多线程共享,无需额外同步 - 内存高效:大量对象共享相同内部状态,显著减少内存占用
- 写时分离:结合
Rc::make_mut或Arc::make_mut实现COW优化
共享策略选择
策略1:Arc共享(多线程场景)
use std::sync::Arc;
pub struct SharedConfig {
pub settings: Arc<ConfigData>,
}
impl Clone for SharedConfig {
fn clone(&self) -> Self {
SharedConfig {
settings: Arc::clone(&self.settings), // O(1)操作
}
}
}
适用场景:配置数据、只读缓存、跨线程共享资源
策略2:Rc共享(单线程场景)
use std::rc::Rc;
pub struct DocumentNode {
pub content: Rc<String>,
pub children: Vec<Rc<DocumentNode>>,
}
适用场景:GUI组件树、表达式求值、单线程数据结构
策略3:Interning(字符串驻留)
use std::collections::HashMap;
use std::sync::Mutex;
pub struct StringPool {
pool: Mutex<HashMap<String, Arc<str>>>,
}
impl StringPool {
pub fn intern(&self, s: &str) -> Arc<str> {
let mut pool = self.pool.lock().unwrap();
pool.entry(s.to_string())
.or_insert_with(|| s.into())
.clone()
}
}
适用场景:标识符、键名、枚举值等高频重复字符串
代码示例
文本编辑器字符享元
use std::collections::HashMap;
use std::sync::Arc;
// ========== 内部状态:可共享的样式 ==========
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct TextStyle {
pub font: Arc<str>,
pub size: u16,
pub bold: bool,
pub italic: bool,
pub color: u32, // RGBA
}
impl TextStyle {
pub fn new(font: &str, size: u16, color: u32) -> Self {
TextStyle {
font: font.into(),
size,
bold: false,
italic: false,
color,
}
}
pub fn with_bold(mut self, bold: bool) -> Self {
self.bold = bold;
self
}
pub fn with_italic(mut self, italic: bool) -> Self {
self.italic = italic;
self
}
}
// ========== 外部状态:字符位置 ==========
#[derive(Clone, Debug)]
pub struct TextCharacter {
pub symbol: char,
pub row: usize,
pub col: usize,
pub style: Arc<TextStyle>,
}
// ========== 享元工厂 ==========
pub struct StylePool {
pool: HashMap<TextStyle, Arc<TextStyle>>,
}
impl StylePool {
pub fn new() -> Self {
StylePool { pool: HashMap::new() }
}
pub fn get(&mut self, style: TextStyle) -> Arc<TextStyle> {
self.pool
.entry(style)
.or_insert_with(|| Arc::new(style.clone()))
.clone()
}
pub fn unique_styles(&self) -> usize {
self.pool.len()
}
}
// ========== 文档模型 ==========
pub struct Document {
characters: Vec<TextCharacter>,
style_pool: StylePool,
}
impl Document {
pub fn new() -> Self {
Document {
characters: Vec::new(),
style_pool: StylePool::new(),
}
}
pub fn add_char(&mut self, symbol: char, row: usize, col: usize, style: TextStyle) {
let shared_style = self.style_pool.get(style);
self.characters.push(TextCharacter {
symbol,
row,
col,
style: shared_style,
});
}
pub fn render(&self) {
// 按行分组渲染
let mut lines: HashMap<usize, Vec<&TextCharacter>> = HashMap::new();
for ch in &self.characters {
lines.entry(ch.row).or_default().push(ch);
}
for (row, chars) in lines.iter_mut() {
chars.sort_by_key(|c| c.col);
let line: String = chars.iter().map(|c| c.symbol).collect();
println!("Line {}: {}", row, line);
}
}
pub fn memory_stats(&self) -> String {
format!(
"Characters: {}, Unique Styles: {}, Style Memory: {}B",
self.characters.len(),
self.style_pool.unique_styles(),
self.style_pool.unique_styles() * std::mem::size_of::<TextStyle>()
)
}
}
// ========== 使用示例 ==========
fn main() {
let mut doc = Document::new();
// 创建1000个字符,仅使用3种样式
let style_normal = TextStyle::new("Arial", 14, 0x000000FF);
let style_bold = TextStyle::new("Arial", 14, 0x000000FF).with_bold(true);
let style_red = TextStyle::new("Arial", 14, 0xFF0000FF);
for row in 0..10 {
for col in 0..100 {
let style = match (row + col) % 3 {
0 => style_normal.clone(),
1 => style_bold.clone(),
_ => style_red.clone(),
};
doc.add_char('A', row, col, style);
}
}
println!("{}", doc.memory_stats());
// 输出: Characters: 1000, Unique Styles: 3, Style Memory: 96B
// 若不使用享元: 1000 * 48B = 48KB vs 3 * 48B = 144B
doc.render();
}
应用场景
- 文本/富文本编辑器:大量字符共享相同字体、颜色等样式
- 游戏对象池:相同类型的敌人/道具共享纹理、碰撞体等资源
- GUI组件树:相同样式的按钮、标签共享渲染属性
- 网络请求缓存:相同URL的请求共享配置与连接池
性能对比: | 场景 | 对象数 | 无享元内存 | 享元内存 | 节省比例 | |——|——–|———–|———-|———-| | 文本文档 | 10,000字符 | ~480KB | ~1.5KB | 99.7% | | 游戏粒子 | 100,000粒子 | ~8MB | ~24KB | 99.7% | | UI组件 | 1,000按钮 | ~48KB | ~144B | 99.7% |
Rust优势:Arc的原子引用计数开销极低(约1-2纳秒),结合编译期类型检查确保共享数据的不可变性,避免数据竞争。
代理模式详解
定义与作用
代理模式(Proxy Pattern)是一种结构性设计模式,其核心定义是”为其他对象提供一种代理以控制对这个对象的访问”。在Rust语境下,这一模式通过智能指针包装、trait委托与懒加载机制实现访问控制、延迟初始化与横切逻辑注入。
use std::sync::Arc;
// 真实主题
pub trait Image {
fn display(&self);
fn size(&self) -> (u32, u32);
}
pub struct RealImage {
filename: String,
data: Vec<u8>, // 实际图像数据(大对象)
}
impl RealImage {
pub fn load(filename: &str) -> Self {
println!("Loading image: {}", filename);
// 实际加载逻辑...
RealImage {
filename: filename.to_string(),
data: vec![0; 1024 * 1024], // 模拟1MB数据
}
}
}
impl Image for RealImage {
fn display(&self) {
println!("Displaying {}", self.filename);
}
fn size(&self) -> (u32, u32) {
(1920, 1080)
}
}
// 代理:控制访问 + 延迟加载
pub struct ImageProxy {
filename: String,
real_image: Option<Arc<RealImage>>,
}
impl ImageProxy {
pub fn new(filename: &str) -> Self {
ImageProxy {
filename: filename.to_string(),
real_image: None,
}
}
fn get_real(&mut self) -> &Arc<RealImage> {
if self.real_image.is_none() {
self.real_image = Some(Arc::new(RealImage::load(&self.filename)));
}
self.real_image.as_ref().unwrap()
}
}
impl Image for ImageProxy {
fn display(&self) {
// 访问控制:检查权限
if !self.has_permission() {
println!("Access denied");
return;
}
// 延迟加载 + 委托
self.real_image.as_ref().unwrap().display();
}
fn size(&self) -> (u32, u32) {
// 元数据可提前获取,无需加载大图
(1920, 1080)
}
}
impl ImageProxy {
fn has_permission(&self) -> bool {
// 实际应检查用户权限
true
}
}
Rust代理模式的核心优势
- 零成本抽象:泛型代理在编译期展开,委托调用可内联优化
- 懒加载友好:结合
OnceCell实现线程安全的延迟初始化 - 访问控制灵活:可在代理层统一实现缓存、日志、权限等横切逻辑
- 内存安全:智能指针确保代理与真实对象的生命周期正确管理
代理类型与实现策略
1. 虚拟代理(Virtual Proxy):延迟加载
use once_cell::sync::OnceCell;
pub struct LazyLoader<T> {
loader: Box<dyn FnOnce() -> T + Send>,
instance: OnceCell<T>,
}
impl<T> LazyLoader<T> {
pub fn new<F>(loader: F) -> Self
where F: FnOnce() -> T + Send + 'static
{
LazyLoader {
loader: Box::new(loader),
instance: OnceCell::new(),
}
}
pub fn get(&self) -> &T
where T: Send + Sync
{
self.instance.get_or_init(|| (self.loader)())
}
}
适用场景:大文件加载、远程资源获取、昂贵计算结果缓存
2. 保护代理(Protection Proxy):访问控制
pub struct ProtectedResource<R> {
inner: R,
allowed_roles: Vec<String>,
}
impl<R> ProtectedResource<R> {
pub fn new(inner: R, roles: Vec<&str>) -> Self {
ProtectedResource {
inner,
allowed_roles: roles.into_iter().map(String::from).collect(),
}
}
pub fn access<F, T>(&self, user_roles: &[String], f: F) -> Result<T, AccessError>
where F: FnOnce(&R) -> T
{
if user_roles.iter().any(|r| self.allowed_roles.contains(r)) {
Ok(f(&self.inner))
} else {
Err(AccessError::Forbidden)
}
}
}
适用场景:权限校验、数据脱敏、审计日志
3. 缓存代理(Cache Proxy):结果复用
use std::collections::HashMap;
use std::sync::Mutex;
pub struct CachedFunction<F, I, O>
where
F: Fn(&I) -> O + Send + Sync,
I: std::hash::Hash + Eq + Clone,
O: Clone,
{
func: F,
cache: Mutex<HashMap<I, O>>,
}
impl<F, I, O> CachedFunction<F, I, O>
where
F: Fn(&I) -> O + Send + Sync,
I: std::hash::Hash + Eq + Clone,
O: Clone,
{
pub fn new(func: F) -> Self {
CachedFunction {
func,
cache: Mutex::new(HashMap::new()),
}
}
pub fn call(&self, input: I) -> O {
// 先查缓存
if let Some(result) = self.cache.lock().unwrap().get(&input) {
return result.clone();
}
// 计算并缓存
let result = (self.func)(&input);
self.cache.lock().unwrap().insert(input.clone(), result.clone());
result
}
}
适用场景:纯函数结果缓存、API响应缓存、计算密集型任务复用
代码示例
数据库查询代理(缓存+日志+限流)
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use once_cell::sync::OnceCell;
// ========== 真实数据库连接 ==========
pub trait Database {
fn query(&self, sql: &str) -> Result<Vec<Row>, DbError>;
}
pub struct RealDatabase {
connection_string: String,
}
impl RealDatabase {
pub fn connect(conn_str: &str) -> Self {
println!("Connecting to {}", conn_str);
// 实际建立连接...
RealDatabase {
connection_string: conn_str.to_string(),
}
}
}
impl Database for RealDatabase {
fn query(&self, sql: &str) -> Result<Vec<Row>, DbError> {
println!("Executing: {}", sql);
// 实际查询逻辑...
Ok(vec![]) // 简化
}
}
// ========== 代理:组合多种横切逻辑 ==========
pub struct DatabaseProxy {
conn_string: String,
real_db: OnceCell<RealDatabase>,
// 缓存层
query_cache: Mutex<HashMap<String, (Vec<Row>, Instant)>>,
cache_ttl: Duration,
// 限流层
request_count: Mutex<u32>,
max_requests: u32,
window_start: Mutex<Instant>,
window_duration: Duration,
}
impl DatabaseProxy {
pub fn new(conn_str: &str, cache_ttl: Duration) -> Self {
DatabaseProxy {
conn_string: conn_str.to_string(),
real_db: OnceCell::new(),
query_cache: Mutex::new(HashMap::new()),
cache_ttl,
request_count: Mutex::new(0),
max_requests: 100,
window_start: Mutex::new(Instant::now()),
window_duration: Duration::from_secs(60),
}
}
fn get_db(&self) -> &RealDatabase {
self.real_db.get_or_init(|| RealDatabase::connect(&self.conn_string))
}
fn check_rate_limit(&self) -> bool {
let mut count = self.request_count.lock().unwrap();
let mut window = self.window_start.lock().unwrap();
let now = Instant::now();
if now.duration_since(*window) > self.window_duration {
*count = 0;
*window = now;
}
if *count >= self.max_requests {
return false;
}
*count += 1;
true
}
fn get_cached(&self, sql: &str) -> Option<Vec<Row>> {
let cache = self.query_cache.lock().unwrap();
if let Some((rows, timestamp)) = cache.get(sql) {
if timestamp.elapsed() < self.cache_ttl {
println!("[CACHE] Hit for: {}", sql);
return Some(rows.clone());
}
}
None
}
fn set_cached(&self, sql: &str, rows: Vec<Row>) {
let mut cache = self.query_cache.lock().unwrap();
cache.insert(sql.to_string(), (rows, Instant::now()));
}
}
impl Database for DatabaseProxy {
fn query(&self, sql: &str) -> Result<Vec<Row>, DbError> {
// 1. 日志
let start = Instant::now();
println!("[PROXY] Query: {}", sql);
// 2. 限流检查
if !self.check_rate_limit() {
return Err(DbError::RateLimitExceeded);
}
// 3. 缓存检查(仅SELECT查询)
if sql.trim_start().to_uppercase().starts_with("SELECT") {
if let Some(cached) = self.get_cached(sql) {
return Ok(cached);
}
}
// 4. 委托真实数据库
let result = self.get_db().query(sql);
// 5. 缓存结果
if let Ok(rows) = &result {
if sql.trim_start().to_uppercase().starts_with("SELECT") {
self.set_cached(sql, rows.clone());
}
}
// 6. 日志
println!("[PROXY] Completed in {:?}", start.elapsed());
result
}
}
// ========== 辅助类型 ==========
#[derive(Clone, Debug)]
pub struct Row {
pub columns: HashMap<String, String>,
}
#[derive(Debug)]
pub enum DbError {
ConnectionFailed,
QueryError(String),
RateLimitExceeded,
}
impl std::fmt::Display for DbError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
DbError::ConnectionFailed => write!(f, "Connection failed"),
DbError::QueryError(e) => write!(f, "Query error: {}", e),
DbError::RateLimitExceeded => write!(f, "Rate limit exceeded"),
}
}
}
impl std::error::Error for DbError {}
// ========== 使用示例 ==========
fn main() {
let proxy = DatabaseProxy::new(
"postgres://user:pass@localhost/db",
Duration::from_secs(300), // 5分钟缓存
);
// 首次查询:加载连接 + 执行 + 缓存
let _ = proxy.query("SELECT * FROM users WHERE id = 1");
// 相同查询:直接返回缓存
let _ = proxy.query("SELECT * FROM users WHERE id = 1");
// 限流测试:100次/分钟
for i in 0..105 {
match proxy.query(&format!("SELECT * FROM logs LIMIT {}", i)) {
Ok(_) => println!("Request {} OK", i),
Err(e) => println!("Request {} failed: {}", i, e),
}
}
}
应用场景
- 远程代理:gRPC/HTTP客户端封装网络细节
- 虚拟代理:大资源(图片/视频)的懒加载
- 保护代理:权限校验、数据脱敏、审计日志
- 缓存代理:数据库查询、API响应、计算结果复用
- 日志代理:统一记录方法调用参数与耗时
性能对比: | 代理类型 | 额外开销 | 典型收益 | |———-|———-|———-| | 虚拟代理 | 首次加载延迟 | 节省90%+初始内存 | | 缓存代理 | Hash计算+锁 | 缓存命中率>80%时性能提升5-10倍 | | 保护代理 | 权限检查(~100ns) | 避免未授权访问的安全风险 | | 日志代理 | 字符串格式化(~1μs) | 问题排查效率提升 |
Rust优势:通过OnceCell实现线程安全的懒加载,Mutex保护共享状态,编译期确保代理与真实对象的接口一致性。
实践应用
综合案例:分布式配置中心客户端
本案例设计为”分布式配置中心客户端”,通过整合适配器、桥接、装饰器、外观、享元、代理六种结构性模式,构建高效可靠的配置管理系统。系统架构中:
- 外观模式:
ConfigClient提供简洁的get()/watch()接口,隐藏底层复杂交互 - 适配器模式:适配etcd/Consul/ZooKeeper等不同后端存储
- 桥接模式:分离配置解析逻辑(JSON/YAML/TOML)与存储后端
- 装饰器模式:动态添加缓存、加密、日志等横切功能
- 享元模式:共享相同路径的配置项元数据,减少内存占用
- 代理模式:实现本地缓存代理,减少远程请求频次
协作流程:
- 客户端调用
ConfigClient::get("app.timeout") - 外观层路由请求到对应后端适配器
- 桥接层选择解析器处理配置格式
- 代理层先查本地缓存,未命中则请求远程
- 装饰器层记录访问日志并加密敏感配置
- 享元池复用相同路径的元数据对象
各模式协同解决了多后端兼容、配置格式扩展、性能优化、安全管控等复杂需求。
核心实现(简化版)
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use once_cell::sync::OnceCell;
// ========== 外观:统一配置客户端 ==========
pub struct ConfigClient {
backend: Arc<dyn ConfigBackend>,
parser: Arc<dyn ConfigParser>,
cache: LocalCache,
}
impl ConfigClient {
pub fn new(backend: Arc<dyn ConfigBackend>, parser: Arc<dyn ConfigParser>) -> Self {
ConfigClient {
backend,
parser,
cache: LocalCache::new(Duration::from_secs(60)),
}
}
pub fn get(&self, key: &str) -> Result<ConfigValue, ConfigError> {
// 1. 代理:先查本地缓存
if let Some(cached) = self.cache.get(key) {
return Ok(cached);
}
// 2. 桥接:解析器处理格式
let raw = self.backend.fetch(key)?;
let parsed = self.parser.parse(&raw)?;
// 3. 装饰:敏感值脱敏(简化)
let sanitized = self.sanitize(&parsed, key)?;
// 4. 享元:共享元数据
let meta = MetadataPool::shared().get_metadata(key);
let value = ConfigValue {
data: sanitized,
metadata: meta,
};
// 缓存结果
self.cache.set(key, value.clone());
Ok(value)
}
fn sanitize(&self, value: &ConfigValue, key: &str) -> Result<String, ConfigError> {
// 敏感配置脱敏逻辑
if key.contains("secret") || key.contains("password") {
Ok("***REDACTED***".to_string())
} else {
Ok(value.data.clone())
}
}
}
// ========== 适配器:后端存储接口 ==========
pub trait ConfigBackend: Send + Sync {
fn fetch(&self, key: &str) -> Result<String, ConfigError>;
fn watch(&self, key: &str, callback: Box<dyn Fn(String) + Send>) -> Result<WatchHandle, ConfigError>;
}
// etcd适配器
pub struct EtcdAdapter {
client: Arc<etcd_client::Client>, // 假设的etcd客户端
}
impl ConfigBackend for EtcdAdapter {
fn fetch(&self, key: &str) -> Result<String, ConfigError> {
// 实际调用etcd API
Ok("30".to_string()) // 简化
}
fn watch(&self, key: &str, callback: Box<dyn Fn(String) + Send>) -> Result<WatchHandle, ConfigError> {
// 实现watch逻辑
Ok(WatchHandle { id: 1 })
}
}
// Consul适配器(另一个实现)
pub struct ConsulAdapter {
// ...
}
impl ConfigBackend for ConsulAdapter {
// ... 实现ConfigBackend
fn fetch(&self, key: &str) -> Result<String, ConfigError> { todo!() }
fn watch(&self, key: &str, callback: Box<dyn Fn(String) + Send>) -> Result<WatchHandle, ConfigError> { todo!() }
}
// ========== 桥接:配置解析器 ==========
pub trait ConfigParser: Send + Sync {
fn parse(&self, raw: &str) -> Result<ConfigValue, ConfigError>;
}
pub struct JsonParser;
impl ConfigParser for JsonParser {
fn parse(&self, raw: &str) -> Result<ConfigValue, ConfigError> {
// 使用serde_json解析
Ok(ConfigValue {
data: raw.to_string(),
metadata: Arc::new(Metadata::default()),
})
}
}
pub struct YamlParser;
impl ConfigParser for YamlParser {
fn parse(&self, raw: &str) -> Result<ConfigValue, ConfigError> {
// 使用serde_yaml解析
Ok(ConfigValue {
data: raw.to_string(),
metadata: Arc::new(Metadata::default()),
})
}
}
// ========== 享元:元数据池 ==========
#[derive(Clone, Debug)]
pub struct Metadata {
pub path: Arc<str>,
pub version: u64,
pub created_at: u64,
}
impl Default for Metadata {
fn default() -> Self {
Metadata {
path: "".into(),
version: 0,
created_at: 0,
}
}
}
pub struct MetadataPool {
pool: HashMap<String, Arc<Metadata>>,
}
impl MetadataPool {
fn shared() -> &'static MetadataPool {
static POOL: OnceCell<MetadataPool> = OnceCell::new();
POOL.get_or_init(|| MetadataPool { pool: HashMap::new() })
}
fn get_metadata(&mut self, path: &str) -> Arc<Metadata> {
self.pool
.entry(path.to_string())
.or_insert_with(|| Arc::new(Metadata {
path: path.into(),
version: 0,
created_at: 0,
}))
.clone()
}
}
// ========== 代理:本地缓存 ==========
struct LocalCache {
entries: Mutex<HashMap<String, (ConfigValue, Instant)>>,
ttl: Duration,
}
impl LocalCache {
fn new(ttl: Duration) -> Self {
LocalCache {
entries: Mutex::new(HashMap::new()),
ttl,
}
}
fn get(&self, key: &str) -> Option<ConfigValue> {
let entries = self.entries.lock().unwrap();
if let Some((value, timestamp)) = entries.get(key) {
if timestamp.elapsed() < self.ttl {
return Some(value.clone());
}
}
None
}
fn set(&self, key: &str, value: ConfigValue) {
let mut entries = self.entries.lock().unwrap();
entries.insert(key.to_string(), (value, Instant::now()));
}
}
// ========== 辅助类型 ==========
#[derive(Clone, Debug)]
pub struct ConfigValue {
pub data: String,
pub metadata: Arc<Metadata>,
}
#[derive(Debug)]
pub enum ConfigError {
NotFound,
ParseError(String),
NetworkError(String),
}
pub struct WatchHandle { pub id: u32 }
// ========== 使用示例 ==========
fn main() {
// 选择后端与解析器(可配置)
let backend: Arc<dyn ConfigBackend> = Arc::new(EtcdAdapter {
client: Arc::new(etcd_client::Client::connect(["localhost:2379"]).unwrap()),
});
let parser: Arc<dyn ConfigParser> = Arc::new(JsonParser);
let client = ConfigClient::new(backend, parser);
// 简洁的客户端接口
match client.get("app.timeout") {
Ok(value) => println!("Timeout: {} (v{})", value.data, value.metadata.version),
Err(e) => eprintln!("Error: {}", e),
}
// 监听配置变更
let _handle = client.backend.watch("app.timeout", Box::new(|new_val| {
println!("Config changed: {}", new_val);
})).unwrap();
}
模式协作优势:
- 外观模式提供简洁统一的API,降低客户端使用复杂度
- 适配器模式支持多后端无缝切换,提升系统扩展性
- 桥接模式解耦解析逻辑与存储实现,便于独立演进
- 装饰器模式灵活组合横切功能,避免代码重复
- 享元模式显著减少元数据内存占用,提升大规模配置场景性能
- 代理模式通过本地缓存降低远程请求频次,提升响应速度
常见错误与解决
错误1:适配器模式 - 生命周期管理不当
错误表现:适配器持有适配者引用时未正确处理生命周期,导致悬垂引用。
错误代码:
pub struct Adapter<'a> {
adaptee: &'a Adaptee, // 生命周期绑定
}
impl<'a> Target for Adapter<'a> {
fn request(&self) -> String {
self.adaptee.specific_request()
}
}
fn problematic() {
let adapter;
{
let adaptee = Adaptee { data: "temp".into() };
adapter = Adapter { adaptee: &adaptee }; // adaptee即将被丢弃
} // adaptee在此处被drop
adapter.request(); // ❌ 悬垂引用!
}
解决方案:使用Arc或所有权转移确保生命周期安全。
修正代码:
pub struct Adapter {
adaptee: Arc<Adaptee>, // 共享所有权
}
impl Adapter {
pub fn new(adaptee: Arc<Adaptee>) -> Self {
Adapter { adaptee }
}
}
// 或采用所有权转移
pub struct Adapter {
adaptee: Adaptee,
}
错误2:组合模式 - 递归类型大小问题
错误表现:直接递归定义枚举导致编译错误”recursive type has infinite size”。
错误代码:
pub enum TreeNode {
Leaf { value: i32 },
Node { children: Vec<TreeNode> }, // ❌ TreeNode包含TreeNode,大小无限
}
解决方案:使用Box打破递归,或采用trait对象。
修正代码:
// 方案1: Box包装
pub enum TreeNode {
Leaf { value: i32 },
Node { children: Vec<Box<TreeNode>> },
}
// 方案2: Trait对象(更灵活)
pub trait Node {
fn evaluate(&self) -> i32;
}
pub struct LeafNode { value: i32 }
pub struct InternalNode { children: Vec<Box<dyn Node>> }
错误3:装饰器模式 - 泛型爆炸
错误表现:多层泛型装饰器导致编译时间激增、二进制体积膨胀。
错误代码:
// 5层装饰:类型签名极其复杂
type ComplexHandler = LoggingDecorator<
AuthDecorator<
RateLimitDecorator<
CacheDecorator<
EchoHandler
>
>
>
>;
解决方案:关键层使用trait对象减少泛型嵌套。
修正代码:
// 内部保持泛型优化,边界使用trait对象
pub fn build_handler(enable_cache: bool) -> Box<dyn Handler> {
let mut handler: Box<dyn Handler> = Box::new(EchoHandler);
if enable_cache {
handler = Box::new(CacheDecorator { inner: handler });
}
handler = Box::new(AuthDecorator { inner: handler });
handler = Box::new(LoggingDecorator { inner: handler });
handler
}
错误4:享元模式 - 可变状态共享
错误表现:错误地共享可变状态,导致数据竞争或意外修改。
错误代码:
#[derive(Clone)]
pub struct SharedState {
pub counter: u32, // ❌ 可变状态被共享
}
pub struct Flyweight {
state: Arc<SharedState>,
}
// 线程1
flyweight1.state.counter += 1;
// 线程2
flyweight2.state.counter += 1; // ❌ 数据竞争!
解决方案:享元内部状态必须不可变,可变部分作为外部状态。
修正代码:
// 内部状态:不可变 + Arc共享
#[derive(Clone, Debug)]
pub struct ImmutableState {
pub config: Arc<Config>,
pub template: Arc<str>,
}
// 外部状态:每个对象独有
pub struct Flyweight {
immutable: Arc<ImmutableState>,
mutable: MutableState, // 不共享
}
pub struct MutableState {
pub counter: u32,
pub last_modified: Instant,
}
错误5:代理模式 - 死锁风险
错误表现:代理层嵌套获取锁时顺序不一致,导致死锁。
错误代码:
pub struct Proxy {
cache: Mutex<Cache>,
db: Mutex<Database>,
}
impl Proxy {
fn query(&self, key: &str) -> Result<Value, Error> {
// 先锁cache
let cache = self.cache.lock()?;
if let Some(v) = cache.get(key) {
return Ok(v);
}
// 再锁db - 但其他线程可能先锁db再锁cache!
let db = self.db.lock()?; // ❌ 死锁风险
// ...
}
}
解决方案:统一锁获取顺序,或使用无锁数据结构。
修正代码:
// 方案1: 固定锁顺序(文档约定)
impl Proxy {
fn query(&self, key: &str) -> Result<Value, Error> {
// 始终先db后cache
let db = self.db.lock()?;
let value = db.fetch(key)?;
let mut cache = self.cache.lock()?;
cache.insert(key, value.clone());
Ok(value)
}
}
// 方案2: 使用DashMap等无锁结构
use dashmap::DashMap;
pub struct Proxy {
cache: DashMap<String, Value>, // 无锁并发HashMap
db: Mutex<Database>,
}
总结要点
Rust结构性模式的核心价值
- 组合优于继承:通过trait组合与结构体嵌套实现灵活复用,避免继承层次僵化
- 零成本抽象:泛型与内联优化确保设计模式不引入运行时开销
- 编译期安全:类型系统提前捕获接口不匹配、生命周期错误等问题
- 并发友好:
Arc/Mutex/OnceCell等原语简化线程安全实现
模式选择指南
| 场景需求 | 推荐模式 | Rust实现要点 |
|---|---|---|
| 接口不兼容 | 适配器 | trait抽象 + 结构体包装 |
| 抽象/实现解耦 | 桥接 | 泛型参数 或 Box |
| 树形层次结构 | 组合 | Box |
| 动态功能增强 | 装饰器 | 泛型委托 + 方法链 |
| 简化复杂子系统 | 外观 | 模块封装 + trait委托 |
| 大量相似对象 | 享元 | Arc共享 + 不可变内部状态 |
| 访问控制/延迟加载 | 代理 | OnceCell + 智能指针包装 |
性能优化建议
- 优先静态分发:能用泛型就不用trait对象,减少虚表调用
- 避免过度包装:每层代理/装饰增加~1-2纳秒开销,评估必要性
- 享元粒度控制:共享对象不宜过大,避免不必要的内存驻留
- 缓存策略调优:根据访问模式设置合理的TTL与容量上限
Rust惯用法融合
- 使用
#[derive]减少样板代码:Clone/Debug/PartialEq等 - 利用
?操作符简化错误传播:外观层集中处理错误转换 - 采用
impl Trait简化返回类型:避免暴露具体装饰器类型 - 结合
macro_rules!生成重复代码:如批量实现trait委托
进阶资源
- Rust Design Patterns - Structural
- The Rustonomicon - Smart Pointers
- OnceCell Documentation
- DashMap - Concurrent HashMap
- Rust API Guidelines - Type System
Rust社区风采
https://example.com/rust-structural-patterns.jpg
Rust社区通过类型系统的创新应用,为传统结构性设计模式注入了新的活力。在保持设计模式解耦与复用核心价值的同时,借助所有权、trait与零成本抽象等特性,实现了前所未有的类型安全与性能保障。这些实践不仅提升了代码质量,更为构建可靠、高效的大型系统提供了坚实的方法论基础。 ```
💡 使用提示:将上述markdown内容保存为
rust-structural-patterns.md文件,即可用于博客发布或团队文档。文中所有代码示例均可直接在Rust项目中编译运行(需添加相应依赖如once_cell)。
核心要点回顾:
- 适配器:通过trait实现接口转换,解决兼容性问题
- 桥接:泛型与trait对象结合,分离抽象与实现
- 组合:
Box<dyn Trait>实现类型安全的树形结构 - 装饰器:泛型委托实现零成本的行为动态增强
- 外观:模块封装隐藏子系统复杂性
- 享元:
Arc共享不可变状态,显著减少内存占用 - 代理:智能指针+懒加载实现访问控制与优化
通过合理运用这些结构性模式,您可以在保持Rust类型安全与性能优势的同时,构建出灵活、可维护的大型系统架构。🦀✨