设计模式之建造者模式详解

news2024/9/24 23:21:58
建造者模式
1)概述

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

1.复杂对象

复杂对象是指包含多个成员属性的对象。

2.结构图

在这里插入图片描述

Builder(抽象建造者):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。

ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。

Product(产品角色):它是被构建的复杂对象,包含多个组成部件。

Director(指挥者):它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。

客户端一般只需要与指挥者交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件或反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

2)案例-基础方案
1.代码实现

Product-复杂对象

import lombok.Data;

@Data
public class Product {
    private  String partA;
    private  String partB;
    private  String partC;
}

Builder-抽象建造者

abstract class Builder {
    protected Product product = new Product();

    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract void buildPartC();

    //返回产品对象
    public Product getResult() {
        return product;
    }
}

ConcreteBuilder-具体建造者

public class ConcreteBuilder extends Builder{
    @Override
    public void buildPartA() {
        System.out.println("A---");
    }

    @Override
    public void buildPartB() {
        System.out.println("B---");
    }

    @Override
    public void buildPartC() {
        System.out.println("C---");
    }

    @Override
    public Product getResult() {
        product.setPartA("A");
        product.setPartB("B");
        product.setPartC("C");
        return product;
    }
}

Director-指挥者

public class Director {
    private Builder builder;

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

    public void setBuilder(Builder builder) {
        this.builder = builder;
    }

    //产品构建与组装方法
    public Product construct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getResult();
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();

        Director director = new Director(builder);

        Product product = director.construct();
        System.out.println(product);
    }
}
2.建造者模式与抽象工厂模式区别
  • 建造者模式返回一个完整的复杂产品,而抽象工厂模式返回一系列相关的产品;
  • 在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,客户端通过指定具体建造者类型并指导Director类如何去生成对象,侧重于一步步构造一个复杂对象,然后将结果返回。
  • 可以将抽象工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,将建造者模式看成一个汽车组装厂,通过对配件进行组装返回一辆完整的汽车。
3)案例-完整解决方案
1.结构图

在这里插入图片描述

ActorController充当指挥者,ActorBuilder充当抽象建造者,HeroBuilder、AngelBuilder 和 DevilBuilder 充当具体建造者,Actor 充当复杂产品。

2.代码实现

复杂对象-Actor

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Actor {
    private String type; //角色类型
    private String sex; //性别
    private String face; //脸型
    private String costume; //服装
    private String hairstyle; //发型
}

抽象建造者-ActorBuilder

abstract class ActorBuilder {
    protected Actor actor = new Actor();

    public abstract void buildType();
    public abstract void buildSex();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();

    public Actor createActor() {
        return actor;
    }
}

具体建造者

public class HeroBuilder extends ActorBuilder {

    public void buildType() {
        actor.setType("英雄");
    }

    public void buildSex() {
        actor.setSex("男");
    }

    public void buildFace() {
        actor.setFace("英俊");
    }

    public void buildCostume() {
        actor.setCostume("盔甲");
    }

    public void buildHairstyle() {
        actor.setHairstyle("飘逸");
    }
}

----------------------------------------------------

public class AngelBuilder extends ActorBuilder {

    public void buildType() {
        actor.setType("天使");
    }

    public void buildSex() {
        actor.setSex("女");
    }

    public void buildFace() {
        actor.setFace("漂亮");
    }

    public void buildCostume() {
        actor.setCostume("白裙");
    }

    public void buildHairstyle() {
        actor.setHairstyle("披肩长发");
    }
}

指挥者-ActorController

public class ActorController {
    public Actor construct(ActorBuilder ab) {
        Actor actor;

        ab.buildType();
        ab.buildSex();
        ab.buildFace();
        ab.buildCostume();
        ab.buildHairstyle();

        actor = ab.createActor();

        return actor;
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        ActorController ac = new ActorController();

        Actor actor;
        actor = ac.construct(new AngelBuilder());

        System.out.println(actor.getType() + "的外观:");
        System.out.println("性别:" + actor.getSex());
        System.out.println("面容:" + actor.getFace());
        System.out.println("服装:" + actor.getCostume());
        System.out.println("发型:" + actor.getHairstyle());
    }
}

