Rust 建造者模式

407 阅读5分钟

在DDD中,DTO(数据传输对象)->BO(业务对象)、BO(业务对象)->PO(持久化对象,有的叫DO,即和数据表映射的实体)等等情况要做转换,这里提供以下转换方式

1、from或者try_from trait实现对象转换

需要转换对象满足接收对象的所有字段

客户定义

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Customer {
    // uuid
    pub user_id: String,
    // 用户名
    pub username: String,
    // 邮件
    pub email: String,
    // 密码
    pub password: String,
    // 头像
    pub avatar: Option<String>,
    // 验证码
    pub verify_code: Option<String>,
    // 收货地址
    pub receive_address: Vec<ReceiveAddress>,
}

通过实现from trait,可以从Model转换为Customer

impl From<Model> for Customer {
    fn from(user: Model) -> Self {
        Customer {
            user_id: user.user_id,
            username: user.username,
            email: user.email,
            password: user.password,
            avatar: user.avatar,
            verify_code: user.verify_code,
            receive_address: user.receive_address,
        }
    }
}

实现了From trait默认自动实现Into trait,你可以通过以下两种方式实现对象转换,Try from trait用法一样,只是在转换失败时可以返回错误

// 使用from方法将Model实例转换为Customer实例
let customer_instance = Customer::from(model_instance);

// 或者使用更简洁的into方法,它会自动调用对应的from方法
let another_customer_instance = model_instance.into();

但是这样不够优雅,很多时候DTO并不能满足领域对象的所有字段,数据对象也不能满足领域对象的所有字段,例如以上例子的验证码和收货地址,最初没有数据时需要设置默认值

// 转Bo
impl From<Model> for Customer {
    fn from(user: Model) -> Self {
        Customer {
            user_id: user.user_id,
            username: user.username,
            email: user.email,
            password: user.password,
            avatar: user.avatar,
            verify_code: None,
            receive_address: vec![],
        }
    }
}

当下一次从数据库中查到数据需要给收货地址赋值的情况下,这种方案就不适用了,可以使用以下建造者模式

2、链式调用

此时所有字段都是private的,通过builder去赋值

// 注意使用了Default,没有builder的值有默认值
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Customer {
    // uuid
    pub user_id: String,
    // 用户名
    pub username: String,
    // 邮件
    pub email: String,
    // 密码
    pub password: String,
    // 头像
    pub avatar: Option<String>,
    // 验证码
    pub verify_code: Option<String>,
    // 收货地址
    pub receive_address: Vec<ReceiveAddress>,
}
impl Customer {
    // new默认值
    pub fn new() -> Self {
        Self {
            user_id: String::new(),
            username: String::new(),
            email: String::new(),
            password: String::new(),
            avatar: None,
            verify_code: None,
            receive_address: Vec::new(),
        }
    }
    pub fn user_id(mut self, user_id: String) -> Self {
        self.user_id = user_id;
        self
    }

    pub fn username(mut self, username: String) -> Self {
        self.username = username;
        self
    }

    pub fn email(mut self, email: String) -> Self {
        self.email = email;
        self
    }

    pub fn password(mut self, password: String) -> Self {
        self.password = password;
        self
    }

    pub fn avatar(mut self, avatar: Option<String>) -> Self {
        self.avatar = avatar;
        self
    }

    pub fn verify_code(mut self, verify_code: Option<String>) -> Self {
        self.verify_code = verify_code;
        self
    }

    pub fn receive_address(mut self, receive_address: Vec<ReceiveAddress>) -> Self {
        self.receive_address = receive_address;
        self
    }
}

使用

    let customer = Customer::new()
        .user_id("123".to_string())
        .username("张三".to_string())
        .email("<EMAIL>".to_string());
    let customer = customer.avatar(Some("https://www.baidu.com".to_string()));
    print!("{:?}\n", customer);
    //Customer { user_id: "123", username: "张三", email: "<EMAIL>", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }

    // 修改原有对象
    let customer = customer.email("123@qq.com".to_string());
    println!("{:?}", customer);
    //Customer { user_id: "123", username: "张三", email: "123@qq.com", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }

这种方式容易造成意外修改的传播,不推荐

3、建造者模式实现对象转换

在Java中很简单,加上@Builder注解即可

@Builder
public class User {
  private UserLastname lastname;
  private UserFirstname firstname;
  private UserEmail email;
  private UserPublicId userPublicId;
  private UserImageUrl imageUrl;
  private Instant lastModifiedDate;
  private Instant createdDate;
  private Set<Authority> authorities;
  private Long dbId;
  private UserAddress userAddress;
  private Instant lastSeen;
  public User(UserLastname lastname, UserFirstname firstname, UserEmail email, UserPublicId userPublicId, UserImageUrl imageUrl, Instant lastModifiedDate, Instant createdDate, Set<Authority> authorities, Long dbId, UserAddress userAddress, Instant lastSeen) {
    this.lastname = lastname;
    this.firstname = firstname;
    this.email = email;
    this.userPublicId = userPublicId;
    this.imageUrl = imageUrl;
    this.lastModifiedDate = lastModifiedDate;
    this.createdDate = createdDate;
    this.authorities = authorities;
    this.dbId = dbId;
    this.userAddress = userAddress;
    this.lastSeen = lastSeen;
  }
}

通过builder()使用,通过结尾的build()返回新对象

UserBuilder
      .email(user.getEmail().value())
      .firstName(user.getFirstname().value())
      .lastName(user.getLastname().value())
      .publicId(user.getUserPublicId().value())
      .authorities(RestAuthority.fromSet(user.getAuthorities()))
      .build()

Rust实现(值传递)建造者模式

和直接链式调用相比,添加了一个build函数返回新对象

