设计模式之建造者模式(二)

news2024/12/22 16:23:30

目录

  • 概述
    • 概念
    • 角色
    • 类图
    • 适用场景
  • 详述
    • 画小人业务
      • 类的介绍
      • 代码
      • 解析
    • 建造者基本代码
      • 类介绍
      • 代码
      • 解析
  • 总结
    • 设计原则
    • 其他

概述

概念

    建造者模式是一种创建型设计模式,它可以将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

角色

在建造者模式中,有以下几个重要角色:

  • 产品(Product):表示正在构建的复杂对象。它由一系列部件组成,这些部件可能是具体类或接口的实现。

  • 抽象建造者(Abstract Builder):定义了构建产品的接口,声明了创建各个部件的抽象方法。

  • 具体建造者(ConcreteBuilder):实现了抽象建造者接口,负责具体的产品构建,并实现各个部件的具体创建方法。

  • 指挥者(Director):负责安排已有的部件的建造过程,和具体建造者进行交互,以便构建最终的产品。

  • 客户端(Client):通过指挥者创建产品对象的客户端代码。

    建造者模式的核心思想是将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。通过定义不同的具体建造者,可以构建出不同的产品对象,而客户端只需关心所需的产品类型和指挥者的调用即可。这样可以增加灵活性,降低了客户端与产品对象的耦合度。

类图

在这里插入图片描述

适用场景

建造者模式适用于以下情况:

  • 当要创建的对象具有复杂的内部结构时。
  • 需要通过多个步骤来构建对象。
  • 构建过程需要根据不同的配置选择不同的表示。

详述

    理解一个设计模式,可以先从概念入手,在建造者模式概念当中,有几个说的较为模糊的词语(其实是专业词汇,只是我们不太懂罢了),再来回顾下概念“将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示”,这里的复杂对象在前面的角色当中有说过,就是product,那什么叫构建和表示分离呢,构建是什么,表示是什么。接下来结合代码和业务说下我的理解。

画小人业务

类的介绍

  • PersonBuilder 抽象建造者类:定义了构建产品各个部件的抽象方法,以及保存产品对象的引用。在例子中,它定义了画人物各个部分的抽象方法,并保存了 Graphics 类型的引用。

  • PersonThinBuilder 具体建造者类:实现了抽象建造者类中定义的抽象方法,构建小瘦人的各个部分。在例子中,它实现了画小瘦人各个部分的方法。

  • PersonFatBuilder 具体建造者类:实现了抽象建造者类中定义的抽象方法,构建小胖人的各个部分。在例子中,它实现了画小胖人各个部分的方法。

  • PersonDirector 指挥者类:负责组织建造过程,控制具体建造者的调用顺序,最终构建出完整的产品。在例子中,它将小瘦人和小胖人的构建过程组织起来,并调用具体建造者的方法构建出完整的人物。

  • Client 客户端类:通过实例化具体建造者和指挥者对象,构建出不同类型的产品。在这里,创建了一个窗口,并在窗口中画出小瘦人和小胖人。

代码

//抽象建造者
abstract class PersonBuilder {
    protected Graphics g;
    public PersonBuilder(Graphics g){
        this.g=g;
    }
    public abstract void buildHead();
    public abstract void buildBody();
    public abstract void buildArmLeft();
    public abstract void buildArmRight();
    public abstract void buildLegLeft();
    public abstract void buildLegRight();

}
//具体建造者--画小瘦人
public class PersonThinBuilder extends PersonBuilder{
    public PersonThinBuilder(Graphics g){
        super(g);
    }
    @Override
    public void buildHead() {
        g.drawOval(150, 120, 30, 30);
    }

    @Override
    public void buildBody() {
        g.drawRect(160, 150, 10, 50);
    }

    @Override
    public void buildArmLeft() {
        g.drawLine(160, 150, 140, 200);
    }

    @Override
    public void buildArmRight() {
        g.drawLine(170, 150, 190, 200);
    }

    @Override
    public void buildLegLeft() {
        g.drawLine(160, 200, 145, 250);
    }

    @Override
    public void buildLegRight() {
        g.drawLine(170, 200, 185, 250);
    }
}
//具体建造者2--画小胖人
public class PersonFatBuilder extends PersonBuilder{
    public PersonFatBuilder(Graphics g) {
        super(g);
    }

    @Override
    public void buildHead() {
        g.drawOval(250, 120, 30, 30);

    }

    @Override
    public void buildBody() {
        g.drawOval(245, 150, 40, 50);

    }

