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

建造者模式(Builder Pattern)也叫做生成器模式,是 GoF 的 23 种设计模式的一种,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

当我们需要实列化一个复杂的类,以得到不同结构类型和不同的内部状态的对象时,我们可以用不同的类对它们的实列化操作逻辑分别进行封装,这些类我们就称之为建造者。

~

本篇内容包括:关于建造者模式、建造者模式 Demo、使用建造者模式进行重构


文章目录

    • 一、关于建造者模式
        • 1、关于建造者模式
        • 2、关于建造者模式的构成
        • 3、关于建造者模式的优缺点
        • 4、关于建造者模式与工厂模式区别
    • 二、建造者模式 Demo
        • 1、Demo 设计
        • 2、Demo 实现
        • 3、Demo 测试
    • 三、使用建造者模式进行重构
        • 1、重构前代码
        • 2、重构前使用
        • 3、重构后代码
        • 4、重构后使用


一、关于建造者模式

1、关于建造者模式

建造者模式(Builder Pattern)也叫做生成器模式,是 GoF 的 23 种设计模式的一种,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

当我们需要实列化一个复杂的类,以得到不同结构类型和不同的内部状态的对象时,我们可以用不同的类对它们的实列化操作逻辑分别进行封装,这些类我们就称之为建造者。

当我们需要来自同一个类,但是要就有不同结构对象时,就可以通过构造另一个建造者来进行实列化。

建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用。

  • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
  • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。

2、关于建造者模式的构成

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

  • 抽象建造者类(Builder):这个接口规定要实现包含创建产品各个子部件的抽象方法以及返回复杂产品的方法,并不涉及具体的部件对象的创建。
  • 具体建造者类(ConcreteBuilder):实现抽象 Builder 定义的所有方法,并且返回一个装配好的对象。
  • 产品类(Product):一般是多个部件组成的复杂对象,由具体建造者来创建其各个零部件。
  • 指挥者类(Director):负责安排已有模块的顺序,然后调用 Builder 建造产品。

3、关于建造者模式的优缺点

# 优点

  • 封装性,在建造者模式中,调用方不必知道产品内部组成的细节,将一个复杂对象的构建与它的表示分离,使得相同的创建过程可以创建不同的产品对象。
  • 扩展性,每个具体建造者都相互独立,替换具体建造者或新增具体建造者都很便捷。
  • 更关注 “由零件一步一步地组装出产品对象”。将复杂产品的创建步骤拆分到不同的方法中,使得创建过程更加清晰。

# 缺点

  • 建造者模式所创建的产品对象一般组成部分相似,如果产品的内部变化复杂,需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
  • 如果产品内部结构发生变化,建造者也要相应修改,有较大的维护成本。

4、关于建造者模式与工厂模式区别

  1. 工厂模式,一般都是创建一个产品,注重的是把这个产品创建出来就行,只要创建出来,不关心这个产品的组成部分。从代码上看,工厂模式就是一个方法,用这个方法就能生产出产品。,,
  2. 建造者模式,也是创建一个产品,但是不仅要把这个产品创建出来,还要关系这个产品的组成细节,组成过程。 从代码上看,建造者模式在建造产品时,这个产品有很多方法,建造者模式会根据这些相同方法但是不同执行顺序建造出不同组成细节的产品。

二、建造者模式 Demo

1、Demo 设计

生产汽车是一个复杂的过程,它包含了车架,车座等等组件的生产,而车架又有碳纤维,铝合金等材质的,车座有橡胶,真皮等材质。对于汽车的生产就可以使用建造者模式。

2、Demo 实现

# Car 产品类

public class Car {/*** 车架*/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;}
}

# Builder 抽象建造者类

public abstract class Builder {protected Car car = new Car();/*** 建造车架*/public abstract void buildFrame();/*** 建造座椅*/public abstract void buildSeat();/*** 造车** @return Car*/public abstract Car createCar();
}

# ACarBulider 具体建造者类

