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

news2025/7/7 5:06:52

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/4063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

家庭主妇问题

一 问题描述 X 村的人们住在美丽的小屋里。若两个小屋通过双向道路连接,则可以说这两个小屋直接相连。X 村非常特别,可以从任意小屋到达任意其他小屋,每两个小屋之间的路线都是唯一的。温迪的孩子喜欢去找其他孩子玩,然后打电话给…

C++中TCP socket传输文件

在两个文件中都定义文件头和用到的宏&#xff1a; #define MAX_SIZE 10 #define ONE_PAGE 4096 struct FileHead {char str[260];int size; }; 在客户端发送接收阶段&#xff1a; //1.发送文件头char path[260] {0};cout<<"请输入文件路径"<<endl;cin…

数字图像处理MATLAB

数字图像处理MATLAB 基&#xff08;本&#xff09;操&#xff08;作&#xff09; 图片读取 Aimread(test.bmp); imshow(A);2. 图像写入 Aimread(test.bmp); imwrite(A,test-bak.bmp); Bimread(test-bak.bmp); imshow(B);3. 图像文件信息查询 infoimfinfo(test.bmp);4. 显示…

【创建型】生成器模式(Builder)

目录生成器模式(Builder)适用场景生成器模式实例代码&#xff08;Java&#xff09;生成器模式(Builder) 将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 适用场景 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方…

【SpringBoot笔记22】SpringBoot框架集成Redis数据库

这篇文章&#xff0c;主要介绍SpringBoot框架如何集成Redis数据库。 目录 一、SpringBoot集成Redis 1.1、引入依赖 1.2、配置redis连接信息 1.3、添加RedisTemplate配置类 1.4、编写测试类 1.5、运行测试 一、SpringBoot集成Redis Redis是一个非关系型数据库&#xff0c…

PCIe ECAM机制访问PCIE的配置空间

1.PCIe ECAM机制 PCI Express Enhanced Configuration Access Mechanism (ECAM)是访问PCIe配置空间的一种机制。是将PCIe的配置空间映射到MEM空间&#xff0c;使用MEM访问其配置空间的一种实现。可参考NCB-PCI_Express_Base_5.0r1.0-2019-05-22.pdf的第7.2.2小节。 其地址映射…

上海亚商投顾:沪指录得6连阳 两市成交再度破万亿

上海亚商投顾前言&#xff1a;无惧大盘大跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 三大指数今日横盘震荡&#xff0c;收盘集体小幅上扬&#xff0c;日K线均录得6连阳。虚拟现实概念股集体拉升&#…

【SA-Token】授权 鉴权中心微服务

授权 鉴权中心微服务 1 什么是JWT 1.2 JWT 的基本概念 1.3 JSON Web Token jwt 是一个开放标准 它定义了一种紧凑的、自包含的方式 用于作为JSON 对象在各方之间安全地传输信息 1.4.那些场景下可以考虑使用JWT &#xff1f; ​ 1.用户授权 信息交换 1.5 JWT的结构及其含义 …

镜频抑制滤波器对射频接收前端输出噪声的影响

射频接收前端包括LNA、Filter、Mixer等部件&#xff0c;从噪声因子级联的角度讲&#xff0c;希望接收链路第一级为高增益、低噪声系数放大器&#xff0c;以期望得到较低的系统噪声系数&#xff0c;提高接收灵敏度。除LNA外&#xff0c;接收链路还有一个关键的部件——镜频抑制滤…

精读大型网站架构:前端架构模块化的方法及困境,自研框架Trick

模块化的方法 网页和网页之间有很多相似或者相同的模块&#xff0c;模块化就是把这些模块抽离并独立管理。而模块化的方法&#xff0c;就是把模块的HTML、CSS和JavaScript文件独立出来&#xff0c;然后通过某种方法关联到使用这些模块的网页上。 在介绍模块化的具体方法之前&…

consul--基础--05--api

consul–基础–05–api 1、介绍 主要接口是RESTful HTTP API&#xff0c;该API可以用来增删查改nodes、services、checks、configguration。所有的endpoints主要分为以下类别 kv&#xff1a;Key/Value存储agent&#xff1a;Agent控制catalog&#xff1a;管理nodes和serviceshe…

数据结构-例题实训作业-二叉树相关

第1关:以先序的方式建立二叉树 任务描述 本关任务:以先序的方式建立二叉树并显示(顺时针90度后看) 相关知识 为了完成本关任务,你需要掌握: 1.二叉树的概念 2.二叉树的先序遍历方式 3.二叉树的遍历。 编程要求 在以下空白处补写代码,以先序方式完成二叉树的建立。 //…

计算机组成原理浮点数表示

浮点数表示 浮点数的表示分为阶码和尾数&#xff1b; 比如3.026*1011;阶码是11;尾数是3.026&#xff1b; 对于阶码&#xff1a; 阶符为正&#xff0c;小数点向后移n位&#xff08;n表示阶的大小&#xff09;; 阶符为负&#xff0c;小数点向前移n位&#xff08;n表示阶的大小&a…

基础IO(上)——Linux

文章目录1.储备知识2. 文件描述符2.1 c接口2.2 直接使用系统接口2.3 open函数返回值2.4 文件描述符fd2.5 周边文件3. 重定向3.1 输出重定向3.2 输出重定向3.3 追加重定向3.4 dup4. 如何理解一切皆文件&#xff1f;1.储备知识 对文件的操作范畴&#xff1a; 在系统角度理解文件 …

R语言生物群落数据统计分析

R 语言作的开源、自由、免费等特点使其广泛应用于生物群落数据统计分析。生物群落数据多样而复杂&#xff0c;涉及众多统计分析方法。本教学以生物群落数据分析中的最常用的统计方法回归和混合效应模型、多元统计分析技术及结构方程等数量分析方法为主线&#xff0c;通过多个来…

中医-通过舌象判断身体状况

本文分享通过舌象判断身体的整体状况&#xff08;中医角度&#xff09;&#xff0c;得出一个可供辨证的参考&#xff0c;并且可以根据舌象做出相关的饮食调整&#xff0c;本文主讲理论&#xff0c;相关舌象图片易引人不适&#xff0c;如需找相关图片&#xff0c;可根据本文中的…

【SpringBoot】一文了解SpringBoot配置高级

文章目录前言ConfigurationProperties使用场景小结宽松绑定/松散绑定&#x1f315;博客x主页&#xff1a;己不由心王道长&#x1f315;! &#x1f30e;文章说明&#xff1a;SpringBoot配置高级&#x1f30e; ✅系列专栏&#xff1a;SpringBoot &#x1f334;本篇内容&#xff1…

javaweb JavaScript快速入门 对象 BOM DOM 事件监听

JavaScript 引入方式 1.内部脚本&#xff1a;将 JS代码定义在HTML页面中 2.外部脚本&#xff1a;将 JS代码定义在外部 JS文件中&#xff0c;然后引入到 HTML页面中 JavaScript 基础语法 windows.alert可以省略windows var: 1.作用域为全局变量 2.变量可以重复定义 &#xf…

半桥LLC谐振变换器及同步整流MATLAB仿真(一)

在开关电源中&#xff0c;LLC谐振变换器是最常见的DC-DC变换器之一。 LLC谐振电路早在上世纪80年代就已经提出&#xff0c;到如今仍有广泛的应用&#xff0c;可见其优越性。其优点表现在&#xff1a; 1.LLC的开关器件能实现软开关&#xff0c;开关损耗小 2.效率高、功率密度大 …

[附源码]计算机毕业设计JAVA 宠物医院管理系统

[附源码]计算机毕业设计JAVA 宠物医院管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…