    @Override
    public void buildArmLeft() {
        g.drawLine(250, 150, 230, 200);

    }

    @Override
    public void buildArmRight() {
        g.drawLine(280, 150, 300, 200);
    }

    @Override
    public void buildLegLeft() {
        g.drawLine(260, 200, 245, 250);

    }

    @Override
    public void buildLegRight() {
        g.drawLine(280, 200, 185, 250);

    }
}
//指挥者
public class PersonDirector {
    private PersonBuilder pb;

    public PersonDirector(PersonBuilder pb) {
        this.pb = pb;

    }
public void createPerson(){
        pb.buildHead();
        pb.buildBody();
        pb.buildArmLeft();
        pb.buildArmRight();
        pb.buildLegLeft();
        pb.buildLegRight();
}
}
//客户端
public class Client extends JFrame {
    public Client() {
        setSize(400, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }

    public void paint(Graphics g) {
        PersonThinBuilder gThin = new PersonThinBuilder(g);
        PersonDirector pdThin = new PersonDirector(gThin);
        pdThin.createPerson();

        PersonFatBuilder gFat = new PersonFatBuilder(g);
        PersonDirector pdFat = new PersonDirector(gFat);
        pdFat.createPerson();
    }

    public static void main(String[] args) {
        new Client().setVisible(true);//显示窗口
    }
}


    在这个例子中,可以清晰地看到建造者模式的结构和流程:抽象建造者定义产品的各个部分,具体建造者实现这些部分;指挥者组织建造过程,调用具体建造者构建出完整的产品。这种方式可以使得系统更加灵活、可扩展,并且降低了组件之间的耦合度。

解析

    本例中通过定义不同的具体建造者,可以创建出具有不同特征的产品。在示例中,通过 PersonThinBuilder 和 PersonFatBuilder 可以构建出小瘦人和小胖人两种不同类型的产品。
添加新的具体建造者并不影响已有的建造者类和指挥者类。如果需要增加一种新类型的产品,只需要创建一个新的具体建造者来实现该产品的构建过程,并在指挥者类中调用新的具体建造者即可。这样,系统的扩展性得到了保证,而无需修改已有的代码。
    指挥者类负责组织建造过程,但它不知道具体建造者的细节。具体建造者类负责构建产品的各个部分,但它不知道最终产品的组装方式。这样,指挥者和具体建造者之间的关系是松耦合的,它们之间仅通过抽象建造者进行交互。这种解耦使得指挥者类和具体建造者类可以独立变化,互不影响。
    总之,这里通过将复杂对象的构建过程分解为多个简单的部分,使得系统更加灵活、可扩展,并且减少了组件之间的依赖关系,提高了系统的可维护性和可测试性。

建造者基本代码

    根据业务了解完建造者模式之后,来看下建造者的基本代码,同时也来对比一下基本代码和上面的画小人的业务有什么不一样的地方。

类介绍

    建造者模式的基本代码包含了四个角色:产品类、抽象建造者、具体建造者和指挥者。Product 类表示产品,Builder 类是抽象建造者,ConcreteBuilder1 和 ConcreteBuilder2 是具体建造者,而 Director 是指挥者。这些角色各自承担不同的责任,协作完成产品的构建过程。而在上面的画小人的代码中,并没有product,那这两段代码有什么本质上的不同吗,先看完下面的代码。

代码

//产品
public class Product {
    ArrayList<String> parts = new ArrayList<String>();
    //添加新的产品部件
    public void add(String part){
        parts.add(part);
    }
    //列举所有的产品
    public void show(){
        for(String part:parts){
            System.out.println(part);
        }
    }
}
//抽象建造者
abstract class Builder {
    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract Product getResult();

}

//具体建造者1
public class ConcreteBuilder1 extends Builder {
    private Product product = new Product();

    @Override
    public void buildPartA() {
        product.add("部件A");
    }

    @Override
    public void buildPartB() {
        product.add("部件B");
    }

    @Override
    public Product getResult() {
       return product;
    }
}
//具体建造者2
public class ConcreteBuilder2 extends Builder {
    private Product product = new Product();

    @Override
    public void buildPartA() {
        product.add("部件X" );
    }

    @Override
    public void buildPartB() {
        product.add("部件Y");
    }

