建造者模式深入理解:演示建造单个和多个产品的实践,结合模板模式,通俗易懂

news2024/11/20 13:37:40

首先呢看下建造者的定义是什么样的,先读一遍

建造者模式

建造者模式(Builder Pattern)是一种创建型设计模式,它主要用于将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表现形式。这种模式通过一系列可重用的独立的类(称为建造者或构建器)来一步一步创建一个复杂的对象,而不需要知道具体的内部构造细节

优缺点

建造者模式的主要优点

  1. 封装性好:通过建造者将产品的各个部件和组装过程封装起来,客户端无需了解产品内部的具体构造细节,只需指定需要的产品类型和配置即可。
  2. 扩展性好:由于每个具体建造者都是相互独立的,因此新增一种产品或者变更已有产品的构建方式时,只需要新增或修改相应的建造者类,不影响其他部分代码,从而实现系统的松耦合。
  3. 便于控制构建过程:在构建过程中,可以根据需要逐步细化构造步骤,灵活地控制产品的创建过程,甚至支持动态决定构建部件的数量和顺序。
  4. 便于并行构造:在某些场景下,不同部分的构建可以独立进行,有利于提高系统性能,特别是在多线程环境下的构建。

建造者模式的主要缺点

  1. 增加类的数量:引入建造者模式会增加额外的类,包括抽象建造者、具体建造者和可能的导演类等,增加了系统的复杂性和理解难度。
  2. 内部修改困难:一旦产品结构较为复杂,对于产品内部细节的改动可能会导致多个建造者的相关逻辑都需要调整,维护成本相对较高。
  3. 过度设计风险:如果对象结构比较简单,或者变化不多,使用建造者模式可能会显得过于复杂,不适用于简单对象的构建。
    总结来说,建造者模式非常适合用于创建具有多重构造参数或组合选项的复杂对象,尤其当这些选项的不同组合可能导致大量微小差异的产品实例时,该模式能够很好地管理和组织这些复杂性。

网图来一张
在这里插入图片描述
好了,直接上代码,这里我们先构建一个CarProduct,很多描述都写在了对应的注释中了啊,这里就不过多赘述

package com.mtgg.laoxiang.service.example.designer.builder;

import lombok.Data;

/**
 * 要构建的产品对象
 */
@Data
public class CarProduct {
    private String partA;
    private String partB;
    private String partC;
}
package com.mtgg.laoxiang.service.example.designer.builder;

/**
 * 建造者抽象类
 */
public abstract class Builder {
    //建造产品时,实例化,相当于统一放到一个地方,product相当于一个封装,里面可以放各种东西的组成
    protected CarProduct carProduct = new CarProduct();


    //建造,建造各种东西然后set到product中
    public abstract void builderPartA();

    public abstract void builderPartB();

    public abstract void builderPartC();

    //提供获得产品的入口,要获得这个产品,就调用这个方法
    public CarProduct getResult() {
        return carProduct;
    }

}

上面定义了产品,构造器,获取产品的方法,下面这个就是构造器要具体干的活了

package com.mtgg.laoxiang.service.example.designer.builder;

public class BJBuilder extends Builder{

    @Override
    public void builderPartA() {
        System.out.println("构建个1球");
        carProduct.setPartA("1球");
    }

    @Override
    public void builderPartB() {
        System.out.println("构建个2球");
        carProduct.setPartB("2球");
    }

    @Override
    public void builderPartC() {
        System.out.println("构建个3球");
        carProduct.setPartC("3球");
    }
}

package com.mtgg.laoxiang.service.example.designer.builder;

public class HZBuilder extends Builder{

    @Override
    public void builderPartA() {
        System.out.println("构建个1香蕉");
        carProduct.setPartA("1香蕉");
    }

    @Override
    public void builderPartB() {
        System.out.println("构建个2香蕉");
        carProduct.setPartB("2香蕉");
    }

    @Override
    public void builderPartC() {
        System.out.println("构建个3香蕉");
        carProduct.setPartC("3香蕉");
    }
}

