接着上一篇文章,我们在这里看一下Rust的面向对象编程范式和编程范式的最佳实践。
与前面讨论的函数式和命令式示例相反,让我们引入一个新的结构体FileFilter,它封装了过滤文件和文件迭代的逻辑。
pub struct FileFilter {
predicates: Vec<Box<Predicate>>,
start: Option<PathBuf>,
stack: Vec<fs::ReadDir>,
}
每个FileFilter对象都携带其状态:用于过滤的谓词集合、起始路径和用于迭代的目录栈。
Predicate是这样定义的:
type Predicate = dyn Fn(&Path) -> bool;
你可能会惊讶地发现这里有一个dyn。在Rust中,没有两个闭包具有相同的类型,即使它们完全相同!
为了在Vec集合中适应这一点,我们使用带有动态分派的trait对象。通过“装箱”这些闭包,我们创建了一个Box
在函数式编程中,我们利用迭代器和闭包的强大功能来过滤文件。在命令式风格中,我们直接使用循环和条件来操作向量。在面向对象风格中,结构体FileFilter抽象掉了这些细节。
看一下add_filter方法:
pub fn add_filter(mut self, predicate: impl Fn(&Path) -> bool + 'static) -> Self {
self.predicates.push(Box::new(predicate));
self
}
这允许我们通过链接调用轻松地添加多个过滤器:
let filter = FileFilter::new()
.add_filter(|path| {
// 检查路径是否以“foo”开头
path.file_name()
.and_then(OsStr::to_str)
.map(|name| name.starts_with("foo"))
.unwrap_or(false)
})
.add_filter(|path| path.extension() == Some(OsStr::new("xml")));
真正展示Rust中面向对象方法的是FileFilter的Iterator trait的实现:
impl Iterator for FileFilter {
type Item = Result<PathBuf>;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
这样,FileFilter就成为了一个构建块,它与Rust强大的迭代器生态系统完美地集成在一起,可以像其他任何迭代器一样在所有相同的地方使用,这种设计允许将复杂的迭代逻辑封装在对象中。
FileFilter示例说明了Rust中的OOP如何进行可靠的封装和模块化,我们将内容(谓词)与方式(迭代和过滤逻辑)分开。trait系统轻松地将自定义迭代器与生态系统的其余部分集成在一起,使用这些工具可以使代码更易于组合和重用。
在Rust中混合不同的编程风格不仅是可能的,而且是鼓励的!从Rust对其语言设计的主要理念中也可以看出这一点。C、Haskell、OCaml和Erlang等各种各样的影响塑造了Rust的设计。
一开始,Rust在本质上更偏向于函数式,但后来它演变成了一种更加平衡的语言,支持各种风格,问题是如何在不同的编程范例之间划清界限。
以下是一些最佳实践:
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8