public class ACarBuilder extends Builder {@Overridepublic void buildFrame() {car.setFrame("铝合金车架");}@Overridepublic void buildSeat() {car.setSeat("真皮车座");}@Overridepublic Car createCar() {return car;}
}

# BCarBulider 具体建造者类

public class BCarBuilder extends Builder {@Overridepublic void buildFrame() {car.setFrame("碳纤维车架");}@Overridepublic void buildSeat() {car.setSeat("皮革车座");}@Overridepublic Car createCar() {return car;}
}

# Director 指挥者类

public class Director {private final Builder mBuilder;public Director(Builder builder) {mBuilder = builder;}public Car construct() {mBuilder.buildFrame();mBuilder.buildSeat();return mBuilder.createCar();}
}

3、Demo 测试

public class Client {private static void showCar(Builder builder) {Director director = new Director(builder);Car car = director.construct();System.out.println(car.getFrame());System.out.println(car.getSeat());}public static void main(String[] args) {showCar(new ACarBuilder());showCar(new BCarBuilder());}}

三、使用建造者模式进行重构

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

1、重构前代码

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;}public String getCpu() {return cpu;}public void setCpu(String cpu) {this.cpu = cpu;}public String getScreen() {return screen;}public void setScreen(String screen) {this.screen = screen;}public String getMemory() {return memory;}public void setMemory(String memory) {this.memory = memory;}public String getMainboard() {return mainboard;}public void setMainboard(String mainboard) {this.mainboard = mainboard;}@Overridepublic String toString() {return "Phone{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainboard='" + mainboard + '\'' +'}';}}

2、重构前使用

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

3、重构后代码

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);}}@Overridepublic String toString() {return "Phone{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainboard='" + mainboard + '\'' +'}';}
}

4、重构后使用

public class Client {public static void main(String[] args) {Phone phone = new Phone.Builder().cpu("intel").mainboard("华硕").memory("金士顿").screen("三星").build();System.out.println(phone);}
}

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

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

相关文章

图文并茂 VLAN 详解,让你看一遍就理解 VLAN

一、为什么需要VLAN 1.1、什么是VLAN? VLAN(Virtual LAN),翻译成中文是“虚拟局域网”。LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络。VLAN所指的LAN特指使用路由器分割的网络——也就是广播域。 在此让我们先复习…

认识VLAN,并学会VLAN的划分和网络配置实例