指挥者来了,指挥我到底想要啥,什么数量,什么顺序,定义几个图纸来交给构造器去构造,然后main使用的时候直接调用这个定义好的流程,就能拿到对应的产品

package com.mtgg.laoxiang.service.example.designer.builder;

/**
 * 指挥者
 * 控制生成流程
 */
public class Director {
    //这个抽象类可以定义成全局变量,也可以定义接口,此处可以引用接口,更不违反原则
    private Builder builder;

    /**
     * 注入builder
     */
    private Director(Builder builder) {
        this.builder = builder;
    }

    /**
     * 控制流程,此处类似使用模板模式
     */
    public CarProduct construct2(){
        builder.builderPartC();
        builder.builderPartA();
        return builder.getResult();
    }

    public CarProduct construct(){
        builder.builderPartB();
        builder.builderPartA();
        builder.builderPartC();
        return builder.getResult();
    }

    public static void main(String[] args) {
        //此处可选择不同的建造者建造产品
        Director director = new Director(new HZBuilder());
        CarProduct construct = director.construct();
        System.out.println(construct);

        director = new Director(new BJBuilder());
        CarProduct construct1 = director.construct2();
        System.out.println(construct1);
    }

}

*构建个2香蕉
构建个1香蕉
构建个3香蕉
Product(partA=1香蕉, partB=2香蕉, partC=3香蕉)
构建个3球
构建个1球
Product(partA=1球, partB=null, partC=3球) *

在这里插入图片描述

如果我想建造另外一个对象呢?难道类似的类都要加一份?那肯定是low的,做法如下,

可以加具体建造,定义好要建造的东西,再建一个指挥者方法,尽量不要在原来的方法上改,改造后如下

假如有另一个对象PhoneProduct

@Data
public class PhoneProduct {
    private String pa;
    private String pb;
    private String pc;
}

Builder中加上要构建的这个产品,并加个返回产品的方法,所以这么看的话那几个构建的抽象方法就不能太个性化,最好抽象一些,像是定义三四个工厂一样,具体怎么生产看子类实现

public abstract class Builder {
    //建造产品时,实例化,相当于统一放到一个地方,product相当于一个封装,里面可以放各种东西的组成

    //另外一个产品
    protected PhoneProduct phoneProduct = new PhoneProduct();
……………………

    public PhoneProduct getPhoneResult() {
        return phoneProduct;
    }
}

好了我们定义一个子类去构建具体的内容,不用改另外一个对象的构建,保证对扩展开放,对修改关闭

public class XMBuilder extends Builder{
    @Override
    public void builderPartA() {
        System.out.println("小米a");
        phoneProduct.setPa("pa");
    }

    @Override
    public void builderPartB() {
        System.out.println("小米b");
        phoneProduct.setPb("pb");
    }

    @Override
    public void builderPartC() {
        System.out.println("小米c");
        phoneProduct.setPc("pc");
    }
}

指挥者我们改造一下,构建顺序以及数量都可以抽出来共用,只需要定义construct2或constructPhone方法,指定模板,返回产品就OK了,想要啥产品要啥产品

public class Director {
    //这个抽象类可以定义成全局变量,也可以定义接口,此处可以引用接口,更不违反原则
    private Builder builder;

    /**
     * 注入builder
     */
    private Director(Builder builder) {
        this.builder = builder;
    }

    /**
     * 构建模板 定义构建顺序以及内容
     */
    public void buildTemplateA(){
        builder.builderPartB();
        builder.builderPartA();
        builder.builderPartC();
    }

    /**
     * 构建模板 定义构建顺序以及内容
     */
    public void buildTemplateB(){
        builder.builderPartC();
        builder.builderPartA();
    }

    /**
     * 构建以及获取car产品的入口
     */
    public CarProduct construct2(){
        this.buildTemplateB();
        return builder.getResult();
    }

	//不同形态的car产品
    public CarProduct construct(){
        this.buildTemplateA();
        return builder.getResult();
    }

	//这个是构建和获取phone产品的入口
    public PhoneProduct constructPhone(){
        this.buildTemplateA();
        return builder.getPhoneResult();
    }

