工厂模式的是将创建逻辑封装在一个方法中,在 "外部"实现对其使用。
(banq::老子道德经中“无以为用”,“无”的意思就是跳出事物内部细节,从事物外部才能使用它。Rust的事物内部和外部边界很严格,所有权概念使然。)
简单工厂
简单工厂模式只是一个带有大条件的函数,根据参数选择要实例化的产品,然后返回,因为没有继承和复杂的创建特征,因此它是简单工厂。
create_button是一个功能(一个事物):创建随机按钮:
fn create_button(random_number: f64) -> Box<dyn Button> { if random_number < 0.5 { Box::new(TitleButton::new("Button".to_string())) } else { Box::new(IdButton::new(123)) } }
|
而render_dialog则使用从create_button得到的任何结果,再对其进行操作:
fn render_dialog(random_number: f64) { // ... let button = create_button(random_number); button.render(); // ... }
|
工厂方法
工厂方法是一种创建设计模式,它提供了在超特征中创建对象的接口,但允许子特征改变将要创建的对象的类型。
案例一:以创建对话框按钮为例:
主接口Dialog:gui.rs
pub trait Button { fn render(&self); fn on_click(&self); }
/// Dialog有一个工厂方法`create_button`。 /// /// 它根据工厂的实现来创建不同的按钮。 pub trait Dialog { /// 这里是工厂方法,必须有一个具体的实现来覆盖,具体实现有两个。 fn create_button(&self) -> Box<dyn Button>;
fn render(&self) { let button = self.create_button(); button.render(); }
fn refresh(&self) { println!("Dialog - Refresh"); } }
|
工厂方法的两个实现之一:Windows风格的按钮:windows_gui.rs
use crate::gui::{Button, Dialog};
pub struct WindowsButton;
impl Button for WindowsButton { fn render(&self) { println!("Drawing a Windows button"); self.on_click(); }
fn on_click(&self) { println!("Click! Hello, Windows!"); } }
pub struct WindowsDialog;
impl Dialog for WindowsDialog { /// 创建一个windows风格按钮 fn create_button(&self) -> Box<dyn Button> { Box::new(WindowsButton) } }
|
工厂方法的两个实现之一:html_gui.rs
use crate::gui::{Button, Dialog};
pub struct HtmlButton;
impl Button for HtmlButton { fn render(&self) { println!("<button>Test Button</button>"); self.on_click(); }
fn on_click(&self) { println!("Click! Button says - 'Hello World!'"); } }
pub struct HtmlDialog;
impl Dialog for HtmlDialog { /// 创建一个 HTML 按钮. fn create_button(&self) -> Box<dyn Button> { Box::new(HtmlButton) } }
|
工厂方法的调用客户端:main.rs
mod gui; mod html_gui; mod init; mod windows_gui;
use init::initialize;
fn main() { // 其余的代码并不依赖于特定的对话框类型,因为 // 它通过抽象的`Dialog`特性与所有对话框对象一起工作。 // 在`gui`模块中定义的抽象的`Dialog`特性对所有的对话框都有效。 let dialog = initialize(); dialog.render(); dialog.refresh(); }
|
案例二:游戏案例:
游戏房间主接口:game.rs
/// 将用工厂方法实例化的迷宫房间。 pub trait Room { fn render(&self); }
/// 迷宫游戏有一个工厂方法产生不同的房间。 pub trait MazeGame { type RoomImpl:Room;
/// 一个工厂方法 fn rooms(&self) -> Vec<Self::RoomImpl>;
fn play(&self) { for room in self.rooms() { room.render()。 } } }
/// 客户端代码初始化资源并做其他准备工作。 /// 然后它使用一个工厂来构建和运行游戏。 pub fn run(maze_game:impl MazeGame) { println! ("加载资源...")。 println!("开始游戏...")。
maze_game.play()。 }
|
游戏房间有两个实现:
- 普通游戏房间:ordinary_maze.rs
- 魔法游戏房间:magic_maze.rs
调用工厂方法的客户端代码:main.rs
mod game; mod magic_maze; mod ordinary_maze;
use magic_maze::MagicMaze; use ordinary_maze::OrdinaryMaze;
///游戏运行时,根据具体的工厂类型,会有不同的迷宫。 ///要么是普通迷宫,要么是魔法迷宫。 /// /// 为了演示的目的,两种迷宫都用来构建游戏。 fn main() { // 选项1:游戏从一个普通迷宫开始。 let ordinary_maze = OrdinaryMaze::new(); game::run(ordinary_maze);
// 选项2:游戏从一个魔法迷宫开始。 let magic_maze = MagicMaze::new(); game::run(magic_maze); }
|