    @Override
    public Product getResult() {
       return product;
    }
}
//指挥者
public class Director {
    public void construct(Builder builder){
        builder.buildPartA();
        builder.buildPartB();
    }
}
//客户端
public class Client {
    public static void main(String[] args) {
        Director director = new Director();
        Builder b1 = new ConcreteBuilder1();
        Builder b2 = new ConcreteBuilder2();

        //指挥者用concreteBuilder1的方法来建造产品
        director.construct(b1);
        Product p1=b1.getResult();
        p1.show();

        //指挥者用concreteBuilder2的方法来建造产品
        director.construct(b2);
        Product p2=b2.getResult();
        p2.show();

    }
}

解析

两段代码的不同:

    1、在上面画小人的例子中, PersonFatBuilder和PersonThintBuilder使用了具体的数值来定义绘图的位置和大小,这在一些情况下可能不够灵活和可维护。通常,建议使用变量或常量来代替这些硬编码的数值,以增加代码的可读性和可扩展性。

    2、画小人的例子中没有明确定义一个产品类(Product)。建造者模式通常包含一个产品类,建造者负责创建并组装产品,而指挥者负责调用具体的建造者来创建产品。但是在画小人的例子并没有一个显式的产品类。在这里可以认为PersonBuilder和其子类(如PersonFatBuilder)兼具了建造者和产品的角色。也就是说,建造者本身也承担了产品的属性和方法,用于表示要构建的对象,(但是要注意这里的产品有具体的属性值了,这与product就有了很大的区别,后面细说),但是即便没有显式的产品类,但仍然可以理解为遵循了建造者模式的核心思想:将构建逻辑与表现逻辑分离,通过不同的建造者来创建不同的对象。建造者模式不一定非要有一个显式的产品类,这取决于具体的设计需求。在一些简单的场景中,可以将产品的属性和方法直接定义在建造者中,以简化代码结构。然而,在更复杂的场景中,使用一个独立的产品类可以更好地组织和管理产品的属性和行为。

    3、在看两段代码中建造者里的接口(看指挥者里调用的builder接口也一样,这样可能更直观),看代码:

//画小人业务中builder里的接口
public void createPerson(){
        pb.buildHead();
        pb.buildBody();
        pb.buildArmLeft();
        pb.buildArmRight();
        pb.buildLegLeft();
        pb.buildLegRight();
}
//建造者基本代码中builder里的接口
public class Director {
    public void construct(Builder builder){
        builder.buildPartA();
        builder.buildPartB();
    }
}

    这两个看起来好像只是名字不同,其实完全不一样,画小人的业务画出来的只能是小人,因为有头有胳膊有腿,但是建造者基本代码中起名是buildPartA(),再去看两个具体的builder里添加的都是什么内容(一个是“部件A/B”,另一个是“部件X/Y”), 这里buildpartA和buildpartB并不是具体的内容,只是两个规范或者说两个标准,这里不光是标准,也定义了执行的顺序,具体创建什么东西是由product来决定的, Product当中添加的参数类型,也是可以更换的,也就是用户指定的需要创建的类型。 也就是符合Builder当中定义的规范数量,并且符合product当中 add方法接收的参数的类型的产品就可以,无论是部件a,b还是部件xy ,再举一个例子,可以创建有头有身子的小人也可以创建放盐又放醋的菜。在逻辑上就好像改变父类 Builder的类型的效果。

    4、现在就来说说要引入一个独立的产品类product有什么好处:
    前面说过概念中的复杂对象指的是product。这里product也是概念中提到的“表示”,在这里是个虚的概念,所谓虚的就是谁都可以,只要符合规范,规范指的的只要数据类型是String就可以,当然这个类型也是你自己定义的。例子中指定的是String。

    ConcreteBuilder做“构建”的,构建的是product的组件(builderPartA/B),这部分的组件是由部件(部件X/Y等)构成的,规定的是每个组件里部件(最小的颗粒)的数量和顺序。在这里也可以把builderPartA/B看成是部件的“表示”。而director相对于builder来说是构建的过程,构建的是各个组件(builderPartA/B)的顺序,以及有哪些组件(不是ConcreteBuilder定义的组件都必须调用,需要谁就调用谁)。

总结

设计原则

在建造者模式中,涉及到以下几个设计原则:

  • 单一职责原则(Single Responsibility Principle):每个类应该只有一个引起它变化的原因。在建造者模式中,具体建造者负责构建产品的各个部件,指挥者负责组织建造过程,产品负责表示正在构建的复杂对象。通过将不同的责任分配给不同的类,保持了类的单一职责。

    开闭原则(Open-Closed Principle):软件实体应对扩展开放,对修改关闭。在建造者模式中,可以通过新增具体建造者来扩展不同类型的产品,而无需修改已有的代码。这样在增加新的产品类型时,不会对现有的客户端代码产生影响。

