设计模式-建造者模式

建造者模式

1.定义

将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示

2.使用场景

当一个对象的创建过程很复杂,类的构造函数参数个数比较多,而且这些参数有些是可选的参数,考虑使用构造者模式。
举例:例如我们现在有如下一个类计算机类Computer,其中cpu与ram是必填参数,而其他3个是可选参数,那么我们如何构造这个类的实例呢,通常有两种常用的方式:

public class Computer {
    private String cpu;//必须
    private String ram;//必须
    private int usbCount;//可选
    private String keyboard;//可选
    private String display;//可选
    
    public Computer(String cpu, String ram) {
        this(cpu, ram, 0);
    }
    public Computer(String cpu, String ram, int usbCount) {
        this(cpu, ram, usbCount, "罗技键盘");
    }
    public Computer(String cpu, String ram, int usbCount, String keyboard) {
        this(cpu, ram, usbCount, keyboard, "三星显示器");
    }
    public Computer(String cpu, String ram, int usbCount, String keyboard, String display) {
        this.cpu = cpu;
        this.ram = ram;
        this.usbCount = usbCount;
        this.keyboard = keyboard;
        this.display = display;
    }
}

第二种:Javabean 模式,就是通过set方式赋值

public class Computer {
        ...

    public String getCpu() {
        return cpu;
    }
    public void setCpu(String cpu) {
        this.cpu = cpu;
    }
    public String getRam() {
        return ram;
    }
    public void setRam(String ram) {
        this.ram = ram;
    }
    public int getUsbCount() {
        return usbCount;
    }
...
}

那么这两种方式有什么弊端呢? 第一种主要是使用及阅读不方便。你可以想象一下,当你要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易就传混了 ,
第二种方式在构建过程中对象的状态容易发生变化,造成错误。因为那个类中的属性是分步设置的,所以就容易出错。为了解决这两个痛点,builder模式就横空出世了。

3.如何实现建造者模式
  1. 创建一个产品类,这个类是建造者模式中,要返回的产品对象的类
package com.gupaoedu.vip.pattern.builder;

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Computer {

    private String cpu;//必须
    private String ram;//必须
    private int usbCount;//可选
    private String keyboard;//可选
    private String display;//可选

    // 原来的写法:通过重载构造方法,通过不同的参数组合,获取不同的对象
    /*public Computer(String cpu, String ram) {
        this(cpu, ram, 0);
    }
    public Computer(String cpu, String ram, int usbCount) {
        this(cpu, ram, usbCount, "罗技键盘");
    }
    public Computer(String cpu, String ram, int usbCount, String keyboard) {
        this(cpu, ram, usbCount, keyboard, "三星显示器");
    }
    public Computer(String cpu, String ram, int usbCount, String keyboard, String display) {
        this.cpu = cpu;
        this.ram = ram;
        this.usbCount = usbCount;
        this.keyboard = keyboard;
        this.display = display;
    }*/
}

  1. 创建一个抽象的建造者接口(简写模式下,可以不用定义该接口)
package com.gupaoedu.vip.pattern.builder;

public interface IComputerBuilder {
     Computer build(String cpu,String ram) ;
}
  1. 建造者:持有要构建的对象

在建造者模式中,为产品属性赋值,原本的返回值类似是void,但是我们把返回值类型改为建造者本身,通过各个set方法,为产品对象的属性分别 赋值, 在赋值之后,返回建造者这个类自己(这样写的好处就是调用者使用时可以进行链式调用,非常方便),然后在build方法中,返回最终创建好的对象给外部调用者。

package com.gupaoedu.vip.pattern.builder;

/**
 * 建造者
 */
public class ComputerBuilder  implements IComputerBuilder{

    // 要构建的对象
    private Computer computer = new Computer();

    // build方法返回构建好的完整的对象
    @Override
    public Computer build(String cpu,String ram) {
        computer.setCpu(cpu);
        computer.setRam(ram);
        return computer;
    }

