当前位置: 首页 > news >正文

学习设计模式之建造者模式,但是宝可梦

前言

作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。

建造者模式

建造者模式是一种创建型模式,主要针对于某一个类有特别繁杂的属性,并且这些属性中有部分不是必须的。
避免在创建对象时,需要众多的构造函数,就有了建造者模式。

比如说,我现在有一个果篮,我可以放苹果、香蕉、梨等各种水果进去构造一个果篮,但是会出现什么状况呢?
我可能今天只有苹果,或者只有香蕉,也有可能我今天只有其中的两种。那么,有3个属性的情况下,会出现共6种情况。
这时候有2种解决办法:1.针对所有情况,写6个不同参数的构造函数;2.写一个全参构造方法,在没有某种水果时传入0或者null。

第一种方法,在属性变多的时候,构造函数直接变成海量;第二种方法,需要记住参数的顺序,容易出错。

(吐槽:要是可以像python一样,把参数名指定一下就好了:比如
plt.plot(data_x, data_y1, label="IUG-CF", marker='*')

那么针对这种“对象的属性过多,又有些属性不必须”的时候,建造者模式是一个好的选择。(就像最近投简历,简历由众多模块组成,可能就没有实习经历,或者项目经历)

1.情景模拟

小智和小霞在旅行,路过西柚市,这里在举办一个多属性大师的比赛,规则是每种属性的宝可梦只能带一只,组成一个队伍参加比赛。
我们把宝可梦组成的队伍抽象成一个类,每种属性的宝可梦作为类的属性。
于是有了Product

/*** Product* 一个多属性队伍由多个属性的宝可梦组成* 省略了Pokemon接口,直接用String*/
public class MultiTypeTeam {private String fireType; // 火属性private String waterType; // 水属性private String flyingType; // 飞行系private String grassType; // 草属性private String electricType; // 电属性// ...// setter + toString 省略
}

小智和小霞找到了比赛的报名入口,这里是一个建造者的抽象建造类:

/*** 抽象建造者Builder* 为每个属性的构建提供方法*/
public interface TeamBuilder {void chooseFireType();void chooseWaterType();void chooseFlyingType();void chooseGrassType();void chooseElectricType();
}

小智的队伍很快构建出来了:

public class SatoshiTeamBuilder implements TeamBuilder{private MultiTypeTeam multiTypeTeam;public SatoshiTeamBuilder() {this.multiTypeTeam = new MultiTypeTeam();}@Overridepublic void chooseFireType() {multiTypeTeam.setFireType("喷火龙");}@Overridepublic void chooseWaterType() {multiTypeTeam.setWaterType("杰尼龟");}@Overridepublic void chooseFlyingType() {multiTypeTeam.setFlyingType("巴大蝴");}@Overridepublic void chooseGrassType() {multiTypeTeam.setGrassType("妙蛙种子");}@Overridepublic void chooseElectricType() {multiTypeTeam.setElectricType("皮卡丘");}@Overridepublic MultiTypeTeam createTeam() {return this.multiTypeTeam;}
}

小霞直接无语了,谁懂啊家人们,我一个水属性大师,当然只有水属性

public class MistyTeamBuilder implements TeamBuilder{private MultiTypeTeam multiTypeTeam;public MistyTeamBuilder() {this.multiTypeTeam = new MultiTypeTeam();}@Overridepublic void chooseFireType() {}@Overridepublic void chooseWaterType() {multiTypeTeam.setWaterType("宝石海星");}// ......@Overridepublic MultiTypeTeam createTeam() {return this.multiTypeTeam;}
}

再定义一个指挥者,实现使用者和对象创建过程的解耦:

/*** 指挥者* 指挥构造,实现解耦,让获取者无需关心对象怎么构建的*/
public class Director {public MultiTypeTeam buildTeam(TeamBuilder teamBuilder){teamBuilder.chooseElectricType();teamBuilder.chooseFireType();teamBuilder.chooseFlyingType();teamBuilder.chooseWaterType();teamBuilder.chooseGrassType();return teamBuilder.createTeam();}
}

测试类:

public class BuilderDemo {public static void main(String[] args) {Director director = new Director();MultiTypeTeam satoshiTeam = director.buildTeam(new SatoshiTeamBuilder());MultiTypeTeam mistyTeam = director.buildTeam(new MistyTeamBuilder());System.out.println(satoshiTeam.toString());System.out.println(mistyTeam.toString());}
}
MultiTypeTeam{fireType='喷火龙', waterType='杰尼龟', flyingType='巴大蝴', grassType='妙蛙种子', electricType='皮卡丘'}
MultiTypeTeam{fireType='null', waterType='宝石海星', flyingType='null', grassType='null', electricType='null'}

稍微总结一下,建造者模式的四个角色:

  • 产品 (Product): 要建造的类
  • 抽象建造者 (Builder): 提供建造需要的方法
  • 具体建造者 (Concrete Builder): 实现建造
  • 指挥者 (Director): 调度建造方法,实现解耦,每次使用对象时只需通过Director,不需要知道怎么构建的

上面的实现方法,其实可以让使用者自己来进行按需构建,从而省略指挥者的角色。
并且,修改其中某个参数的时候,需要写一个全新的实现类,这很麻烦。
于是,通过查阅资料,有一种对传统建造者方法的改进,也称为简化版。

2. 改进实现

顺便在这里做一个改进,我们将水属性宝可梦设置为必须的,以区分必须的属性和非必须的属性,然后将建造的方法的返回类型改为建造者

/*** 返回一个产品对象*/
public interface TeamBuilderSimple {TeamBuilderSimple chooseFireType(String pokemon);TeamBuilderSimple chooseGrassType(String pokemon);TeamBuilderSimple chooseFlyingType(String pokemon);TeamBuilderSimple chooseElectricType(String pokemon);MultiTypeTeam getTeam();
}

那么具体的构造类只需要实现方法:

/*** 自行传入*/
public class ConcreteTeamBuilderSimple implements TeamBuilderSimple{MultiTypeTeam multiTypeTeam;ConcreteTeamBuilderSimple(String waterPokemon){multiTypeTeam = new MultiTypeTeam();multiTypeTeam.setWaterType(waterPokemon);}@Overridepublic TeamBuilderSimple chooseFireType(String pokemon) {multiTypeTeam.setFireType(pokemon);return this;}@Overridepublic TeamBuilderSimple chooseGrassType(String pokemon) {multiTypeTeam.setGrassType(pokemon);return this;}@Overridepublic TeamBuilderSimple chooseFlyingType(String pokemon) {multiTypeTeam.setFlyingType(pokemon);return this;}@Overridepublic TeamBuilderSimple chooseElectricType(String pokemon) {multiTypeTeam.setElectricType(pokemon);return this;}@Overridepublic MultiTypeTeam getTeam() {return multiTypeTeam;}
}

于是要使用的时候,就更加方便,通过一种链式调用自行构建对象,且不需要写繁杂的构造函数

public class BuilderDemo02 {public static void main(String[] args) {MultiTypeTeam satoshiTeam = new ConcreteTeamBuilderSimple("小锯鳄").chooseElectricType("皮卡丘").chooseFireType("暖暖猪").chooseFlyingType("急冻鸟").chooseGrassType("菊草叶").getTeam();MultiTypeTeam mistyTeam = new ConcreteTeamBuilderSimple("暴鲤龙").getTeam();System.out.println(satoshiTeam.toString());System.out.println(mistyTeam.toString());}
}
MultiTypeTeam{fireType='暖暖猪', waterType='小锯鳄', flyingType='急冻鸟', grassType='菊草叶', electricType='皮卡丘'}
MultiTypeTeam{fireType='null', waterType='暴鲤龙', flyingType='null', grassType='null', electricType='null'}

3.应用

3.1 Swagger

写过web应用的读者们应该接触过Swagger, 通过SpringBoot整合Swagger的时候,会编写一个配置类,我们来看看他的代码:

@Configuration
public class SwaggerConfig {@Beanpublic OpenAPI springShopOpenAPI() {return new OpenAPI().info(new Info().title("GoCooking").description("GoCooking API文档").version("v1").license(new License().name("Apache 2.0").url("http://springdoc.org"))).externalDocs(new ExternalDocumentation().description("外部文档").url("https://springshop.wiki.github.org/docs"));}
}

OpenAPI类中有个info属性, 通过info()方法来设置info

public OpenAPI info(Info info) {this.info = info;return this;}

Info类的构建过程,就是本文改进版的写法,比如下面两个方法

    public Info description(String description) {this.description = description;return this;}public Info title(String title) {this.title = title;return this;}

3.2 StringBuilder

StringBuilder使用append()方法后,返回的还是同一个对象,这也是建造者模式的应用

public StringBuilder append(String str) {super.append(str);return this;}

相关文章:

学习设计模式之建造者模式,但是宝可梦

前言 作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。 建造者模式 建造者模式是一种创建型模式,主要针对于某一个类有特别繁杂的属性,并且这些属性中有部分不是必须的。…...

数学建模:变异系数法

🔆 文章首发于我的个人博客:欢迎大佬们来逛逛 变异系数法 变异系数法的设计原理是: 若某项指标的数值差异较大,能明确区分开各被评价对象,说明该指标的分辨信息丰富,因而应给该指标以较大的权重&#xf…...

paddle.load与pandas.read_pickle的速度对比(分别在有gpu 何无gpu 对比)

有GPU 平台 测试通用代码 import time import paddle import pandas as pd# 测试paddle.load start_time time.time() paddle_data paddle.load(long_attention_model) end_time time.time() print(f"Paddle load time: {end_time - start_time} seconds")# 测试…...

探讨uniapp的路由与页面栈及参数传递问题

1首先引入页面栈 框架以栈的形式管理当前所有页面, 当发生路由切换的时候,页面栈的表现如下: 页面的路由操作无非:初始化、打开新页面、页面重定向、页面返回、tab切换、重加载。 2页面路由 uni-app 有两种页面路由跳转方式&am…...

字节一面:你能讲一下跨域吗

前言 最近博主在字节面试中遇到这样一个面试题,这个问题也是前端面试的高频问题,作为一名前端开发工程师,我们日常开发中与后端联调时一定会遇到跨域的问题,只有处理好了跨域才能够与后端交互完成需求,所以深入学习跨域…...

leetcode 563.二叉树的坡度

⭐️ 题目描述 🌟 leetcode链接:https://leetcode.cn/problems/binary-tree-tilt/description/ 代码: class Solution { public:int childFind(TreeNode* root , int& sumTile) {if (root nullptr) {return 0; // 空树坡度为0}int l…...

【第1章 数据结构概述】

目录 一. 基本概念 1. 数据、数据元素、数据对象 2. 数据结构 二. 数据结构的分类 1. 数据的逻辑结构可分为两大类:a. 线性结构;b. 非线性结构 2. 数据的存储结构取决于四种基本的存储方法:顺序存储、链接存储、索引存储、散列存储 3. …...

【附安装包】MyEclipse2019安装教程

软件下载 软件:MyEclipse版本:2019语言:简体中文大小:1.86G安装环境:Win11/Win10/Win8/Win7硬件要求:CPU2.5GHz 内存4G(或更高)下载通道①百度网盘丨下载链接:https://pan.baidu.co…...

poi-tl设置图片(通过word模板替换关键字,然后转pdf文件并下载)

选中图片右击 选择设置图片格式 例如word模板 maven依赖 <!-- java 读取word文件里面的加颜色的字体 转pdf 使用 --><dependency><groupId> e-iceblue </groupId><artifactId>spire.doc.free</artifactId><version>3.9.0</ver…...

[element-ui] el-tree 懒加载load

懒加载&#xff1a;点击节点时才进行该层数据的获取。 注意&#xff1a;使用了懒加载之后&#xff0c;一般情况下就可以不用绑定:data。 <el-tree :props"props" :load"loadNode" lazy></el-tree>懒加载—由于在点击节点时才进行该层数据的获取…...

【C++】使用 nlohmann 解析 json 文件

引言 nlohman json GitHub - nlohmann/json: JSON for Modern C 是一个为现代C&#xff08;C11&#xff09;设计的JSON解析库&#xff0c;主要特点是 易于集成&#xff0c;仅需一个头文件&#xff0c;无需安装依赖 易于使用&#xff0c;可以和STL无缝对接&#xff0c;使用体验…...

Nginx到底是什么,他能干什么?

目录 Ngnix是什么&#xff0c;它是用来做什么的呢&#xff1f; 一。Nginx简介 二&#xff0c;为什么要用Nginx呢&#xff1f; 二。Nginx应用 1.HTTP代理和反向代理 2.负载均衡 Ngnix是什么&#xff0c;它是用来做什么的呢&#xff1f; 一。Nginx简介 Nginx是enginex的简写&…...

如何判断一个java对象还活着

引用计数算法 引用计数器的算法是这样的&#xff1a;在对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器值就加一&#xff1b;当引用失效时&#xff0c;计数器值就减一&#xff1b;任何时刻计数器为零的对象就是不可能再被使用的。 缺点&#x…...

Go语言基础之结构体

Go语言中没有“类”的概念&#xff0c;也不支持“类”的继承等面向对象的概念。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。 类型别名和自定义类型 自定义类型 在Go语言中有一些基本的数据类型&#xff0c;如string、整型、浮点型、布尔等数据…...

前端食堂技术周刊第 96 期:2023 CSS 状态、Nuxt 3.7、TypeScript 5.2、eBay 性能优化、贝塞尔曲线

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;冰镇黑乌龙 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看…...

一文总结Redis知识点

目录 为什么基于MySQL又出现Redis&#xff1f;Redis的优点&#xff1f;Redis支持的基本命令Redis支持的数据结构1 String2 List3 Set4 Sorted Set5 Hash6 Stream 消息队列7 Geospatial 地理空间8 Bitmap 位图9 Bitfield 位域10 HyperLogLog Redis是单线程还是多线程&#xff1f…...

ARM寄存器组

CM3 拥有通用寄存器 R0‐R15 以及一些特殊功能寄存器。 R0-R7&#xff0c;通用目的寄存器 R0-R7也被称为低组寄存器&#xff0c;所有指令可以访问它们&#xff0c;它们的字长为32位&#xff0c;复位后的初始值是不可预料的。 R8-R12&#xff0c;通用目的寄存器 R8-R12也被称…...

Windows查看当前文件夹下的所有.c文件的个数

在Windows的命令提示符&#xff08;CMD&#xff09;中&#xff0c;你可以使用for循环和dir命令结合起来&#xff0c;以计算当前文件夹下所有 .c 文件的个数。 下面是一个简单的示例&#xff0c;这个批处理脚本会计算当前目录下所有 .c 文件的个数&#xff1a; echo off setlo…...

ubuntu Qt 地图离线调用

ubuntu环境下在Qt上调用百度地图_ubuntu 百度地图_拿到金像奖上课那家店的博客-CSDN博客 【Qt初入江湖】Qt QtWebEngineWidgets 底层架构、原理详细描述_鱼弦的博客-CSDN博客 Ubuntu20.04 QT无法用Qwebengine控件的解决方案&#xff08;临时&#xff09;_cmsyq的博客-CSDN博客…...

Android Studio升级到Android API 33版本后,XML布局输入没有提示

低版本的Android Studio升级到Android API 33版本后&#xff0c;XML布局输入没有提示。查一下我目前使用的Android Studio 是2021年发布&#xff0c;而Android API 33是2022年发布的&#xff0c;这是由低版本升级到高版本造成不兼容的问题。解决方法有两种&#xff1a; 第一种…...

PHP开发者必看:如何在本地环境快速搭建gRPC和Protobuf开发环境

PHP开发者必看&#xff1a;如何在本地环境快速搭建gRPC和Protobuf开发环境 作为一名长期与PHP打交道的开发者&#xff0c;我深刻理解在微服务架构盛行的当下&#xff0c;掌握gRPC和Protobuf技术栈的重要性。记得第一次尝试在本地搭建环境时&#xff0c;光是版本兼容问题就耗费了…...

Windows 11安卓子系统实战:无需商店直装APK的终极指南

1. Windows 11安卓子系统核心概念解析 Windows 11安卓子系统&#xff08;Windows Subsystem for Android&#xff0c;简称WSA&#xff09;是微软推出的重磅功能&#xff0c;它让Windows系统首次实现了原生运行安卓应用的能力。这个功能本质上是在Windows内核层构建了一个轻量化…...

OpenClaw自动化办公:nanobot镜像处理Excel与PPT文件

OpenClaw自动化办公&#xff1a;nanobot镜像处理Excel与PPT文件 1. 为什么选择OpenClaw处理办公文档&#xff1f; 上周五下午5点&#xff0c;当我面对第7个需要合并的Excel报表时&#xff0c;手指已经因为重复的复制粘贴动作开始发麻。作为团队里负责月度数据汇总的"表哥…...

如何用FCEUX重温经典游戏?全场景部署指南

如何用FCEUX重温经典游戏&#xff1f;全场景部署指南 【免费下载链接】fceux FCEUX, a NES Emulator 项目地址: https://gitcode.com/gh_mirrors/fc/fceux 为什么选择FCEUX模拟器&#xff1f;&#x1f3ae; 在众多NES模拟器中&#xff0c;FCEUX凭借三大核心优势脱颖而出…...

悬浮门厂家次评:专业视角下的悬浮门(悬航门)品牌解析

悬浮门厂家次评是当前高端出入口领域备受关注的话题&#xff0c;随着各类园区、机关单位、学校等场景对安防与形象要求的提升&#xff0c;悬浮门&#xff08;悬航门&#xff09;凭借其平稳运行、静音美观、抗风稳固等特性&#xff0c;逐渐成为大门采购的主流选择。本文基于行业…...

当multisim遇见ai助手:快马平台如何智能分析与优化你的电路设计

作为一名电子设计爱好者&#xff0c;最近在InsCode(快马)平台尝试了一个特别有意思的项目——用AI辅助优化Multisim电路设计。整个过程就像有个专业的电子工程师在旁边实时指导&#xff0c;分享下我的实践心得&#xff1a; 直流工作点智能诊断 输入一个简单的晶体管放大电路后&…...

云服务器购买怎么选?2026云服务器优惠与租赁指南

在AI创作、3D渲染、远程办公快速发展的今天&#xff0c;「云服务器购买」「云服务器租赁」已经成为越来越多个人和企业的刚需。但面对复杂的配置和价格体系&#xff0c;很多人都会问&#xff1a;&#x1f449; 到底怎么选最划算&#xff1f; &#x1f449; 有没有长期稳定又有“…...

从‘偏差-方差’到一行代码:用NumPy/PyTorch五步实现GAE,附PPO实战避坑点

从‘偏差-方差’到一行代码&#xff1a;用NumPy/PyTorch五步实现GAE&#xff0c;附PPO实战避坑点 强化学习中的策略优化常常面临一个核心挑战&#xff1a;如何准确评估动作的价值&#xff1f;广义优势估计&#xff08;GAE&#xff09;通过巧妙平衡偏差与方差&#xff0c;成为PP…...

软件测试生命周期全解析:用考试答题逻辑,零基础吃透测试核心

之前我们用考场答题的类比&#xff0c;轻松搞懂了软件开发生命周期&#xff0c;很多初学者恍然大悟&#xff1a;原来编程就是一场有章法的“考试”。但一场考试能不能拿到高分、能不能符合出题人&#xff08;客户&#xff09;的要求&#xff0c;光靠埋头答题&#xff08;开发编…...

自动化立体仓库堆垛机设计(设计说明书+17张CAD图纸+开题报告+任务书+实习报告+中期检查报告+外文翻译)

自动化立体仓库堆垛机作为现代物流系统的核心设备&#xff0c;其设计需兼顾机械结构强度、运动控制精度与系统稳定性。该设计通过三维建模与力学仿真验证&#xff0c;确保堆垛机在高速运行时的结构可靠性&#xff0c;同时优化货叉伸缩机构与载货台升降导轨的配合间隙&#xff0…...