VLAN的划分和网络的配置实例 1、VLAN基础知识 VLAN(Virtual Local Area Network)的中文名为:“虚拟局域网”,注意和VPN(虚拟专用网)进行区分。 VLAN是一种将局域网设备从逻辑上划分(不是从物…

VLAN划分及配置注意事项

VLAN(Virtual Local Area Network)即虚拟局域网,是将一个物理的LAN在逻辑上划分成多个广播域的通信技术。VLAN内的主机间可以直接通信,而VLAN间不能直接通信,从而将广播报文限制在一个VLAN内。VLAN之间的通信是通过第3…

Docker原理剖析

一、简介 1、了解Docker的前生LXC LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C中的NameSpace。容器有效地将由单个操作系统管理的资源划分到孤立的组中&#…

获取Linux内存、cpu、磁盘IO等信息

#!/bin/bash # 获取要监控的本地服务器IP地址 IPifconfig | grep inet | grep -vE inet6|127.0.0.1 | awk {print $2} echo "IP地址:"$IP# 获取cpu总核数 cpu_numgrep -c "model name" /proc/cpuinfo echo "cpu总核数:"$c…

Docker容器网络解析

Docker 容器网络的发展历史 在 Dokcer 发布之初,Docker 是将网络、管理、安全等集成在一起的,其中网络模块可以为容器提供桥接网络、主机网络等简单的网络功能。 从 1.7 版本开始,Docker正是把网络和存储这两部分的功能都以插件化形式剥离出来…

将指定excel的一列数据提取到另一个excel的指定列

#!/usr/bin/env python import openpyxl bjD:/地市县公司/西藏台账数据分析-设备台帐分析.xlsx wb openpyxl.load_workbook (bj) get_sheets wb.sheetnames #print(get_sheets) TA01TA01 TA02TA02 TA03TA03 TE01TE01 YG201YG201 YG202YG202 YG203YG203 YG204YG204 YG205YG205…

Docker 数据管理介绍

默认容器的数据是保存在容器的可读写层,当容器被删除时其上的数据也会丢失,所以为了实现数据的持久性则需要选择一种数据持久技术来保存数据。官方提供了三种存储方式:Volumes、Bind mounts和tmpfs。前面还介绍了:Docker 服务终端…

Docker 数据持久化的三种方案

容器中的数据可以存储在容器层。但是将数据存放在容器层存在以下问题: 数据不是持久化。意思是如果容器删除了,这些数据也就没了 主机上的其它进程不方便访问这些数据 对这些数据的I/O会经过存储驱动,然后到达主机,引入了一层间…

Git 存储原理及相关实现

Git 是目前最流行的版本控制系统,从本地开发到生产部署,我们每天都在使用 Git 进行我们的版本控制,除了日常使用的命令之外,如果想要对 Git 有更深一步的了解,那么研究下 Git 的底层存储原理将会对理解 Git 及其使用非…

Git内部原理

Git有什么特点? fast,scalable,distributed revision control system(快速,可扩展的分布式版本控制系统) 几乎所有操作都是本地执行 每一个clone都是整个生命周期的完整副本 the stupid content tracker&a…

git存储原理

四种数据类型 实际上Git基于数据类型的不同,把对象分为四种:数据对象、树对象、提交对象、标签对象。Git文件系统的设计思路与linux文件系统相似,即将文件的内容与文件的属性分开存储,文件内容以“装满字节的袋子”存储在文件系统…

详解设计模式:中介者模式

中介者模式(Mediator Pattern)也被称为调停者模式,是在 GoF 23 种设计模式中定义了的行为型模式。 中介者模式 是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支…

rebase参数以及注意事项

可以根据需要将pick参数,改变为下面代表不同作用的参数;这样就可以对节点C和D进行不同的操作了。比如: pick:默认参数,表示不对提交节点进行任何操作,直接应用原提交节点。不创建新提交; rewor…

RPC 服务 与 HTTP 服务的区别

1、什么是RPC RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信…

Docker 网络命名空间

Docker 用户可以通过与 CNM 的 Object 以及 API 的交互来管理对应容器的网络,下面是一个典型的容器网络生命周期: 1、Driver要向NetworkController注册。内置的Driver在Libnetwork内注册,远程的Driver则通过Plugin mechanism注册。每一个Driv…

缓存雪崩、击穿、穿透解决方案

用户的数据一般都是存储于数据库,数据库的数据是落在磁盘上的,磁盘的读写速度可以说是计算机里最慢的硬件了。 当用户的请求,都访问数据库的话,请求数量一上来,数据库很容易就奔溃的了,所以为了避免用户直…

Ansible中的playbook详解

首先简单说明一下playbook,playbook是什么呢? 根本上说playbook和shell脚本没有任何的区别,playbook就像shell一样,也是把一堆的命令组合起来,然后加入对应条件判断等等,在shell脚本中是一条一条的命令&am…

【Docker】容器镜像有哪些特性

首先解释一下什么是Docker镜像? Docker镜像它其实是一个模板,拥有这个模板我们才能创建我们的Docker容器,镜像里含有启动 docker 容器所需的文件系统结构及其内容,因此是启动一个 docker 容器的基础。docker 镜像的文件内容以及一…

nginx中的location指令

1、location 介绍 location是Nginx中的块级指令(block directive),location指令的功能是用来匹配不同的url请求,进而对请求做不同的处理和响应,这其中较难理解的是多个location的匹配顺序,本文会作为重点来解释和说明。 开始之前…