Rust语言之GoF设计模式:Builder建造者模式


Builder是一种创建设计模式,它允许逐步构建复杂的对象。

Builder设计模式与Fluent Interface习惯用法不同,尽管 Rust 开发人员有时会互换使用这些术语。

  1. Fluent Interface idiom 是一种使用以下方法链接构造或修改对象的方法的方法: let car = Car::default().places(5).gas(30). 它对于构造对象非常有用。不过,它不是 Builder。
  2. Builder是一种具有共同构建trait但具有不同构建实现的模式。同时,Fluent Interface 可以与 Builder 模式一起使用,以获得更好的代码设计。

在此示例中,trait Builder定义了如何组装汽车。但是,根据构建器的实现,构造的对象可以是汽车,也可以是汽车手册。

mod.rs

mod car;
mod car_manual;

use crate::components::{CarType, Engine, GpsNavigator, Transmission};

/// Builder 定义了如何组装一辆汽车。
pub trait Builder {
    type OutputType;
    fn set_car_type(&mut self, car_type: CarType);
    fn set_seats(&mut self, seats: u16);
    fn set_engine(&mut self, engine: Engine);
    fn set_transmission(&mut self, transmission: Transmission);
    fn set_gsp_navigator(&mut self, gps_navigator: GpsNavigator);
    fn build(self) -> Self::OutputType;
}

pub use car::CarBuilder;
pub use car_manual::CarManualBuilder;

构造目标是创建两种不同产品:
car.rs

use crate::{
    cars::Car,
    components::{CarType, Engine, GpsNavigator, Transmission},
};

use super::Builder;

pub const DEFAULT_FUEL: f64 = 5f64;

#[derive(Default)]
pub struct CarBuilder {
    car_type: Option<CarType>,
    engine: Option<Engine>,
    gps_navigator: Option<GpsNavigator>,
    seats: Option<u16>,
    transmission: Option<Transmission>,
}

impl Builder for CarBuilder {
    type OutputType = Car;

    fn set_car_type(&mut self, car_type: CarType) {
        self.car_type = Some(car_type);
    }

    fn set_engine(&mut self, engine: Engine) {
        self.engine = Some(engine);
    }

    fn set_gsp_navigator(&mut self, gps_navigator: GpsNavigator) {
        self.gps_navigator = Some(gps_navigator);
    }

    fn set_seats(&mut self, seats: u16) {
        self.seats = Some(seats);
    }

    fn set_transmission(&mut self, transmission: Transmission) {
        self.transmission = Some(transmission);
    }

    fn build(self) -> Car {
        Car::new(
            self.car_type.expect("Please, set a car type"),
            self.seats.expect(
"Please, set a number of seats"),
            self.engine.expect(
"Please, set an engine configuration"),
            self.transmission.expect(
"Please, set up transmission"),
            self.gps_navigator,
            DEFAULT_FUEL,
        )
    }
}

汽车手册本:car_manual.rs

下面是Builder模式这个结构的导演、指导者:
director.rs

use crate::{
    builders::Builder,
    components::{CarType, Engine, GpsNavigator, Transmission},
};

/// 导演知道如何造车。
///
/// 然而,建造者可以建造一个汽车手册说明书,而不是一辆真正的汽车。
/// 一切都取决于具体的建造者。
pub struct Director;

impl Director {
    pub fn construct_sports_car(builder: &mut impl Builder) {
        builder.set_car_type(CarType::SportsCar);
        builder.set_seats(2);
        builder.set_engine(Engine::new(3.0, 0.0));
        builder.set_transmission(Transmission::SemiAutomatic);
        builder.set_gsp_navigator(GpsNavigator::new());
    }

    pub fn construct_city_car(builder: &mut impl Builder) {
        builder.set_car_type(CarType::CityCar);
        builder.set_seats(2);
        builder.set_engine(Engine::new(1.2, 0.0));
        builder.set_transmission(Transmission::Automatic);
        builder.set_gsp_navigator(GpsNavigator::new());
    }

    pub fn construct_suv(builder: &mut impl Builder) {
        builder.set_car_type(CarType::Suv);
        builder.set_seats(4);
        builder.set_engine(Engine::new(2.5, 0.0));
        builder.set_transmission(Transmission::Manual);
        builder.set_gsp_navigator(GpsNavigator::new());
    }
}

下面看看builder设计模式的客户端调用,看看通过 builder这个结构赋能了客户端多大自由性?

main.rs

#![allow(unused)]

mod builders;
mod cars;
mod components;
mod director;

use builders::{Builder, CarBuilder, CarManualBuilder};
use cars::{Car, Manual};
use director::Director;

fn main() {
    let mut car_builder = CarBuilder::default();

    // Director从客户那里获得具体的建造者对象
   
// (应用程序代码)。这是因为应用程序更知道
   
// 构建器来获得一个特定的产品。
    Director::construct_sports_car(&mut car_builder);

   
// 最终的产品通常是从构建器对象中获取的,因为
   
// Director不知道也不依赖于具体的构建者和
   
// 产品。
    let car: Car = car_builder.build();
    println!(
"Car built: {:?}\n", car.car_type());

    let mut manual_builder = CarManualBuilder::default();

    
// Director可能知道几个建造配方
    Director::construct_city_car(&mut manual_builder);

    
// 最后建构汽车手册。
    let manual: Manual = manual_builder.build();
    println!(
"Car manual built:\n{}", manual);
}

参考:Java的设计模式之Builder

总结
Builder模式主要是用于创建复杂目标对象,这个目标对象内部有复杂的Complicated内部结构和组件,是一个大群体,一般用于DDD中创建聚合根对象。