设计模式——生成器模式

本贴最后更新于 462 天前,其中的信息可能已经时移世易

一、前言

今天我们来学习一下生成器模式,生成器模式又被称为建造者模式,它属于设计模式三大类型中的创建型模式,通常用于系统中一些复杂的对象场景中,接下来,就让我们对它来一次深入的了解。

二、生成器模式详解

1、为什么需要生成器模式?

在我们的开发中,我们总会遇见一些复杂的对象,而这些复杂的对象,它们通常由多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车!

同理,我们在开发中也是只需要使用这些复杂的对象,而不是关心它创建的细节,这时就可以通过生成器模式对其进行设计与描述,生成器模式会将部件和其组装过程分开,一步一步创建一个复杂的对象。

我们只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。

2、定义

生成器模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

换成大白话理解就是:一个复杂的对象,它的创建过程和使用过程要分开。对于对象的使用者来说,我只需要告诉创建者我需要使用这个复杂对象,至于这个复杂对象是怎么创建的,不关我事 (ps:有点渣男的味道)

3、适用场景

在以下情况考虑使用生成器模式:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时
  • 当构造过程必须允许被构造的对象有不同的表示时
  • 一个对象有非常复杂的内部结构(很多属性)
  • 想把复杂对象的创建和使用分离

4、UML 图

01 生成器模式.jpg

5、实际案例

良好的代码设计不仅可以让代码重复性更高,还能使代码更易读从而降低代码后期的维护成本,最重要的是可以提高系统的可靠性。

因此今天我们举一个实际开发中通过生成器模式针对解决复杂对象的一个应用场景。

具体需求

  • 根据用户近期的消费金额、消费次数、浏览商品类型、商品价格区间等一些属性,生成用户画像。根据画像分析用户行为,实现精准营销或刺激消费等。
  • 不同的业务关注的角度不同。比如精准营销业务关注的是用户近半年的数据,而且以消费数据为主;刺激消费业务关注的是用户近一个月的数据,而且以常打开的商品为主。

从编程角度把需求提炼一下,大概就是以下两点:

  1. 提供一个用户对象,这个对象包括用户名、消费金额、消费次数、浏览商品类型、商品价格区间等属性
  2. 根据这个对象进行一些业务处理

我们先来看看硬编码请求:

02 生成器模式.jpg

伪代码是在精准营销和刺激消费的业务逻辑里面,分别创建了一个 User 对象。两个业务中创建 User 对象的逻辑基本一样,只有在获取近期消费数据时稍有差别。一个是获取近半年的数据,另一个是获取近一个月的数据。这样的硬编码是把 User 对象的创建过程,嵌入到了其他业务逻辑里面,这就造成一些问题:

  1. 代码重用性降低

    User 对象的创建逻辑基本一样,但是写了两遍。如果后期加入新的业务,User 对象的创建逻辑还要再写一遍,代码重用性太低

  2. 维护成本增大

    示例中的伪代码模拟的比较简单,实际上 User 对象的创建过程非常复杂,需要查询各种数据并且对数据进行过滤、分类、整理,代码可能有几百行

    精准营销或刺激消费的业务逻辑也是非常复杂的,把两块复杂的逻辑写到一块,后期阅读或维护代码的成本将几何倍的增长

  3. 代码耦合度增加

    将两块业务逻辑写到一起,其中不免会共享一些逻辑,如果后期想对共享的逻辑进行修改,让其仅对其中一方生效,代码的修改是很不友好的,很容易造成另一方的逻辑漏洞

生成器模式

代码实现:

User

public class User {
    private String nickname;
    private int payCnt;
    private int payAmt;
    private List<String> productType;
    private List<String> amtInterval;

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public void setPayCnt(int payCnt) {
        this.payCnt = payCnt;
    }

    public void setPayAmt(int payAmt) {
        this.payAmt = payAmt;
    }

    public void setProductType(List<String> productType) {
        this.productType = productType;
    }

    public void setAmtInterval(List<String> amtInterval) {
        this.amtInterval = amtInterval;
    }

    public String getNickname() {
        return nickname;
    }

    public int getPayCnt() {
        return payCnt;
    }

    public int getPayAmt() {
        return payAmt;
    }

    public List<String> getProductType() {
        return productType;
    }

    public List<String> getAmtInterval() {
        return amtInterval;
    }

    @Override
    public String toString() {
        return "User{" +
                "nickname='" + nickname + '\'' +
                ", payCnt=" + payCnt +
                ", payAmt=" + payAmt +
                ", productType=" + productType +
                ", amtInterval=" + amtInterval +
                '}';
    }
}

User 建造者

public abstract class IUserBuilder {
    // 构建用户昵称
    public abstract void buildNicaname(String nickName);
    // 构建用户消费次数,days代表最近天数
    public abstract void buildPayCnt(int payCnt);
    // 构建用户消费金额,days代表最近天数
    public abstract void buildPayAmt(int payAmt);
    // 构建用户经常浏览商品类型
    public abstract void buildProductType(List productType);
    // 构建用户经常浏览商品价格区间
    public abstract void buildAmtInterval(List amtInterval);
    // 获取user对象
    public abstract User getUser();
}

User 真正建造者

public class UserBuilder extends IUserBuilder {

    private User user = new User();

    @Override
    public void buildNicaname(String nickName) {
        user.setNickname(nickName);
    }
    @Override
    public void buildPayCnt(int payCnt) {
        user.setPayCnt(payCnt);
    }
    @Override
    public void buildPayAmt(int payAmt) {
        user.setPayAmt(payAmt);
    }
    @Override
    public void buildProductType(List productType) {
        user.setProductType(productType);
    }
    @Override
    public void buildAmtInterval(List amtInterval) {
        user.setAmtInterval(amtInterval);
    }
    @Override
    public User getUser() {
        return user;
    }
}

Director

public class Director {

    private UserBuilder userBuilder;

    public void setUserBuilder(UserBuilder userBuilder){
        this.userBuilder = userBuilder;
    }

    public User makeUser(String nickName, int payCnt, int payAmt, List productType,List amtInterval){

        this.userBuilder.buildNicaname(nickName);
        this.userBuilder.buildPayCnt(payCnt);
        this.userBuilder.buildPayAmt(payAmt);
        this.userBuilder.buildProductType(productType);
        this.userBuilder.buildAmtInterval(amtInterval);

        return this.userBuilder.getUser();
    }
}

客户端

public class Test {

    public static void main(String[] args) {

        UserBuilder userBuilder = new UserBuilder();
        Director director = new Director();
        director.setUserBuilder(userBuilder);

        List<String> productTypelist = new ArrayList<String>();
        productTypelist.add("增发剂");
        productTypelist.add("格子衫");

        List<String> amtIntervallist = new ArrayList<String>();
        amtIntervallist.add("1-9");
        amtIntervallist.add("2-10");

        User user = director.makeUser("张三",30,1000,productTypelist,amtIntervallist);

        System.out.println(user);

    }
}

结果

03 生成器模式.jpg

最后我们可以看到对于精准营销或刺激消费的业务逻辑来说,它们不用再关心 User 对象的创建过程,可以更专注于自身的业务逻辑,无论是代码阅读或后期维护都更方便。

三、总结

以上就是我个人关于 设计模式——生成器模式 的一些笔记如果有什么问题,可以将问题发我邮箱 luodiab@126.com,欢迎各位的意见。

四、参考文章

设计模式(三):生成器模式

Java 的生成器模式(又名建造者模式),你真的会了吗(上)

  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖
  • 笔记

    好记性不如烂笔头。

    308 引用 • 793 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...