在建造者模式中,客户端只需实例化指挥者类,指挥者类针对抽象建造者编程,客户端根据需要传入具体的建造者类型,指挥者将指导具体建造者一步一步构造一个完整的产品,相同的构造过程可以创建完全不同的产品。

4)Director 深入
1.省略Director

在有些情况下,为简化系统结构,可以将Director和抽象建造者Builder进行合并,在Builder中提供逐步构建复杂产品对象的construct()方法。

由于Builder类通常为抽象类,因此可以将construct()方法定义为静态(static)方法,如果将游戏角色设计中的指挥者类ActorController省略,ActorBuilder类的代码修改如下:

/**
 * 省略指挥者
 */
abstract class ActorBuilder02 {
    protected static Actor actor = new Actor();
    
    public abstract void buildType();
    public abstract void buildSex();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();

    public static Actor construct(ActorBuilder ab) {

        ab.buildType();
        ab.buildSex();
        ab.buildFace();
        ab.buildCostume();
        ab.buildHairstyle();

        return actor;
    }
}

或者

/**
 * 省略指挥者
 */
abstract class ActorBuilder03 {
    protected Actor actor = new Actor();

    public abstract void buildType();
    public abstract void buildSex();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();

    public Actor construct() {

        this.buildType();
        this.buildSex();
        this.buildFace();
        this.buildCostume();
        this.buildHairstyle();

        return actor;
    }
}

以上两种对 Director 类的省略方式都不影响系统的灵活性和可扩展性,同时还简化了系统结构,但加重了抽象建造者类的职责,如果construct() 方法较为复杂,待构建产品的组成部分较多,建议还是将 construct() 方法单独封装在Director中,更符合"单一职责原则"。

2.引入钩子方法

建造者模式除了逐步构建一个复杂产品对象外,还可以通过Director类来更加精细地控制产品的创建过程,例如增加钩子方法(HookMethod) 来控制是否对某个 buildPartX() 的调用。

钩子方法的返回值类型通常为boolean类型,方法名一般为isXXX(),钩子方法定义在抽象建造者类中。

例如在游戏角色的抽象建造者类ActorBuilder中定义一个方法isBareheaded(),用于判断某个角色是否为"光头(Bareheaded)",在ActorBuilder中提供一个默认实现,其返回值为false,代码如下所示:

abstract class ActorBuilderWithHook {
    protected Actor actor = new Actor();
    
    public abstract void buildType();
    public abstract void buildSex();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();
    
    //钩子方法
    public boolean isBareheaded() {
        return false;
    }
    
    public Actor createActor() {
        return actor;
    }
}

如果某个角色无须构建头发部件,例如"恶魔(Devil)",则对应的具体建造器DevilBuilder将覆盖isBareheaded()方法,并将返回值改为true,代码如下所示:

public class DevilBuilder extends ActorBuilder {

    public void buildType() {
        actor.setType("恶魔");
    }

    public void buildSex() {
        actor.setSex("妖");
    }

    public void buildFace() {
        actor.setFace("丑陋");
    }

    public void buildCostume() {
        actor.setCostume("黑衣");
    }

    public void buildHairstyle() {
        actor.setHairstyle("光头");
    }

    //覆盖钩子方法
    public boolean isBareheaded() {
        return true;
    }
}

此时,指挥者类 ActorController 的代码修改如下:

public class ActorControllerWithHook {
    public Actor construct(ActorBuilderWithHook ab) {

        Actor actor;

        ab.buildType();
        ab.buildSex();
        ab.buildFace();
        ab.buildCostume();

        //通过钩子方法来控制产品的构建
        if (!ab.isBareheaded()) {
            ab.buildHairstyle();
        }

        actor = ab.createActor();
        return actor;
    }
}

当在客户端代码中指定具体建造者类型并通过指挥者来实现产品的逐步构建时,将调用钩子方法isBareheaded()来判断游戏角色是否有头发,如果isBareheaded()方法返回true,即没有头发,则跳过构建发型的方法buildHairstyle();否则将执行buildHairstyle()方法。

通过引入钩子方法,可以在Director中对复杂产品的构建进行精细的控制,不仅指定buildPartX()方法的执行顺序,还可以控制是否需要执行某个buildPartX()方法。

5)总结

