Java 设计模式——建造者模式

news2025/6/28 1:07:04

目录

  • 1.概述
  • 2.结构
  • 3.实例
    • 3.1.产品类
    • 3.2.抽象建造者类
    • 3.3.具体建造者类
    • 3.4.指挥者类
    • 3.5.测试
  • 4.优缺点
  • 5.使用场景
  • 6.模式扩展
  • 7.创建者模式对比

1.概述

建造者模式 (Builder Pattern) 是一种创建型设计模式,用于创建复杂对象。它将对象的构建过程分离成独立的部分,同时允许按照不同的方式构建对象。通常情况下,当一个对象的构建过程有多个步骤,且这些步骤可以按照不同的顺序来构建时,建造者模式就非常有用。通过将构建逻辑封装到具体的建造者类中,客户端可以使用相同的构建过程来创建不同类型的对象。

2.结构

建造者模式包含如下角色:

  • 抽象建造者类 (Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
  • 具体建造者类 (ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
  • 产品类 (Product):要创建的复杂对象。
  • 指挥者类 (Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。类图如下:
    在这里插入图片描述

3.实例

【例】创建共享单车:生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。而车架又有碳纤维,铝合金等材质的,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。这里 Bike 是产品,包含车架,车座等组件;Builder 是抽象建造者,MobikeBuilder 和 OfoBuilder 是具体的建造者;Director 是指挥者。类图如下:
在这里插入图片描述
具体代码如下:

3.1.产品类

Bike.java

public class Bike {
    private String frame;//车架
    
    private String seat;//车座
    
    public String getFrame() {
        return frame;
    }
    
    public void setFrame(String frame) {
        this.frame = frame;
    }
    
    public String getSeat() {
        return seat;
    }
    
    public void setSeat(String seat) {
        this.seat = seat;
    }
}

3.2.抽象建造者类

Builder.java

public abstract class Builder {
    //声明Bike类型的变量,并进行赋值
    protected Bike bike = new Bike();
    
    public abstract void buildFrame();
    
    public abstract void buildSeat();
    
    //构建自行车的方法
    public abstract Bike createBike();
}

3.3.具体建造者类

MobileBuilder.java

//具体的构建者,用来构建摩拜单车对象
public class MobileBuilder extends Builder{
    public void buildFrame() {
        bike.setFrame("碳纤维车架");
    }
    
    public void buildSeat() {
        bike.setSeat("真皮车座");
    }
    
    public Bike createBike() {
        return bike;
    }
}

OfoBuilder.java

public class OfoBuilder extends Builder{
    public void buildFrame() {
        bike.setFrame("铝合金车架");
    }
    
    public void buildSeat() {
        bike.setSeat("橡胶车座");
    }
    
    public Bike createBike() {
        return bike;
    }
}

3.4.指挥者类

Director.java

public class Director {
    //声明 builder 类型的变量
    private Builder builder;
    
    public Director(Builder builder) {
        this.builder = builder;
    }
    
    //组装自行车的功能
    public Bike construct() {
        builder.buildFrame();
        builder.buildSeat();
        return builder.createBike();
    }
}

3.5.测试

Client.java

public class Client {
    public static void main(String[] args) {
        //创建指挥者对象
        Director director = new Director(new MobileBuilder());
        //让指挥者只会组装自行车
        Bike bike = director.construct();
        
        System.out.println(bike.getFrame());
        System.out.println(bike.getSeat());
    }
}

注意:上面示例是 Builder 模式的常规用法,指挥者类 Director 在建造者模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把指挥者类和抽象建造者进行结合。

// 抽象 builder 类
public abstract class Builder {
	protected Bike mBike = new Bike();
	
	public abstract void buildFrame();
	public abstract void buildSeat();
	public abstract Bike createBike();
	
	public Bike construct() {
		this.buildFrame();
		this.BuildSeat();
		return this.createBike();
	}
}

4.优缺点

(1)建造者模式具有以下几个优点:

  • 分离构建过程和表示:建造者模式可以将一个复杂对象的构建过程与它的表示分离,使得相同的构建过程可以创建不同的表示。这样可以使代码结构更清晰,易于阅读和维护。
  • 隐藏产品内部细节:建造者模式将对象的构建过程封装在具体的建造者类中,使得客户端可以不需要知道产品的内部实现细节,只需要关注如何组装和使用产品。
  • 提供更好的控制:由于建造者模式将构建过程分步进行,可以通过指定不同的具体建造者,实现不同的构建过程。这样就可以更灵活地控制构建过程,根据需要定制复杂对象的创建方式。

(2)建造者模式的一些缺点包括:

  • 增加了代码量:引入建造者模式会增加额外的代码量,因为需要定义抽象建造者和具体建造者类,以及指挥者类等。对于简单的对象构建过程,可能会显得繁琐。
  • 对客户端的要求高:客户端需要显式地创建指挥者对象,并指定具体的建造者,且需要了解建造者类的接口和使用方法。这对于不熟悉建造者模式的开发人员来说可能会增加学习成本。

综上所述,建造者模式在创建复杂对象时具有很多优点,但也需要在实际应用中权衡其优缺点来决定是否使用。

5.使用场景

(1)建造者模式适用于以下几种场景:

  • 当对象的构建过程比较复杂,需要有多个步骤或者按照不同的顺序构建时,可以使用建造者模式。例如,创建一个包含多个部分的电子产品,每个部分的构建方式可能不同,而且可以按照不同的顺序组装。
  • 当需要创建的对象有多个不同的表示或配置时,可以使用建造者模式。使用不同的具体建造者可以创建不同的对象表示,而且可以在运行时动态地选择具体建造者。
  • 当想要隔离产品的构造和表示细节,使得构造过程和最终的产品解耦时,可以使用建造者模式。客户端只需要关注构建过程和产品的使用,而不需要关注产品的内部实现细节。
  • 当需要创建一系列相似但稍有差异的对象时,可以使用建造者模式。通过复用相同的构建逻辑和变化的部分,可以高效地创建多个对象。
  • 当需要在一个对象的构建过程中能够灵活地修改构建方式或者增加更多的构建步骤时,可以使用建造者模式。具体建造者的实现可以根据需求进行扩展和修改,而不会影响到客户端代码。

(2)需要注意的是,如果对象的构建过程相对简单,没有多个步骤或者差异很小,或者只有少数几个属性需要设置,那么使用建造者模式可能会显得过于繁琐,可以考虑使用其他创建型模式或者简化构建过程。

6.模式扩展

建造者模式除了上面的用途外,在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构
(1)重构前代码如下:
Phone.java

public class Phone {
    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;
    
    public Phone(String cpu, String screen, String memory, String mainboard) {
        this.cpu = cpu;
        this.screen = screen;
        this.memory = memory;
        this.mainboard = mainboard;
    }
    
    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        //构建Phone对象
        Phone phone = new Phone("麒麟","三星屏幕","金士顿","华硕主板");
        System.out.println(phone);
    }
}

上面在客户端代码中构建Phone对象,传递了四个参数,如果参数更多,那么代码的可读性会变差,以及使用的成本会变高。

(2)重构后代码如下:
Phone.java

public class Phone {
    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;
    
    private Phone(Builder builder) {
        cpu = builder.cpu;
        screen = builder.screen;
        memory = builder.memory;
        mainboard = builder.mainboard;
    }
    
    public static final class Builder{
        private String cpu;
        private String screen;
        private String memory;
        private String mainboard;
        
        public Builder(){}
        
        public Builder cpu(String val){
            cpu = val;
            return this;
        }
    
        public Builder screen(String val){
            screen = val;
            return this;
        }
    
        public Builder memory(String val){
            memory = val;
            return this;
        }
    
        public Builder mainboard(String val){
            mainboard = val;
            return this;
        }
        
        public Phone build(){
            return new Phone(this);
        }
    }
    
    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        //构建Phone对象
        Phone phone = new Phone.Builder()
                .cpu("麒麟")
                .memory("金士顿")
                .screen("三星屏幕")
                .mainboard("华硕")
                .build();
        System.out.println(phone);
    }
}

重构后的代码在使用起来更方便,某种程度上也可以提高开发效率。从软件设计上,对程序员的要求比较高。

7.创建者模式对比

(1)工厂方法模式 VS 建造者模式
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。

(2)抽象工厂模式 VS 建造者模式

  • 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
  • 建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。

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

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

相关文章

在 Linux 中重命名文件和目录

目录 前言 使用 mv 命令重命名文件和目录 通过组合 mv、find 和 exec 命令重命名与某个模式匹配的多个文件 使用 rename 命令轻松重命名多个文件 总结 前言 在这篇基本命令行教程中,你将学习在 Linux 终端重命名文件和目录的各种方法。 如何在 Linux 终端中重命…

从前序与中序遍历序列构造二叉树(C++实现)

从前序与中序遍历序列构造二叉树 题目思路分析代码代码讲解 题目 思路分析 我们可以通过递归实现的二叉树构建函数。它根据给定的先序遍历序列和中序遍历序列构建一棵二叉树,并返回根节点。可以创建一个_build 函数,该函数负责构建二叉树的节点&#xff…

个人测试用例以及测试金句分享

📑打牌 : da pai ge的个人主页 🌤️个人专栏 : da pai ge的博客专栏 ☁️宝剑锋从磨砺出,梅花香自苦寒来 ☁️测试必背金句 界面易用可双安&…

Ants

描述 输入 L10 n3 x{2,6,7} 输出 min4 max8思路 最短时间肯定是每只蚂蚁都朝离自己最近的端点去爬行,这样不会出现蚂蚁相遇的情况 最长时间肯定是每只蚂蚁都朝离自己最远的端点去爬行,但这样会发生蚂蚁相遇的情况 对于最长时间中相遇情况的分析 我们先…

力扣:提莫攻击

代码&#xff1a; class Solution { public:int findPoisonedDuration(vector<int>& timeSeries, int duration){//根据数组中给出的元素的值来进行判断&#xff01;//若后面元素-前面元素>d 中了d秒&#xff01;// <d 中了差的秒数&…

RNN 网络结构及训练过程简介

本文通过整理李宏毅老师的机器学习教程的内容&#xff0c;介绍 RNN&#xff08;recurrent neural network&#xff09;的网络结构。 RNN 网络结构, 李宏毅 RNN RNN 的特点在于存储功能&#xff0c;即可以记忆前面时刻的信息。 最简单的 RNN 结构如下&#xff1a; 当然&a…

MUYUCMS v2.1:一款开源、轻量级的内容管理系统

MuYuCMS&#xff1a;一款基于Thinkphp开发的轻量级开源内容管理系统&#xff0c;为企业、个人站长提供快速建站解决方案。它具有以下的环境要求&#xff1a; 支持系统&#xff1a;Windows/Linux/Mac WEB服务器&#xff1a;Apache/Nginx/ISS PHP版本&#xff1a;php > 5.6 (…

JDK源码系列:StringBuffer与StringBuilder对比

一、源码分析StringBuffer与StringBuilder的区别 1、StringBuffer是多线程安全的&#xff0c;StringBuilder是多线程不安全的 多线程安全指的是 多个线程同时对一个对象进行append 等操作&#xff0c;不会出现覆盖、丢失的情况。 看下StringBuffer是如何做到多线程安全的&#…

吉他初学者学习网站搭建系列(3)——如何实现吉他在线调音

文章目录 背景知识teoriapitchytone效果 背景知识 学过初中物理就会知道&#xff0c;声音是由空气振动产生的。振动产生波&#xff0c;所以声音就是不同振幅和频率的波构成的。振幅决定了声音的响度&#xff0c;频率决定了声音的音高。想更进一步了解的可以访问这个网页wavefo…

C++ STL map迭代器失效问题

最近在开发过程中&#xff0c;定位一个问题的时候&#xff0c;发现多线程场景下大量创建和销毁某个C:\Windows\System32\reg.exe时出现了383个进程创建消息处理的接口&#xff0c;和384个进程销毁处理消息的接口都在等待锁&#xff0c;另外一个线程也在等锁&#xff0c;后面看了…

【数据结构实验】查找(二)基于线性探测法的散列表

文章目录 1. 引言2. 实验原理2.1 散列表2.2 线性探测法 3. 实验内容3.1 实验题目&#xff08;一&#xff09;输入要求&#xff08;二&#xff09;输出要求 3.2 算法实现三、实验设计3.3 代码整合 4. 实验结果 1. 引言 本实验将通过C语言实现基于线性探测法的散列表 2. 实验原理…

车载通信架构 —— 传统车内通信网络CAN(可靠性为王)

车载通信架构 —— 传统车内通信网络CAN(可靠性为王) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非…

cesium轨迹线(闪烁轨迹线)

cesium轨迹线(闪烁轨迹线) 下面有源码 实现思路 使用ellipse方法加载圆型,修改polyline中‘material’方法重写glsl来实现当前效果(cesium版本1.109) 示例代码 index.html <!DOCTYPE html> <html lang="en"><head

SQL Server秘籍:数据分隔解密,数据库处理新境界!

点击上方蓝字关注我 在数据数据过程中经常会遇到数据按照一定字符进行拆分&#xff0c;而在不同版本的SQL SERVER数据库中由于包含的函数不同&#xff0c;处理的方式也不一样。本文将列举2个版本的数据库中不同的处理方法。 1. 使用 XML 方法 在SQL SERVER 2016版本之前&#x…

VMware OpenSLP漏洞解决方案

PS&#xff1a;早期为客户做VMware检测的方法&#xff0c;大家如有遇到可参考 OpenSLP堆溢出漏洞攻击大量ESXI服务器&#xff0c;该漏洞编号为CVE-2021-21974&#xff0c;由 OpenSLP 服务中的堆溢出问题引起 大于以下版本则不受影响 ESXi versions 7.x prior to ESXi7…

LangChain 10思维链Chain of Thought一步一步的思考 think step by step

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

nodejs+vue+python+PHP+微信小程序-健身俱乐部在线管理平台的设计与实现-安卓-计算机毕业设计

随着经济的发展、财富的累积&#xff0c;人们生活水平、生活质量大幅度提高&#xff0c;生活环境得到明显改善&#xff0c;但是竞争激烈、人们生活压力大、生活节奏快加上饮食习惯和生活方式不合理导致国内 亚健康人群逐年增多。统计数据表明当前我国亚健康人群比例已经超过了7…

Linux-基本指令(1.0)

Linux是一个非常流行的操作的知识&#xff0c;并提供实例帮助读者更好地理解。让我们一起来学习吧&#xff01;系统&#xff0c;也是云计算、大数据、人工智能等领域的重要基础。学习Linux命令是Linux系统管理的基础&#xff0c;也是开发过程中必不可少的技能。本博客将介绍Lin…

思维模型 波纹效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。小变化&#xff0c;大影响。 1 波纹效应的应用 1.1 波纹效应在市场中的应用 2008 年&#xff0c;美国金融危机爆发&#xff0c;导致全球经济陷入衰退。这场危机的起因是美国房地产市场的崩…

【人工智能】Chatgpt的训练原理

前言 前不久&#xff0c;在学习C语言的我写了一段三子棋的代码&#xff0c;但是与我对抗的电脑是没有任何思考的&#xff0c;你看了这段代码就理解为什么了&#xff1a; void computerMove(char Board[ROW][COL], int row, int col) {while (1){unsigned int i rand() % ROW, …