    public static void main(String[] args) {
        //此处可选择不同的建造者建造产品
        Director director = new Director(new HZBuilder());
        CarProduct construct = director.construct();
        System.out.println(construct);

        director = new Director(new BJBuilder());
        CarProduct construct1 = director.construct2();
        System.out.println(construct1);

        //构建另外一个产品,使用时指定用XMBuilder这个厂家生产的就行
        director = new Director(new XMBuilder());
        PhoneProduct construct2 = director.constructPhone();
        System.out.println(construct2);
    }

}

下面这是执行结果

构建个2香蕉
构建个1香蕉
构建个3香蕉
CarProduct(partA=1香蕉, partB=2香蕉, partC=3香蕉)
构建个3球
构建个1球
CarProduct(partA=1球, partB=null, partC=3球)
小米b
小米a
小米c
PhoneProduct(pa=pa, pb=pb, pc=pc)

祝你好运!~~

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

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

相关文章

如何用Mac工具制作“苹果高管形象照”

大伙儿最近有没有刷到“苹果高管形象照”风格,详细说来就是: 以苹果官网管理层简介页面中,各位高管形象照为模型,佐以磨皮、美白、高光等修图术,打造的看上去既有事业又有时间有氧的证件照,又称“苹…

【教学类-43-21】完结篇 16宫格(4*4可算全部数字)

作品展示: 16宫格里面的4*4小格子可以凑满1-16,旁边的7宫格格2份 背景需求: 做完了1-20宫格的A4模板,最后做一个16宫格小格子(附加7宫格2套)的样式,只有4宫格(2*2)、9宫…

uniapp+vue3打包问题记录

**背景:**打包app出现问题,只显示底部导航的文字,其他一片空白 1. pages.json文件:tabBar中的iconPath图标格式不支持svg,只支持:png, jpg, jpeg的格式,当图片改为.png的时候可以正常显示 2. …

代码随想录算法训练营第6天 | 242.有效的字母异位词 , 349. 两个数组的交集 , 202. 快乐数 , 1. 两数之和

哈希知识基础 文章链接:https://programmercarl.com/%E5%93%88%E5%B8%8C%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E5%93%88%E5%B8%8C%E8%A1%A8 242.有效的字母异位词 题目链接:https://leetcode.cn/problems/valid-anagram/description/…

cv 不能正常读取中文路径