建造者模式的核心在于如何一步步构建一个包含多个组成部件的完整对象,使用相同的构建过程构建不同的产品,在软件开发中,如果需要创建复杂对象并希望系统具备很好的灵活性和可扩展性可以考虑使用建造者模式。

1.优点
  • 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

  • 每一个具体建造者都相对独立,可以方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象,由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合"开闭原则"。

  • 可以精细控制产品的创建过程,将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。

2.缺点
  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。

  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。

3.适用场景
  • 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。

  • 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。

  • 对象的创建过程独立于创建该对象的类,通过引入指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。

  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

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

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

相关文章

项目2-用户登录

1.创建项目 2.引入前端代码并检查是否有误 3.定义接口 需求分析 对于后端开发⼈员⽽⾔, 不涉及前端⻚⾯的展⽰, 只需要提供两个功能 1. 登录⻚⾯: 通过账号和密码, 校验输⼊的账号密码是否正确, 并告知前端 2. ⾸⻚: 告知前端当前登录⽤⼾. 如果当前已有⽤⼾登录, 返回登录的账…

看看Java Web怎么上传文件到服务器

旁白不多说了直接上主题了。 1、新建上传文件夹 在eclipse中&#xff0c;在我们前面文章中用到的项目HelloJSP&#xff0c;在webapp目录下新建uploadfiles文件夹&#xff0c;如下所示&#xff1a; 2、修改HelloWorld.jsp文件 <body><h1>文件上传</h1><…

稻飞虱在线监测仪的工作原理

TH-DF122随着现代农业科技的快速发展&#xff0c;智能化、精准化的农业管理工具日益成为农业生产的得力助手。其中&#xff0c;稻飞虱在线监测仪作为一种创新的农业监测设备&#xff0c;正以其独特的工作原理和显著的应用效果&#xff0c;成为保障稻田生态安全和提高稻米产量的…

ideaSSM 人才引进管理系统bootstrap开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea 开发 SSM 人才引进管理系统是一套完善的信息管理系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff…

[python]bar_chart_race设置日期格式

1、设置日期标签的时间格式 # 设置日期格式&#xff0c;默认为%Y-%m-%dbcr.bar_chart_race(df, covid19_horiz.gif, period_fmt%b %-d, %Y) 2、更改日期标签为数值 # 设置日期标签为数值bcr.bar_chart_race(df.reset_index(dropTrue), covid19_horiz.gif, interpolate_period…

关于在CentOS中卸载MySQL

想要卸载MySQL当然要知道自己的MySQL是用那种方法来安装的了&#xff0c;一般来说MySQL的安装方法在市面上有三种 编译安装、YUM安装、RPM安装&#xff0c;下面会介绍到后两种安装的卸载方法 首先查看是否安装MySQL&#xff0c;一般可以看到版本信息就证明安装了 mysql -V 卸载…

day04_JDBC_课后练习(创建数据库,表格,添加模拟数据,搭建开发环境,编写实体类,实现接口,测试)

文章目录 day04_JDBC_课后练习1、创建数据库2、创建如下表格3、添加模拟数据4、搭建开发环境&#xff0c;准备各个工具组件&#xff08;1&#xff09;使用druid&#xff08;德鲁伊&#xff09;数据库连接池&#xff08;2&#xff09;使用尚硅谷的JDBCTools工具类&#xff08;直…

高中信息技术教资刷题笔记_选择题篇

1.信息技术基础 位与字节的换算 模2除法运算 网页保存 进制之间的计算 教你快速学会二进制、十进制、十六进制之间的转换 - 知乎 (zhihu.com) 原码、补码、反码计算 物联网技术 位运算 按位与&#xff1a;同位置为1&#xff0c;则为1&#xff0c;其他都是0按位或&#xff1a;有…

[Windows常用软件] word 复制粘贴报错修复

背景 在word 内 ctrlv 会报这个错。 microsoft visual basic MathPage.Wll 运行时错误 网上查了一下是 mathtype 导致的&#xff0c;应该是我之前卸载 mathtype 没有卸载干净导致的。 解决方案 参考知乎里面的一个回答解决的&#xff1a;https://www.zhihu.com/question/37…

GAMES101 学习4