    public ComputerBuilder setDisplay(String display) {
        computer.setDisplay(display);
        return this;
    }

    public ComputerBuilder setKeyboard(String keyboard) {
        computer.setKeyboard(keyboard);
        return this;
    }

    public ComputerBuilder setUsbCount(int usbCount) {
        computer.setUsbCount(usbCount);
        return this;
    }
}

  1. Director: 自由构建对象
package com.gupaoedu.vip.pattern.builder;

public class Director {
    public static void main(String[] args) {
        ComputerBuilder computerBuilder = new ComputerBuilder();
        //1.每次都是由builder去操作这个对象,对使用者来讲,对象的创建过程被封装了起来
        //2.对于可选参数,用户可以设置,也可以不设置,先设置哪个,后设置哪个,都不影响对象的构建,使用者可以自由定制
        // 缺点:产生多余的builder对象;如果产品内部发生变化,建造者都要修改
        computerBuilder.setDisplay("27寸显示器")
                .setKeyboard("罗技键盘")
                .setUsbCount(3);
        Computer computer = computerBuilder.build("英特尔cpu", "三星ram");
        System.out.println(computer);

        StringBuilder stringBuilder = new StringBuilder();

    }
}

测试:
在这里插入图片描述
假如我现在不想要键盘,那么直接使用 computerBuilder.setDisplay(“27寸显示器”).setUsbCount(3); 即可,是不是很方便!
再也不用考虑蛋疼的属性排列组合问题了!

4. 建造者模式的优点
  • 封装性好,对象的创建和使用分离
  • 扩展性好,建造类之间独立,一定 程度上解耦
5.建造者模式的缺点
  • 原本只需要构造方法就完成了对象的创建,使用建造者模式之后,额外产生了builder对象
  • 产品内部一旦发生变化,建造者内部也得跟着修改,维护不方便,不符合开闭原则
6.建造者模式与工厂模式区别
  • 建造者模式更加注重方法的调用顺序,工厂模式注重于创建对象(不同工厂创建不同的对象 )
  • 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样
  • 关注点不同:工厂模式只要把对象创建出来就可以了,建造者模式不仅要创建出对象,还要知道这个对象由哪些部件组成
  • 建造者模式根据建造过程中的顺序不同,最终的对象部件组成也不同。
7. 建造者模式的实际应用场景

Mybatis Generator相信很多人都用过,如下代码,获取一个example,然后调用example.createCriteria() 创建一个Criteria 对象,然后设置查询条件:

public EUDataGridResult getContentList(Integer page, Integer rows, Long categoryId) {

		TbContentExample example = new TbContentExample();
		// 设置分页条件,必须写在设置查询条件之前
		PageHelper.startPage(page, rows);
		Criteria criteria = example.createCriteria();
		criteria.andCategoryIdEqualTo(categoryId);
		List<TbContent> contentList = mapper.selectByExample(example);

		// 封装返回对象
		EUDataGridResult result = new EUDataGridResult();
		result.setRows(contentList);

		// 通过pagehelper jar包的PageInfo类 获取记录总条数
		result.setTotal(new PageInfo<>(contentList).getTotal());
		return result;
	}

如上,这里的查询条件是不确定的,那么Mybatis Generator生成出来的代码里面对于Criteria 设置查询条件是怎么写的呢?

  public Criteria andCategoryIdEqualTo(Long value) {
            addCriterion("category_id =", value, "categoryId");
            return (Criteria) this;
        }

        public Criteria andCategoryIdNotEqualTo(Long value) {
            addCriterion("category_id <>", value, "categoryId");
            return (Criteria) this;
        }

        public Criteria andCategoryIdGreaterThan(Long value) {
            addCriterion("category_id >", value, "categoryId");
            return (Criteria) this;
        }

可以看到,这里andCategoryIdEqualTo方法返回的是Criteria 对象,这个Criteria 对象其实就是一个建造者模式的一种应用

参考: 秒懂设计模式之建造者模式(Builder pattern)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值