  • 依赖倒置原则(Dependency Inversion Principle):高层模块不应依赖于低层模块,二者都应依赖于抽象。在建造者模式中,指挥者与具体建造者之间的交互是通过抽象建造者接口进行的,而不是直接依赖于具体建造者。这样可以降低耦合性,并且使得具体建造者可以灵活替换。

  • 迪米特法则(Law of Demeter):一个对象应该对其他对象保持最小的了解。在建造者模式中,客户端只需要与指挥者进行交互,而不直接与具体建造者交互。这样客户端只需关注所需产品和指挥者的调用,不需要了解具体建造者的细节。

    通过遵循以上设计原则,建造者模式可以使系统更加灵活、可扩展,并且降低了组件之间的耦合度。

其他

    建造者模式适用于创建复杂对象,但如果对象结构相对简单,可以考虑使用其他创建型模式,如工厂方法模式。
    当构建过程固定且简单时,可以考虑省略抽象建造者和指挥者的角色,直接在具体建造者中进行构建。

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

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

相关文章

Win11 TensorRT环境部署

一、CUDA和CUDNN安装 cuda和cudnn网上有很多安装教程&#xff0c;这里列举了一些&#xff0c;就不详细说了&#xff0c;具体链接如下&#xff1a; csdn.net - CUDA安装教程&#xff08;超详细&#xff09; 原创 zhihu.com - 深度学习之CUDACUDNN详细安装教程 tencent.com - C…

《地理信息系统原理》笔记/期末复习资料(13. 地理信息系统的发展趋势)

目录 13. 地理信息系统的发展趋势 13.1. 互操作GIS 13.1.1. 传统GIS在数据标准化上的缺陷和面临的新课题 13.1.2. GIS互操作的概念 13.1.3. 开放式地理信息系统&#xff08;OGIS&#xff09;及其特点 13.1.4. OGIS的组成部分 13.1.5. OGIS的实现技术 13.2. GIS的集成化…

【C语言】C的面向对象

一、BREW接口实现 高通的BREW&#xff08;Binary Runtime Environment for Wireless&#xff09;是一个早期为手机设备开发的应用程序平台&#xff0c;用于开发在CDMA手机上运行的软件。尽管这个平台目前已经不太流行&#xff0c;但是在其使用高峰时期&#xff0c;开发者需要使…

2024年甘肃省职业院校技能大赛信息安全管理与评估赛项二三阶段样题一

2024年甘肃省职业院校技能大赛高职学生组电子与信息大类信息安全管理与评估赛项样题一 第二阶段 任务书 任务描述 随着网络和信息化水平的不断发展&#xff0c;网络安全事件也层出不穷&#xff0c;网络恶意代码传播、信息窃取、信息篡改、远程控制等各种网络攻击 行为已严重…

LVS负载均衡器(nat模式)+nginx(七层反向代理)+tomcat(多实例),实现负载均衡和动静分离

目录 前言 一、配置nfs共享存储 二、配置2个nginx节点服务的网页页面 节点1:192.168.20.10 步骤一&#xff1a;修改网关指向调度器的内网ip地址 步骤二&#xff1a;将nfs共享的目录进行挂载&#xff0c;并修改nginx的配置文件中location的root指向挂载点 步骤三&#xff…

GPT 魔力涌现

GPT 二、Prompt 的典型构成 角色&#xff1a;给 AI 定义一个最匹配任务的角色&#xff0c;比如&#xff1a;「你是一位软件工程师」「你是一位小学老师」指示&#xff1a;对任务进行描述上下文&#xff1a;给出与任务相关的其它背景信息&#xff08;尤其在多轮交互中&#xff…

“一键调整尺寸,轻松完成视频批量剪辑:批量放大视频尺寸“

你是否曾经遇到过需要批量调整视频尺寸的情况&#xff1f;无论是为了适应不同的播放平台&#xff0c;还是为了满足客户的特定需求&#xff0c;批量调整视频尺寸都是一项繁琐而耗时的工作。但是&#xff0c;现在有一种方法可以让你轻松完成这项任务&#xff0c;那就是使用我们的…

豪腾四海×实在RPA丨最懂财务的数字员工,为企业节省人天2000+

企业数字化转型&#xff0c;财务是一个重要的切入点。随着豪腾四海数字化业务不断展开&#xff0c;新的系统、流程和数据源被不断引入&#xff0c;财务部门面临的是不断暴增的对账、数电票处理、审计等日常工作。 “如此大的工作量&#xff0c;即使是经验丰富的资深财务&#…

设计模式——桥接模式(结构型)

引言 桥接模式是一种结构型设计模式&#xff0c; 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构&#xff0c; 从而能在开发时分别使用。 问题 抽象&#xff1f; 实现&#xff1f; 听上去挺吓人&#xff1f; 让我们慢慢来&#xff0c; 先考虑一个简单的…

【Oracle】创建表

目录 方法一&#xff1a;CREATE TABLE 语法 创建表示例1&#xff1a;创建stuinfo(学生信息表) 创建表示例2&#xff1a;添加stuinfo(学生信息表)约束 方法二&#xff1a;CREATE TABLE AS 语法 创建表示例3&#xff1a; 创建表示例4&#xff1a;实现对select查询的结果进行…

利用有限制通配符来提升API的灵活性

在Java中&#xff0c;有限制通配符&#xff08;bounded wildcard&#xff09;允许你在泛型中指定一个范围&#xff0c;从而提升API的灵活性。通配符使得你能够编写更通用、适用于多种类型的代码。以下是一个利用有限制通配符提升API灵活性的例子&#xff1a; 假设有一个简单的…

PR模板,复古怀旧电影效果视频制作PR项目工程文件

Premiere复古怀旧电影效果视频制作pr模板项目工程文件下载 这个PR模板以复古城市印象电影质感为特色&#xff0c;结合了电影和数字故障效果。包含6个场景。可以编辑文本、添加媒体和自定义颜色。包含视频教程。4K版本。不需要任何插件。 软件支持&#xff1a;PR2022 | 分辨率&a…

大数据Doris(三十六):Duplicate 模型(冗余模型)介绍

文章目录 Duplicate 模型(冗余模型)介绍 一、创建doris表 二、插入数据

RocketMQ Streams详解

一、RocketMQ Streams 概览 RocketMQ Streams是基于RocketMQ的轻量级流计算引擎。能以SDK方式被应用依赖&#xff0c;无须部署复杂的流计算服务端即可获得流计算能力。 因此具有资源消耗少、扩展性好、支持流计算算子丰富的特点。 1、整体架构​ 数据从RocketMQ中被RocketMQ-…

「X」Embedding in NLP|神经网络和语言模型 Embedding 向量入门

在「X」Embedding in NLP 进阶系列中&#xff0c;我们介绍了自然语言处理的基础知识——自然语言中的 Token、N-gram 和词袋语言模型。今天&#xff0c;我们将继续和大家一起“修炼”&#xff0c;深入探讨神经网络语言模型&#xff0c;特别是循环神经网络&#xff0c;并简要了解…

【postgresql】ERROR: INSERT has more expressions than target columns

执行下面sql insert into apply_account_cancellation3 select * from pply_account_cancellation; 返回下面错误信息 insert into apply_account_cancellation3 select * from apply_account_cancellation > ERROR: INSERT has more expressions than target colu…

ShardingSphereJDBC简单入门

ShardingSphere 介绍ShardingSphere-JDBCSharding-Sphere-ProxyShardingSphere-Sidecar混合架构运行模式DistSQL可拔插架构ShardingSphere的发展路线 主从复制ShardingSphere-JDBC功能SQL解析SQL支持程度SQL稳定支持SQL实验性支持 MySQL不支持SQL清单分页 数据分片垂直分片水平…

世界5G大会

会议名称:世界 5G 大会 时间:2023 年 12 月 5 日-12 月 8 日 地点:河南郑州 一、会议简介 世界 5G 大会,是由国务院批准,国家发展改革委、科技部、工 信部与地方政府共同主办,未来移动通信论坛联合属地主管厅局联合 承办,邀请全球友好伙伴共同打造的全球首个 5G 领域…

KITTI数据集处理为COCO数据集格式

KITTI作为自动驾驶常用数据集&#xff0c;被广泛的应用于自动驾驶目标检测等过程中。 首先是数据集类别合并&#xff0c;原始的KITTI数据集有九个类别&#xff0c;分别是&#xff1a; Car Van Truck Pedestrian Person_sitting Cyclist Tram Misc而我们在使用过程中&#xff0…

使用@ApiModel和@ApiModelProperty的技巧

在现代软件开发中&#xff0c;提供清晰全面的 API 文档 至关重要。ApiModel 和 ApiModelProperty 这样的代码注解在此方面表现出色&#xff0c;通过增强模型及其属性的元数据来丰富文档内容。它们的主要功能是为这些元素命名和描述&#xff0c;使生成的 API 文档更加明确。 Api…