Rust Design Pattern

设计模式的使用,是对开发者的考验。比如说在 Golang 中最常用的是工厂模式,创建模块时,以 Struct 为基础,常命名为 NewXX 开头发的方法作为工厂模式。而在 Rust 中,常用的则是建造模式(builder),由此来创建某一模块或功能的集合。

另外在 Rust 中独有的模式,例如 Drop

Builder

Builder^1 is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

建造者是一个创造性的设计模式,让你一步步构建复杂的对象。模式允许你使用相同的结构代码构建不同的类型和表现对象。

例子使用Box Builder,构建不同尺寸的盒子,通过链式调用设置不同的盒子属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
struct Box {
width: u32,
height: u32,
}
struct BoxBuilder {
width: Option<u32>,
height: Option<u32>,
}

impl BoxBuilder {
fn new() -> Self {
BoxBuilder {
width: None,
height: None,
}
}

fn default() -> Self {
BoxBuilder {
width: Some(30),
height: Some(30),
}
}

fn set_width(mut self, width: u32) -> Self {
self.width = Some(width);
self
}

fn set_height(mut self, height: u32) -> Self {
self.height = Some(height);
self
}

fn build(self) -> Box {
let width = self.width.unwrap();
let height = self.height.unwrap();
Box { width, height }
}
}

fn main() {
let box1: Box = BoxBuilder::new().set_height(20).set_width(10).build();
println!(
"the box1 width is {}, height is {}.",
box1.width, box1.height
);

let box2: Box = BoxBuilder::default().build();
println!(
"the box2 width is {}, height is {}.",
box2.width, box2.height
);
}

output:

1
2
3
4
5
6
➜  design git:(master) ✗ cargo run
Compiling design v0.1.0 (/Users/yother/WorkSpace/BackEnd/Rust/module/design)
Finished dev [unoptimized + debuginfo] target(s) in 1.06s
Running `target/debug/design`
the box1 width is 10, height is 20.
the box2 width is 30, height is 30.

通过架构体 BoxBuilder 构建对象 Box,实现的方法中有设置长、宽和默认大小,最后通过build()函数调用实现构建出指定的 Box 对象。

使用trait构建对象

这种方式有些类似约束 struct 对象方法,使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
trait Builder {
type OutType;
fn set_width(&mut self, width: u32);
fn set_height(&mut self, height: u32);

fn width(&self) -> u32;
fn height(&self) -> u32;
fn build(&self) -> Self;
}

struct Box {
width: u32,
height: u32,
}

impl Box {
fn small_box(builder: &mut impl Builder) -> Self {
Box::build(builder, 10)
}

fn mid_box(builder: &mut impl Builder) -> Self {
Box::build(builder, 20)
}

fn big_box(builder: &mut impl Builder) -> Self {
Box::build(builder, 30)
}

fn build(builder: &mut impl Builder, size: u32) -> Self {
builder.set_height(size);
builder.set_width(size);

let build = builder.build();
Box {
width: build.width(),
height: build.height(),
}
}
}

#[derive(Default)]
struct BoxBuilder {
width: u32,
height: u32,
}
impl BoxBuilder {}

impl Builder for BoxBuilder {
type OutType = BoxBuilder;
fn set_width(&mut self, width: u32) {
self.width = width;
}

fn set_height(&mut self, height: u32) {
self.height = height;
}

fn build(&self) -> Self::OutType {
BoxBuilder {
width: self.width,
height: self.height,
}
}

fn width(&self) -> u32 {
self.width
}

fn height(&self) -> u32 {
self.height
}
}

fn main() {
let mut builder = BoxBuilder::default();
let small_box = Box::small_box(&mut builder);
println!(
"build small box, the spec is: width({}), height({})",
small_box.width, small_box.height
);

let mid_box: Box = Box::mid_box(&mut builder);
println!(
"build mid box, the spec is: width({}), height({})",
mid_box.width, mid_box.height
);

let big_box: Box = Box::big_box(&mut builder);
println!(
"build big box, the spec is: width({}), height({})",
big_box.width, big_box.height
);
}

output:

1
2
3
4
5
6
7
➜  design git:(master) ✗ cargo run
Compiling design v0.1.0 (/Users/yother/WorkSpace/BackEnd/Rust/module/design)
Finished dev [unoptimized + debuginfo] target(s) in 0.22s
Running `target/debug/design`
build small box, the spec is: width(10), height(10)
build mid box, the spec is: width(20), height(20)
build big box, the spec is: width(30), height(30)

通过 BoxBuilderBuilder 构架不同尺寸的盒子,根据 Builder 设置盒子的大小,这也是在 Rust 中常用获取不同类型实例的方式。例如上述代码中 small_box mid_box big_box

Drop

Running Code On Cleanup With The Drop Trait.

使用 Drop Trait 在清理时运行代码。

我们都知道,Rust 没有 GC(垃圾回收),只能通过手动控制变量的创建与销毁。当有一些操作需要在销毁前执行,那么就需要使用Drop内置 Trait 来处理。官方描述^2如下:

which lets you customize what happens when a value is about to go out of scope. You can provide an implementation for the Drop trait on any type, and that code can be used to release resources like files or network connections.

允许你自定义当值即将超越范围时的情况,你可以提供为任意类型实现Drop,代码可用于释放文件或网络连接等资源。

Usage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
trait File {
type OutType;
fn open(filename: String) -> Self::OutType;
fn size(&self) -> u32;
fn close(&self);
}

#[derive(Default)]
struct Doc {
name: String,
size: u32,
}

impl File for Doc {
type OutType = Doc;

fn open(filename: String) -> Self::OutType {
Doc {
name: filename,
size: 10,
}
}

fn close(&self) {
println!("close the filename: {}", self.name);
}

fn size(&self) -> u32 {
self.size
}
}

impl Drop for Doc {
fn drop(&mut self) {
self.close();
}
}

fn main() {
println!("process start");
let doc = Doc::open(String::from("study.doc"));
println!("doc size:{}", doc.size());
// 主动调用drop回收类型变量
// drop(doc);
println!("process end");
}

模仿文件打开与关闭操作,并实现Drop trait,在类型变量离开作用域之前,调用实现的 drop(&mut self) 函数输出: "close the filename: {}", self.name。与其说一种设计模式,倒不如说是一种特性会更合适些。

引用