材质和外观 材质 BRDF 漫反射 任何方向的光进来都会被均匀的反射到周围各个不同的方向上去 假设能量守恒&#xff0c;那么 Li Lo&#xff0c;这之后BRDF就 &#xff0c;就可以定义一个反照率 &#xff08;Albeo&#xff09; - &#xff0c;在&#xff08;0 - 1&#xff0…

SpringCloud Alibaba Nacos简单应用(二)

&#x1f600;前言 本篇博文是关于SpringCloud Alibaba Nacos简单应用&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的…

人才储备再升级!软通动力子公司鸿湖万联多名讲师荣获首批“鸿蒙原生应用开发培训讲师”认证

近日&#xff0c;由鸿蒙生态服务公司组织开展的国内首批“鸿蒙原生应用开发培训讲师”认证顺利完成。作为此次认证的重要参与者&#xff0c;鸿湖万联申报的四位鸿蒙资深专家级讲师&#xff0c;经过严格的评审环节&#xff0c;全部成功获得了国内首批“鸿蒙原生应用开发培训讲师…

vue基础——java程序员版(总集)

前言&#xff1a; ​ 这是一个java程序员的vue学习记录。 ​ vue是前端的主流框架&#xff0c;按照如今的就业形式作为后端开发的java程序员也是要有所了解的&#xff0c;下面是本人的vue学习记录&#xff0c;包括vue2的基本使用以及引入element-ui&#xff0c;使用的开发工具…

外部普米集中监控多个Prometheus实例:Prometheus Agent 模式与Prometheus 联邦模式 超级详细

外部普米集中监控多个Prometheus实例 Prometheus Agent 模式-使用推送方式来监控1.外部Prometheus配置1.需要开放端口&#xff0c;在启动时&#xff0c;需要配置开放监听端口2.添加prometheus启动参数3.修改配置后重启prometheus即可 2.各个节点的普米配置1.修改prometheus.yml…

夜神模拟器如何配置BurpSuite抓包及安装Xposed框架

希望和各位大佬一起学习&#xff0c;如果文章内容有错请多多指正&#xff0c;谢谢&#xff01; 个人博客链接&#xff1a;CH4SER的个人BLOG – Welcome To Ch4sers Blog 0x00 前言 夜神模拟器安卓 7 以上直接安装证书是无法成功抓取 App 的 HTTPS 数据包的&#xff0c;因为安…

FTP服务器部署

1、FTP 概述 1 &#xff09; FTP &#xff1a; File Transfer Protocol &#xff1a;文件传输协议 2 &#xff09; FTP 协议包括两个组成部分&#xff08; C/S) FTP 服务器 FTP 客户端 3 &#xff09; FTP 服务器用来存储文件 , 客户端电脑可以使用 FTP 客户端软件 来进行…

005、Dynamo与Revit API之间的转换

今天来聊聊 Dynamo 与 Revit 之间图元转换的基础知识&#xff0c;这些需要你牢牢记住哦&#xff0c;不然在 Python script 中写代码&#xff0c;经常会报错的~ 通常来讲&#xff0c;所有来自 Dynamo 节点的几何图形都不是 Revit 的几何对象&#xff0c;所以它们需要与 Revit AP…

Unity 布局元素Layout Element

Layout Element是一种用于控制UI元素在布局组件&#xff08;如Horizontal Layout Group、Vertical Layout Group、Grid Layout Group、Content Size Fitter和Aspect Ratio Fitter&#xff09;中的大小和位置的组件。Layout Element组件可以附加到UI元素上&#xff0c;以便在布局…

牛客题霸-SQL篇(刷题记录三)

本文基于前段时间学习总结的 MySQL 相关的查询语法&#xff0c;在牛客网找了相应的 MySQL 题目进行练习&#xff0c;以便加强对于 MySQL 查询语法的理解和应用。 由于涉及到的数据库表较多&#xff0c;因此本文不再展示&#xff0c;只提供 MySQL 代码与示例输出。 以下内容是…

贪心算法相关题目

文章目录 1. 什么是贪心&#xff1f;2. 分发饼干3. 摆动序列4. 最大子数组和5. 买卖股票的最佳时机 II6. 跳跃游戏7. 跳跃游戏 II8.K 次取反后最大化的数组和9.加油站10.分发糖果11.柠檬水找零 1. 什么是贪心&#xff1f; 贪心的本质是选择每一阶段的局部最优&#xff0c;从而…