之前一直以为是PyQT的getOpenFileNames方法不能读取中文路径的。多次尝试后发现不是,是OpenCV的问题。 self.selected_imgPaths, _ QtWidgets.QFileDialog.getOpenFileNames(self, "打开图片", "./pending_images", "*.jpg;;*.JPG;;*.png…

C语言——实用调试技巧

一、为什么漏洞叫bug 为什么漏洞的英文是bug,众所周知bug有虫子的意思,这其实有一个很有名的故事。 1947年,计算机科学的先驱格蕾丝霍普(Grace Hopper)和她的团队在使用哈佛大学的马克二电脑时,发现了一个…

为CT_P自动注册与CT_Run相关的方法

概述 在docx.oxml.text.paragraph模块中定义了CT_P段落对象元素类,但是CT_P中并未定义add_r等与CT_Run相关的方法。在不断探索源码逻辑的过程中,对这种自动为类注册合适的方法的功能进行了梳理——xmlchemy这个模块设计的真好!!&…

rust跟我学(一):模块编写与使用

在rust中,单元文件可以被称为模块。 以下内容,将以get_local_info为例,讲解mod在工程中的使用。 先看下图,network.rs是src文件夹里的一个文件,我们可以单独把这个文件理解成一个模块。Rust比较智能,它可以将文件名称直接理解成一个模块,这在go里面是做不到的。 Rust其实…

​​社交媒体与新闻:Facebook在信息传播中的作用

社交媒体已经成为我们获取和传播新闻的主要渠道之一,而Facebook作为社交媒体的巨头,在信息传播中扮演着举足轻重的角色。本文将深入探讨社交媒体对新闻传播的影响,聚焦于Facebook在这一领域的独特作用,以及这种作用对我们的新闻体…

2024北京智博会:共赴科技盛会,助力跨界融合与实体经济深度发展

2024年6月,一场备受瞩目的科技盛会将在北京亦创国际会展中心拉开帷幕。作为国内外科技交流的重要平台,2024北京智博会将汇集众多科技领域的精英和企业,共同探讨跨界应用、实体经济深度融合等议题,为推动科技强国发展注入新动力。 …

SSL之mkcert构建本地自签名

文章目录 1. 什么是SSL2. mkcert:快速生成自签名证书2.1 mkcert的工作流程如下:2.2 window 本地实现自签证书2.2.1 下载安装2.2.2 下载,生成本地 SSL2.2.3 生成 pem 自签证书,可供局域网内使用其他主机访问。2.2.4 使用-psck12 生成*.p12 文件 2.3 Sprin…

7.11、Kali Linux中文版虚拟机安装运行教程

目录 一、资源下载准备工作 二、安装教程 三、kali linux换源 四、apt-get update 报错 一、资源下载准备工作 linux 中文版镜像历史版本下载:http://old.kali.org/kali-images/ 大家可以自行选择版本下载,本人下载的是2021版本 二、安装教程 打开vmvare wokst…

修炼九阳神功——“函数”

目录 前言 1. 函数的概念 2. 库函数 2.1 标准库和头⽂件 2.2 库函数的使用方法 2.2.1 功能 2.2.2 头⽂件包含 2.2.3 实践 2.2.4 库函数⽂档的⼀般格式 3. 自定义函数 3.1 函数的语法形式 3.2 函数的举例 ​编辑 4. 形参和实参 4.1 实参 4.2 形参 4.3 实参和形…

【dc-dc】世微AP5127平均电流型LED降压恒流驱动器 双色切换的LED灯驱动方案

这是一款双色切换的LED灯方案,12-50V 降压恒流,输出:6V 2.5A ​ 这是一款PWM工作模式 , 高效率、 外围简单、内置功率管,适用于 输入的 高 精度降压 LED 恒流驱动芯片。输出大功率可 达 25W,电流 2.5A。 可实现全亮/半亮功能切换…

Ansible Filter滤波器的使用(二)

一、【说在前面】 Ansible Filter一般被称为滤波器或者叫过滤器。 这个东西初次听到以为是什么科学计算的东西,但是想来ansible不太可能有什么滤波操作,所以这个东西本质是一个数值筛选器,内置函数,本质是一个为了做区别化的工具…

“15个必备的自动化测试工具,助你构建2024年的自动化策略!“

以下为作者观点: 如何选择正确的自动化测试工具?自动化测试工具是旨在通过自动化测试脚本验证功能或非功能软件需求的应用程序,帮助加快发布速度、提高项目质量并强化成果。 自动化测试工具可以帮助开发测试人员轻松创建、运行和维护测试&a…

闲鱼宝库亮相!闲鱼商品详情关键词搜索电商API接口助你畅享无尽好货!

随着互联网的快速发展,电商平台的崛起已经改变了人们的购物习惯。而在众多电商平台中,闲鱼作为一款社区二手交易平台,一直备受用户喜爱。如今,闲鱼宝库正式亮相,为用户带来了更加全面、详细的商品详情关键词搜索电商AP…

两整数之和

题目链接 两整数之和 题目描述 注意点 不使用 运算符 和 - ​​​​​​​&#xff0c;计算并返回两整数之和-1000 < a, b < 1000 解答思路 需要用位运算来模拟加法&#xff0c;关键是要找到相加的和以及进位1的部分。如果不考虑进位的话&#xff0c;相加可以运用异…

Spring - 如何控制多个 AOP 切面执行顺序?

众所周知&#xff0c;Spring 声明式事务是基于 AOP 实现的&#xff0c;那么&#xff0c;如果我们在同一个方法自定义多个 AOP&#xff0c;我们如何指定他们的执行顺序呢&#xff1f; 三种解决方案 1、通过实现 org.springframework.core.Ordered 接口 Component Aspect S…

​LeetCode解法汇总83. 删除排序链表中的重复元素

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给定一个已排序的链表的头 head &#xf…