设计模式:(Builder Pattern)生成器模式

318 阅读2分钟

设计模式的重要性毋需再言。本文将讲述,如何用TypeScript实现生成器模式

欢迎来到 TypeScript 中的设计模式系列,它介绍了使用TypeScript进行Web开发的一些有用的设计模式。

系列文章如下:

生成器模式 能将一个复杂的对象分解成相对简单的部分,然后根据不同的需要分别创建,最终构建出复杂的对象。

例如:

class User {
    constructor(
        username:string,
        sex:string,
        age:number,
        photo:string,
        email:string
    ){}
}

上面的代码中,我们定义了一个User类,通过此类,可以创建User实例。

const user = new User("kevin", "male", "35", "https://qq.com", "kevin@qq.com")

虽然我们可以成功的创建一个User实例,但注意到初始化User时,需要进行复杂的初始化工作,如需要一次性传入足够多的参数来构造实例。

那有没有更好的方法呢?解决方案是使用生成器设计模式。

如:

class UserBuilder {
  username: string;
  sex: string;
  age: number;
  photo: string;
  email: string;

  setUserName(name: string) {
    this.username = name;
    return this;
  }

  setSex(sex: string) {
    this.sex = sex;
    return this;
  }

  setAge(age: number) {
    this.age = age;
    return this;
  }

  setPhoto(photo: string) {
    this.photo = photo;
    return this;
  }

  setEmail(email: string) {
    this.email = email;
    return this;
  }

  build() {
    return new User(this.username, this.sex, this.age, this.photo, this.email);
  }
}

在 UserBuilder 中,我们定义了几个 setXXX方法和一个build方法。该setXXX方法用于设置实例属性的值,而build方法用于实例化User类

此时创建一个user类的实例变成如下:

const user = new UserBuilder()
  .setUserName('kevin')
  .setSex('male')
  .setAge(35)
  .setPhoto('https://qq.com')
  .setEmail('kevin@qq.com');

看完上面的例子,你会发现builder模式并不复杂。在实际的 TypeScript 项目中,我们可以使用builder-pattern库来有效地使用 builder 模式。

基本用法

interface UserInfo {
    id:number
    userName: string;
    email: string;
}
   
const userInfo = Builder<UserInfo>()
                   .id(1)
                   .userName('foo')
                   .email('foo@bar.baz')
                   .build();

与模板对象使用

const defaultUserInfo: UserInfo = {
  id: 1,
  userName: 'foo',
  email: 'foo@bar.baz'
};

const modifiedUserInfo = Builder(defaultUserInfo)
                          .id(2)
                          .build();

与类对象使用

class UserInfo {
  id!: number;
  userName!: string;
  email!: string;
}

const userInfo = Builder(UserInfo)  // note that ( ) is used instead of < > here
                   .id(1)
                   .userName('foo')
                   .email('foo@bar.baz')
                   .build();

在数据查询的场景中,我们经常会看到生成器模式。比如构造sql或者elasticsearch查询条件,这里我们以一个bodybuilder(An elasticsearch query body builder)库为例来看看它的基本用法

bodybuilder() 
  .query('match', 'message', 'this is a test') 
  .filter('term', 'user', 'kimchy') 
  .filter('term', 'user', 'herald' ) 
  .orFilter('term', 'user', 'johnny') 
  .notFilter('term', 'user', 'cassie') 
  .aggregation('terms', 'user') 
  .build()

最后总结一下builder模式的使用场景:

  • 当一个类有超过 4 个构造函数参数,并且其中一些参数是可选的,考虑使用生成器模式。

参考