为什么Rust中的架构不是奢侈品,而是必需品
当你用Rust编写代码时,类型系统和借用检查器已经为你提供了80%的错误防护。但内存安全仅仅是基础。如果你在这个基础上构建混乱的架构,项目迟早会变成一个相互依赖的迷宫,修改一个模块就会破坏其他十个模块。
Rust中的架构模式并非时尚潮流,也不是面向对象世界的遗留物。它们是经过验证的典型问题解决方案:如何创建一个不会违反不变量的对象?如何连接组件以便可以单独测试它们?如何组织逻辑以便可以在不修改现有代码的情况下进行扩展?
在本文中,我们将通过Rust的实际示例来讨论三组模式(创建型、结构型、行为型)。没有抽象的理论——只有可以直接应用的代码。
创建型模式:正确创建对象
Builder模式——从10个参数的构造函数中拯救你
Rust中没有构造函数重载。如果一个结构体有超过3-4个字段,为所有情况编写一个构造函数就意味着用户必须阅读带有Option和Default的长签名。Builder模式优雅地解决了这个问题。
struct ServerConfig { host: String, port: u16, workers: u8, enable_tls: bool, max_connections: u32,}
struct ServerConfigBuilder { host: Option<String>, port: Option<u16>, workers: Option<u8>, enable_tls: Option<bool>, max_connections: Option<u32>,}
impl ServerConfigBuilder { fn new() -> Self { Self { host: None, port: None, workers: None, enable_tls: None, max_connections: None, } }
fn host(mut self, host: &str) -> Self { self.host = Some(host.to_string()); self }
fn port(mut self, port: u16) -> Self { self.port = Some(port); self }
fn workers(mut self, workers: u8) -> Self { self.workers = Some(workers); self }
fn enable_tls(mut self, enable: bool) -> Self { self.enable_tls = Some(enable); self }
fn max_connections(mut self, max: u32) -> Self { self.max_connections = Some(max); self }
fn build(self) -> Result<ServerConfig, String> { Ok(ServerConfig { host: self.host.unwrap_or_else(|| "127.0.0.1".to_string()), port: self.port.unwrap_or(8080), workers: self.workers.unwrap_or(4), enable_tls: self.enable_tls.unwrap_or(false), max_connections: self.max_connections.unwrap_or(100), }) }}
// 使用方式let config = ServerConfigBuilder::new() .host("0.0.0.0") .port(443) .enable_tls(true) .build() .expect("Invalid config");为什么这在Rust中有效? Builder模式与所有权系统完美结合:方法链消费并返回self,最终的build要么返回准备好的对象,要么返回验证错误。没有可变引用和数据竞争。
工厂模式——当返回类型取决于参数时
想象一下,你需要解析不同的格式:JSON、YAML、TOML。与其在每个地方使用match,不如使用工厂方法。
trait Parser { fn parse(&self, input: &str) -> Result<serde_json::Value, String>;}
struct JsonParser;impl Parser for JsonParser { fn parse(&self, input: &str) -> Result<serde_json::Value, String> { serde_json::from_str(input).map_err(|e| e.to_string()) }}
struct YamlParser;impl Parser for YamlParser { fn parse(&self, input: &str) -> Result<serde_json::