小侃设计模式(五)-建造者模式与模板方法模式

1.概述

建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式,它可以将复杂对象的建造过程抽象出来(抽象类别),这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。建造者是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。模板方法模式是指在一个抽象类中公开定义了执行它的方法的方式/模板,它的子类可以按需要重写方法实现,这是一种行为型模式。这两个模式相似之处在于都需要抽象出共性行为,由子类去实现。本文将详述建造者模式和模板方法模式的概念及使用。

2.建造者模式与模板方法模式

2.1建造者模式

2.1.1 案例

比如我们要组装一台电脑,需要组装的部分包括CPU、主板、屏幕、内存、磁盘、键盘、鼠标等,则会写出如下代码:

public class Computer {

    private String cpu;

    private String memory;

    private String disk;

    private String screen;

    private String mouse;

    private String mainBoard;

    private String keyBoard;

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getMemory() {
        return memory;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    public String getDisk() {
        return disk;
    }

    public void setDisk(String disk) {
        this.disk = disk;
    }

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getMouse() {
        return mouse;
    }

    public void setMouse(String mouse) {
        this.mouse = mouse;
    }

    public String getMainBoard() {
        return mainBoard;
    }

    public void setMainBoard(String mainBoard) {
        this.mainBoard = mainBoard;
    }

    public String getKeyBoard() {
        return keyBoard;
    }

    public void setKeyBoard(String keyBoard) {
        this.keyBoard = keyBoard;
    }
}

//创建对象方式如下:
Computer computer = new Computer();
computer.setCpu("M2");
computer.setScreen("mac screen");
......

这种方式写起来不够优雅,创建不同型号电脑时,需要创建不同Computer对象并挨个设置属性,编写了大量冗余代码,造成系统臃肿。
如果利用建造者模式来生成对象,则需要包含四个角色:Product(产品角色)、Builder(抽象建造者)、ConcreteBuilder(具体建造者)、Director(指挥者)。

1.Product(产品角色):一个具体的产品对象;
2.Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类。
3.ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件;
4.Director(指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它的作用主要有两个,一是隔离客户与对象的生产过程,二是负责控制产品对象的生产过程。

利用建造者模式,定义产品角色:

public class Computer {

    private String cpu;

    private String memory;

    private String disk;

    private String screen;

    private String mouse;

    private String mainBoard;

    private String keyBoard;

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getMemory() {
        return memory;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    public String getDisk() {
        return disk;
    }

    public void setDisk(String disk) {
        this.disk = disk;
    }

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getMouse() {
        return mouse;
    }

    public void setMouse(String mouse) {
        this.mouse = mouse;
    }

    public String getMainBoard() {
        return mainBoard;
    }

    public void setMainBoard(String mainBoard) {
        this.mainBoard = mainBoard;
    }

    public String getKeyBoard() {
        return keyBoard;
    }

    public void setKeyBoard(String keyBoard) {
        this.keyBoard = keyBoard;
    }
}

定义抽象建造者:

public abstract class Builder {

    abstract Builder buildScreen(String screen);

    abstract Builder buildMouse(String mouse);

    abstract Builder buildCpu(String cpu);

    abstract Builder buildDisk(String disk);

    abstract Builder buildMemory(String memory);

    abstract Builder buildMainBoard(String mainBoard);

    abstract Builder buildKeyBoard(String keyBoard);

    abstract Computer build();
}

在定义具体的建造者MacBuilder,组装Mac电脑,LenovoBuilder(组装lenov电脑)、ASUSBuilder(组装华硕电脑)等,代码如下:

public class MacBuilder extends Builder {

    private Computer computer = new Computer();

    @Override
    Builder buildScreen(String screen) {
        computer.setScreen(screen);
        System.out.println("组装屏幕" + screen + "成功");
        return this;
    }

    @Override
    Builder buildMouse(String mouse) {
        computer.setMouse(mouse);
        System.out.println("组装鼠标" + mouse + "成功");
        return this;
    }

    @Override
    Builder buildCpu(String cpu) {
        computer.setCpu(cpu);
        System.out.println("组装cpu" + cpu + "成功");
        return this;
    }

    @Override
    Builder buildDisk(String disk) {
        computer.setDisk(disk);
        System.out.println("组装disk" + disk + "成功");
        return this;
    }

    @Override
    Builder buildMemory(String memory) {
        computer.setMemory(memory);
        System.out.println("组装memory" + memory + "成功");
        return this;
    }

    @Override
    Builder buildMainBoard(String mainBoard) {
        computer.setMainBoard(mainBoard);
        System.out.println("组装mainBoard" + mainBoard + "成功");
        return this;
    }

    @Override
    Builder buildKeyBoard(String keyBoard) {
        computer.setKeyBoard(keyBoard);
        System.out.println("组装keyBoard" + keyBoard + "成功");
        return this;
    }

    @Override
    Computer build() {
        System.out.println("电脑组装完成!");
        return computer;
    }
}

具体建造者完成,定义指挥者负责指挥建造:

public class Director {
    Builder builder = null;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void processMain(String cpu, String disk, String memory, String mainBoard) {
        builder.buildMemory(memory)
                .buildMainBoard(mainBoard)
                .buildCpu(cpu).buildDisk(disk);
    }

    public void processOther(String screen, String mouse, String keyBoard) {
        builder.buildScreen(screen).buildKeyBoard(keyBoard).buildMouse(mouse);
    }

}

测试代码如下:

@Slf4j
public class Test {
    public static void main(String[] args) {
        MacBuilder macBuilder = new MacBuilder();
        Director director = new Director(macBuilder);
        director.processMain("M2", "mac", "mac memory", "mac mainBoard");
        Computer build = macBuilder.build();
        log.info("build main:" + JSON.toJSONString(build));

        director.processOther("mac Screen", "mac Mouse", "mac keyBoard");
        Computer build1 = macBuilder.build();
        log.info("build other:" + JSON.toJSONString(build));
    }
}

测试结果如下:
在这里插入图片描述
上述两种方案的对比,建造者模式将对象的创建过程进行了解耦,如果对象属性多,构建发杂,建造者明显可以减少冗余代码的编写。对象的整个创建工作交给了构造器,符合单一原则。类图关系如下:
在这里插入图片描述

2.1.3 建造者模式的注意事项和细节

1.客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象;
2.每一个具体建造者都相对独立,而与其它的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同地具体建造者即可得到不同地产品对象;
3.可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程;
4.增加新的具体建造者无须修改原有类库的代码,指挥者针对抽象建造者类编程,系统扩展方便,符合“开闭原则”;
5.建造者模式所创建的产品一般具有较多共同点,其组成部分相似,如果产品之间差异性较大,则不适合使用建造者模式,因此其使用范围受到一定的限制;
6.如果产品的内部变化复杂,可能会导致需要定义较多具体建造者来实现这种变化,这会造成系统代码臃肿,此时需要考虑是否选择建造者模式。

2.1.4 抽象工厂模式和建造者模式的区别

抽象工厂模式实现对产品家族的创建,一个产品家族就是一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关系构建过程,只关心哪类产品由哪个工厂生产即可。建造者模式是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而生成产品。

2.2 模板方法模式

2.2.1 定义

模板方法模式(Template Method Pattern),又叫模板模式(Template
Pattern),在一个抽象类公开定义了执行它的方法的模板,它的子类可以按需要重写方法实现,调用以抽象类中定义的方式进行。简而言之,模板方法模式定义了一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重新定义该算法的某些特定步骤。

2.2.2 案例

举一个建房子的案例,建房子的流程是固定的:设计图纸、打地基、砌墙、结顶。可以定义一个模板方法来实现,如下:

public abstract class BuildHouseTemplate {

    //钩子函数,预留前置可能执行的一些操作
    void before() {
        System.out.println("开始设计之前需要做的事情,比如准备资金,寻找场地");
    }

    //设计
    void design() {
        System.out.println("找设计师设计图纸");
    }

    //抽象方法,打地基,子类实现打地基的材料、方式
    abstract void buildBase();

    //抽象方法,砌墙
    abstract void buildWall();

    //结顶操作
    void roofed() {
        System.out.println("结顶盖瓦");
    }

    //钩子函数,预留后续可能执行的一些操作
    void after() {
        System.out.println("房屋结束之后要做的事情,比如放鞭炮庆祝,请吃席。。。");
    }

    //整体流程确定,不可更改,细节由具体子类根据场景实现
    final void build() {
        before();
        design();
        buildBase();
        buildWall();
        roofed();
        after();
    }
}

建造房屋有多种情形,这里分为建造高楼和普通房子,具体实现如下:

public class HighBuilding extends BuildHouseTemplate {

    @Override
    void before() {
        System.out.println("建造高楼之前需要测量土地性质及地形,周边房屋、电缆情况,确定是否具有影响");
    }

    @Override
    void buildBase() {
        System.out.println("用钢筋混凝土打地基,还有加石头,地基要打深一定");
    }

    @Override
    void buildWall() {
        System.out.println("用玻璃来砌墙,采光好");
    }

    @Override
    void after() {
        System.out.println("高楼建造完成,需要放炮庆祝");
    }
}



public class CommonBuilding extends BuildHouseTemplate{

    @Override
    void before() {
        System.out.println("建造普通房屋,需要先准备资金和邀请人");
    }

    @Override
    void buildBase() {
        System.out.println("普通房子打地基,可以不用很深...");
    }

    @Override
    void buildWall() {
        System.out.println("用多孔砖砌墙,保温还减重...");
    }

    @Override
    void after() {
        System.out.println("普通房屋建造完成,请客吃饭");
    }
}

测试类如下:

public class Test {
    public static void main(String[] args) {
        HighBuilding highBuilding = new HighBuilding();
        highBuilding.build();
        System.out.println("--------------------------------------");
        CommonBuilding commonBuilding = new CommonBuilding();
        commonBuilding.build();
    }
}

测试结果如下:
在这里插入图片描述
模板方法一般类图(即案例类图)如下:
在这里插入图片描述

2.2.3 注意事项

1.钩子方法一般默认不做任何事情,子类可以视情况决定是否覆盖;
2.模板方法一般添加final关键字进行修饰,保证子类中该方法不会被修改;
3.模板方法只存在于父类方法中,容易修改,一旦设计修改算法时,只需要修改父类即可;
4.实现了代码的可复用性,父类的一些方法可以直接被子类继承使用;
5.统一了算法,但也提供了灵活性,模板方法保证了算法结构的不变性;
6.模板方法的缺陷在于每个子类都要去实现父类,一定程度上会增加系统中类的个数。

3.小结

1.建造者模式主要用来创建差异不大的对象;
2.模板方法模式则定义完整的实现/流程,不同子类根据情况进行改写具体实现,但算法实施步骤只能存在父类中,且不能被子类修改。

4.参考文献

1.《设计模式-可复用面向对象软件的基础》-Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides
2.《可复用物联网Web3D框架的设计与实现》-程亮(知网)
3.https://www.bilibili.com/video/BV1G4411c7N4-尚硅谷设计模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值