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


    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");
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    这种方式写起来不够优雅,创建不同型号电脑时,需要创建不同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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    定义抽象建造者:

    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在定义具体的建造者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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

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

    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);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    测试代码如下:

    @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));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

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

    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("普通房屋建造完成,请客吃饭");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    测试类如下:

    public class Test {
        public static void main(String[] args) {
            HighBuilding highBuilding = new HighBuilding();
            highBuilding.build();
            System.out.println("--------------------------------------");
            CommonBuilding commonBuilding = new CommonBuilding();
            commonBuilding.build();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    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-尚硅谷设计模式

  • 相关阅读:
    Rockland丨GOAT TRUEBLOT抗山羊IGG HRP说明书
    4.1.5-检查网页内容是否存在信息泄露
    C++进程间通信 匿名管道和命名管道
    Java下打印一个等腰三角型
    【C++STL基础入门】list改、查操作
    Vue笔记
    Linux openvino 环境搭建遇见的问题
    淘宝/天猫api 添加收货地址 API接口
    【榜单公布】10·24征文活动结果出炉!
    zookeeper + kafka
  • 原文地址:https://blog.csdn.net/qq_33479841/article/details/127631515