// 注意使用了Default,没有builder的值有默认值
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Customer {
    // uuid
    user_id: String,
    // 用户名
    username: String,
    // 邮件
    email: String,
    // 密码
    password: String,
    // 头像
    avatar: Option<String>,
    // 验证码
    verify_code: Option<String>,
    // 收货地址
    receive_address: Vec<ReceiveAddress>,
}
// 建造(者结构体,包含一个需要构建的对象
#[derive(Default, Clone, Debug)]
pub struct CustomerBuilder {
    customer: Customer,
}
impl CustomerBuilder {
    pub fn new() -> Self {
    // 初始化默认值
        CustomerBuilder::default()
    }
    pub fn user_id(mut self, user_id: String) -> Self {
        self.customer.user_id = user_id;
        self
    }

    pub fn username(mut self, username: String) -> Self {
        self.customer.username = username;
        self
    }

    pub fn email(mut self, email: String) -> Self {
        self.customer.email = email;
        self
    }

    pub fn password(mut self, password: String) -> Self {
        self.customer.password = password;
        self
    }

    pub fn avatar(mut self, avatar: Option<String>) -> Self {
        self.customer.avatar = avatar;
        self
    }

    pub fn verify_code(mut self, verify_code: Option<String>) -> Self {
        self.customer.verify_code = verify_code;
        self
    }

    pub fn receive_address(mut self, receive_address: Vec<ReceiveAddress>) -> Self {
        self.customer.receive_address = receive_address;
        self
    }
    pub fn build(self) -> Customer {
        Customer {
            user_id: self.customer.user_id,
            username: self.customer.username,
            email: self.customer.email,
            password: self.customer.password,
            avatar: self.customer.avatar,
            verify_code: self.customer.verify_code,
            receive_address: self.customer.receive_address,
        }
    }
}

使用,没有建造的字段由于Default宏的存在会初始化默认值,这种用法和第二种链式调用方式相比每次创建新对象,对象无法修改,只能创建新对象,使用对象会消耗对象适合创建值对象响应DTOEvent(因为这些对象用完就会被Drop,创建后就不可变)

   let customer_builder = CustomerBuilder::new();
   let customer = customer_builder
       .clone()
       .user_id("123".to_string())
       .username("张三".to_string())
       .email("<EMAIL>".to_string());
   let customer = customer.clone().avatar(Some("https://www.baidu.com".to_string()));
   let customer = customer.clone().build();
   print!("{:?}\n", customer);
   // Customer { user_id: "123", username: "张三", email: "<EMAIL>", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }
   // 创建新对象
   let customer = customer_builder.clone().email("123@qq.com".to_string()).build();
   println!("{:?}", customer);
    // Customer { user_id: "", username: "", email: "123@qq.com", password: "", avatar: None, verify_code: None, receive_address: [] }

Rust实现(引用修改)建造者模式

如果不想消耗对象,可以将其字段都设置为&mut,使用clone()是为了返回的新对象是完全独立的副本

// 注意使用了Default,没有builder的值有默认值
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Customer {
    // uuid
    user_id: String,
    // 用户名
    username: String,
    // 邮件
    email: String,
    // 密码
    password: String,
    // 头像
    avatar: Option<String>,
    // 验证码
    verify_code: Option<String>,
    // 收货地址
    receive_address: Vec<ReceiveAddress>,
}
// 建造(者结构体,包含一个需要构建的对象
#[derive(Default, Clone, Debug)]
pub struct CustomerBuilder {
    customer: Customer,
}
impl CustomerBuilder {
    pub fn new() -> Self {
        CustomerBuilder::default()
    }
    pub fn user_id(&mut self, user_id: String) -> &mut Self {
        self.customer.user_id = user_id;
        self
    }

    pub fn username(&mut self, username: String) -> &mut Self {
        self.customer.username = username;
        self
    }

    pub fn email(&mut self, email: String) -> &mut Self {
        self.customer.email = email;
        self
    }

    pub fn password(&mut self, password: String) -> &mut Self {
        self.customer.password = password;
        self
    }

    pub fn avatar(&mut self, avatar: Option<String>) -> &mut Self {
        self.customer.avatar = avatar;
        self
    }

    pub fn verify_code(&mut self, verify_code: Option<String>) -> &mut Self {
        self.customer.verify_code = verify_code;
        self
    }

    pub fn receive_address(&mut self, receive_address: Vec<ReceiveAddress>) -> &mut Self {
        self.customer.receive_address = receive_address;
        self
    }
    pub fn build(&self) -> Customer {
        Customer {
            user_id: self.customer.user_id.clone(),
            username: self.customer.username.clone(),
            email: self.customer.email.clone(),
            password: self.customer.password.clone(),
            avatar: self.customer.avatar.clone(),
            verify_code: self.customer.verify_code.clone(),
            receive_address: self.customer.receive_address.clone(),
        }
    }
}

使用,这里对象创建后不会消耗对象,可以通过.build()修改并返回新对象,适合创建领域模型如聚合对象

let mut binding = CustomerBuilder::new().clone();
let customer = binding
    .user_id("123".to_string())
    .username("张三".to_string())
    .email("<EMAIL>".to_string());
let customer = customer.avatar(Some("https://www.baidu.com".to_string()));
let customer = customer.build();
print!("{:?}\n", customer);
//Customer { user_id: "123", username: "张三", email: "<EMAIL>", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }
// 修改原有对象
let customer = binding.email("123@qq.com".to_string()).build();
println!("{:?}", customer);
//Customer { user_id: "123", username: "张三", email: "123@qq.com", password: "", avatar: Some("https://www.baidu.com"), verify_code: None, receive_address: [] }