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

设计模式系列(07):建造者模式(Builder)

本文为设计模式系列第7篇,聚焦创建型模式中的建造者模式,涵盖定义、原理、实际业务场景、优缺点、最佳实践及详细代码示例,适合系统学习与实战应用。


目录

  • 1. 模式概述
  • 2. 使用场景
  • 3. 优缺点分析
  • 4. 实际应用案例
  • 5. 结构与UML类图
  • 6. 代码示例
  • 7. 测试用例
  • 8. 常见误区与反例
  • 9. 最佳实践
  • 10. 参考资料与延伸阅读

1. 模式概述

建造者模式(Builder Pattern)是一种创建型设计模式。它将复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示。适用于需要分步骤、可定制、部件复杂的对象创建。

1.1 定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

1.2 目的

  • 分离构建过程与表示,支持灵活扩展
  • 简化复杂对象的创建,隐藏细节
  • 支持多种产品配置和复用

2. 使用场景

建造者模式在实际开发中应用广泛,常见场景包括:

  1. 复杂对象组装:如汽车、电脑、房屋等多部件组装,步骤复杂且可定制。
  2. 文档/报表生成:如HTML、PDF、Word等多格式文档的分步生成。
  3. UI表单/界面构建:如动态表单、复杂界面分步渲染。
  4. 配置对象创建:如系统配置、网络配置等参数众多且组合灵活的对象。

真实业务背景举例:

  • 电商平台订单对象包含商品、收货、支付、优惠等多个可选部件,建造者模式可灵活组装。
  • SaaS平台支持多种报表格式导出,建造者模式可统一流程、灵活扩展。
  • 游戏角色装备、技能、属性等复杂对象,建造者模式便于分步构建和扩展。

3. 优缺点分析

3.1 优点

  1. 封装性:隐藏构建细节,客户端无需关心内部实现。
  2. 灵活性:支持不同产品配置和多种表示。
  3. 可维护性:构建过程独立,便于扩展和维护。

3.2 缺点

  1. 类数量增加:每种产品/部件都需建造者类,结构复杂。
  2. 适用范围有限:只适用于部件复杂、构建过程稳定的对象。
  3. 构建过程固定:如步骤变化大,需频繁修改建造者接口。

4. 实际应用案例

  1. 订单对象组装:商品、收货、支付、优惠等分步组装。
  2. 报表/文档生成:多格式报表、文档的统一生成流程。
  3. 汽车/电脑组装:多部件、可选配置的复杂对象。
  4. UI表单构建:动态表单、复杂界面分步渲染。

5. 结构与UML类图

@startuml
package "Builder Pattern" #DDDDDD {class Computer {+ setCpu(cpu: String): void+ setRam(ram: String): void+ setStorage(storage: String): void+ setGpu(gpu: String): void}interface ComputerBuilder {+ buildCpu(): void+ buildRam(): void+ buildStorage(): void+ buildGpu(): void+ getResult(): Computer}class GamingComputerBuilder implements ComputerBuilderclass OfficeComputerBuilder implements ComputerBuilderclass ComputerEngineer {- builder: ComputerBuilder+ constructComputer(): void}ComputerBuilder <|.. GamingComputerBuilderComputerBuilder <|.. OfficeComputerBuilderComputerEngineer o-- ComputerBuilder : builderGamingComputerBuilder --> Computer : getResult()OfficeComputerBuilder --> Computer : getResult()
}
@enduml

6. 代码示例

6.1 基本结构示例

业务背景: 电脑组装业务,支持多种配置,分步构建。

// 产品类:电脑
package com.example.patterns.builder;import java.util.Objects;
import java.util.StringJoiner;public class Computer {private String cpu;private String ram;private String storage;private String gpu;public void setCpu(String cpu) { this.cpu = Objects.requireNonNull(cpu, "CPU cannot be null"); }public void setRam(String ram) { this.ram = Objects.requireNonNull(ram, "RAM cannot be null"); }public void setStorage(String storage) { this.storage = Objects.requireNonNull(storage, "Storage cannot be null"); }public void setGpu(String gpu) { this.gpu = Objects.requireNonNull(gpu, "GPU cannot be null"); }// 改进的toString方法@Overridepublic String toString() {return new StringJoiner(", ", "Computer{", "}").add("cpu='" + cpu + "'").add("ram='" + ram + "'").add("storage='" + storage + "'").add("gpu='" + gpu + "'").toString();}// 添加equals和hashCode方法,便于测试@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Computer computer = (Computer) o;return Objects.equals(cpu, computer.cpu) &&Objects.equals(ram, computer.ram) &&Objects.equals(storage, computer.storage) &&Objects.equals(gpu, computer.gpu);}@Overridepublic int hashCode() {return Objects.hash(cpu, ram, storage, gpu);}
}// 抽象建造者
public interface ComputerBuilder {void buildCpu();void buildRam();void buildStorage();void buildGpu();Computer getResult();// 添加重置方法,支持builder复用default void reset() {// 子类可以重写此方法实现重置逻辑}
}// 游戏电脑建造者
public class GamingComputerBuilder implements ComputerBuilder {private Computer computer = new Computer();@Overridepublic void buildCpu() { computer.setCpu("Intel i9-13900K"); }@Overridepublic void buildRam() { computer.setRam("32GB DDR5"); }@Overridepublic void buildStorage() { computer.setStorage("2TB NVMe SSD"); }@Overridepublic void buildGpu() { computer.setGpu("NVIDIA RTX 4090"); }@Overridepublic Computer getResult() { return computer; }@Overridepublic void reset() {computer = new Computer();}
}// 办公电脑建造者
public class OfficeComputerBuilder implements ComputerBuilder {private Computer computer = new Computer();@Overridepublic void buildCpu() { computer.setCpu("Intel i5-13600K"); }@Overridepublic void buildRam() { computer.setRam("16GB DDR4"); }@Overridepublic void buildStorage() { computer.setStorage("1TB SATA SSD"); }@Overridepublic void buildGpu() { computer.setGpu("Intel UHD Graphics"); }@Overridepublic Computer getResult() { return computer; }@Overridepublic void reset() {computer = new Computer();}
}// 组装工程师(导演类)
public class ComputerEngineer {private ComputerBuilder builder;public ComputerEngineer(ComputerBuilder builder) { this.builder = Objects.requireNonNull(builder, "Builder cannot be null"); }public void constructComputer() {builder.reset(); // 确保从干净状态开始builder.buildCpu();builder.buildRam();builder.buildStorage();builder.buildGpu();}// 添加部分构建方法,支持定制化public void constructBasicComputer() {builder.reset();builder.buildCpu();builder.buildRam();builder.buildStorage();// 不添加独显}
}// 客户端示例
public class Client {public static void main(String[] args) {try {// 创建游戏电脑ComputerBuilder gamingBuilder = new GamingComputerBuilder();ComputerEngineer gamingEngineer = new ComputerEngineer(gamingBuilder);gamingEngineer.constructComputer();Computer gamingComputer = gamingBuilder.getResult();System.out.println("Gaming Computer: " + gamingComputer);// 创建办公电脑ComputerBuilder officeBuilder = new OfficeComputerBuilder();ComputerEngineer officeEngineer = new ComputerEngineer(officeBuilder);officeEngineer.constructComputer();Computer officeComputer = officeBuilder.getResult();System.out.println("Office Computer: " + officeComputer);} catch (Exception e) {System.err.println("Failed to build computer: " + e.getMessage());}}// 总结:通过建造者模式,客户端可灵活组装不同配置电脑,便于扩展和维护。
}

6.2 实际业务场景:订单对象建造者

业务背景: 电商平台订单对象包含商品、收货、支付、优惠等多个可选部件,建造者模式可灵活组装。

// 订单对象
package com.example.patterns.builder.order;import java.math.BigDecimal;
import java.util.Objects;
import java.util.StringJoiner;public class Order {private String product;private String address;private String payment;private String discount;private BigDecimal amount; // 添加金额字段public void setProduct(String product) { this.product = product; }public void setAddress(String address) { this.address = address; }public void setPayment(String payment) { this.payment = payment; }public void setDiscount(String discount) { this.discount = discount; }public void setAmount(BigDecimal amount) { this.amount = amount; }public String getProduct() { return product; }public String getAddress() { return address; }public String getPayment() { return payment; }public String getDiscount() { return discount; }public BigDecimal getAmount() { return amount; }// 校验订单是否有效public boolean isValid() {return product != null && !product.trim().isEmpty() &&address != null && !address.trim().isEmpty() &&payment != null && !payment.trim().isEmpty() &&amount != null && amount.compareTo(BigDecimal.ZERO) > 0;}@Overridepublic String toString() {return new StringJoiner(", ", "Order{", "}").add("product='" + product + "'").add("address='" + address + "'").add("payment='" + payment + "'").add("discount='" + discount + "'").add("amount=" + amount).toString();}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Order order = (Order) o;return Objects.equals(product, order.product) &&Objects.equals(address, order.address) &&Objects.equals(payment, order.payment) &&Objects.equals(discount, order.discount) &&Objects.equals(amount, order.amount);}@Overridepublic int hashCode() {return Objects.hash(product, address, payment, discount, amount);}
}// 订单建造者
public class OrderBuilder {private Order order = new Order();public OrderBuilder product(String product) { if (product == null || product.trim().isEmpty()) {throw new IllegalArgumentException("Product cannot be null or empty");}order.setProduct(product); return this; }public OrderBuilder address(String address) { if (address == null || address.trim().isEmpty()) {throw new IllegalArgumentException("Address cannot be null or empty");}order.setAddress(address); return this; }public OrderBuilder payment(String payment) { if (payment == null || payment.trim().isEmpty()) {throw new IllegalArgumentException("Payment method cannot be null or empty");}order.setPayment(payment); return this; }public OrderBuilder discount(String discount) { // 折扣是可选的,允许nullorder.setDiscount(discount); return this; }public OrderBuilder amount(BigDecimal amount) {if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {throw new IllegalArgumentException("Amount must be positive");}order.setAmount(amount);return this;}// 便捷方法,接受double类型public OrderBuilder amount(double amount) {return amount(BigDecimal.valueOf(amount));}public Order build() { if (!order.isValid()) {throw new IllegalStateException("Order is not valid. Missing required fields.");}return order; }// 重置建造者,支持复用public OrderBuilder reset() {order = new Order();return this;}
}// 客户端示例
public class OrderClient {public static void main(String[] args) {try {// 创建完整订单Order order = new OrderBuilder().product("iPhone 15 Pro").address("上海市浦东新区").payment("微信支付").discount("满1000减100").amount(8999.00).build();System.out.println("订单信息: " + order);// 创建无折扣订单Order simpleOrder = new OrderBuilder().product("MacBook Pro").address("北京市朝阳区").payment("支付宝").amount(15999.00).build();System.out.println("简单订单: " + simpleOrder);} catch (IllegalArgumentException | IllegalStateException e) {System.err.println("订单创建失败: " + e.getMessage());}}// 总结:通过链式建造者和参数校验,订单对象组装更安全,代码简洁易维护。
}

7. 测试用例

业务背景: 验证电脑建造者和订单建造者的核心功能与多实现切换。

public class BuilderPatternTest {@Testpublic void testGamingComputer() {ComputerBuilder builder = new GamingComputerBuilder();ComputerEngineer engineer = new ComputerEngineer(builder);// 构建游戏电脑engineer.constructComputer();Computer computer = builder.getResult();// 验证游戏电脑配置String computerStr = computer.toString();assertTrue(computerStr.contains("Intel i9-13900K"));assertTrue(computerStr.contains("32GB DDR5"));assertTrue(computerStr.contains("NVIDIA RTX 4090"));}@Testpublic void testOfficeComputer() {ComputerBuilder builder = new OfficeComputerBuilder();ComputerEngineer engineer = new ComputerEngineer(builder);// 构建办公电脑engineer.constructComputer();Computer computer = builder.getResult();// 验证办公电脑配置String computerStr = computer.toString();assertTrue(computerStr.contains("Intel i5-13600K"));assertTrue(computerStr.contains("16GB DDR4"));assertTrue(computerStr.contains("Intel UHD Graphics"));}@Testpublic void testOrderBuilder() {// 测试链式建造者Order order = new OrderBuilder().product("iPhone 15 Pro").address("上海市浦东新区").payment("微信支付").discount("满1000减100").amount(8999.00).build();assertEquals("iPhone 15 Pro", order.getProduct());assertEquals("微信支付", order.getPayment());assertTrue(order.isValid());}@Testpublic void testDifferentBuilders() {// 测试不同建造者产生不同结果ComputerBuilder gamingBuilder = new GamingComputerBuilder();ComputerBuilder officeBuilder = new OfficeComputerBuilder();ComputerEngineer engineer1 = new ComputerEngineer(gamingBuilder);ComputerEngineer engineer2 = new ComputerEngineer(officeBuilder);engineer1.constructComputer();engineer2.constructComputer();Computer gamingComputer = gamingBuilder.getResult();Computer officeComputer = officeBuilder.getResult();// 验证两台电脑配置不同assertNotEquals(gamingComputer.toString(), officeComputer.toString());}
}

8. 常见误区与反例

  • 误区1 :导演类(Director)过度复杂。
    如构建逻辑过多应拆分,避免导演类膨胀。
  • 误区2 :建造者接口设计不清晰。
    应保证步骤明确、职责单一。
  • 反例 :客户端直接操作产品部件。
    应通过建造者接口组装,避免高耦合。

9. 最佳实践

  1. 接口设计 :建造者接口要简洁,步骤明确,便于扩展和维护。
  2. 工厂与建造者结合 :复杂对象可结合工厂方法和建造者模式,提升灵活性。
  3. 链式调用 :对于参数较多的对象,推荐链式建造者,提升可读性。
  4. 异常与校验 :建造过程应妥善处理参数校验和异常。
  5. 文档和UML同步 :保持文档、UML和代码示例一致,便于团队协作。

10. 参考资料与延伸阅读

  • 《设计模式:可复用面向对象软件的基础》GoF
  • Effective Java(中文版)
  • https://refactoringguru.cn/design-patterns/builder
  • https://www.baeldung.com/java-builder-pattern

本文为设计模式系列第7篇,后续每篇将聚焦一个设计模式或设计原则,深入讲解实现与应用,敬请关注。

相关文章:

设计模式系列(07):建造者模式(Builder)

本文为设计模式系列第7篇&#xff0c;聚焦创建型模式中的建造者模式&#xff0c;涵盖定义、原理、实际业务场景、优缺点、最佳实践及详细代码示例&#xff0c;适合系统学习与实战应用。 目录 1. 模式概述2. 使用场景3. 优缺点分析4. 实际应用案例5. 结构与UML类图6. 代码示例7…...

Maven 项目中集成数据库文档生成工具

在 Maven 项目中&#xff0c;可以通过集成 数据库文档生成工具&#xff08;如 screw-maven-plugin、mybatis-generator 或 liquibase&#xff09;来自动生成数据库文档。以下是使用 screw-maven-plugin&#xff08;推荐&#xff09;的完整配置步骤&#xff1a; 1. 添加插件配置…...

聊聊Tomato Architecture

序 本文主要研究一下Tomato Architecture Clean/Onion/Hexagonal/Ports&Adapters Architectures Clean Architecture clean architecture定义了四层结构&#xff0c;最内层是entities(enterprise business rules)&#xff0c;再往外是use cases(application business ru…...

小白的进阶之路系列之十二----人工智能从初步到精通pytorch综合运用的讲解第五部分

在本笔记本中,我们将针对Fashion-MNIST数据集训练LeNet-5的变体。Fashion-MNIST是一组描绘各种服装的图像瓦片,有十个类别标签表明所描绘的服装类型。 # PyTorch model and training necessities import torch import torch.nn as nn import torch.nn.functional as F impor…...

Java并发编程实战 Day 6:Future与异步编程模型

【Java并发编程实战 Day 6】Future与异步编程模型 在今天的课程中&#xff0c;我们将深入学习Java中的Future与异步编程模型。这是为期30天的"Java并发编程实战"系列的第6天。 理论基础 Future接口 Future接口是Java提供的用于表示异步计算的结果。它提供了以下方…...

.NET Core 中预防跨网站请求伪造 (XSRFCSRF) 攻击

.NET Core 中预防跨网站请求伪造 (XSRF/CSRF) 攻击 在如今的网络环境中&#xff0c;安全问题一直是开发者们不可忽视的重要方面。跨网站请求伪造&#xff08;Cross-Site Request Forgery&#xff0c;简称 CSRF&#xff09;就是一种常见且具有威胁性的网络攻击方式。攻击者通过…...

MFC Resource.h 文件详解与修改指南

MFC Resource.h 文件详解与修改指南 Resource.h 是 Visual C 和 MFC 项目中用于集中管理资源标识符&#xff08;Resource ID&#xff09;的头文件。它由 Visual Studio 的资源编辑器自动生成并维护&#xff0c;也可以手动编辑。理解并合理使用该文件对于管理对话框、控件、菜单…...

2025年06月03日Github流行趋势

项目名称&#xff1a;onlook 项目地址url&#xff1a;https://github.com/onlook-dev/onlook项目语言&#xff1a;TypeScript历史star数&#xff1a;12871今日star数&#xff1a;624项目维护者&#xff1a;Kitenite, drfarrell, spartan-vutrannguyen, apps/devin-ai-integrati…...

AI视频编码器(0.4.3) 调试训练bug——使用timm SoftTargetCrossEntropy时出现loss inf

检查过程 进入loss内部实现: > /root/miniconda3/envs/UMT2/lib/python3.9/site-packages/timm/loss/cross_entropy.py(35...

【数据分析】基于Cox模型的R语言实现生存分析与生物标志物风险评估

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理生存分析画图输出图片其他标记物的分析总结系统信息介绍 分析生存数据与多种生物标志物之间的关系。它通过Cox比例风险模型来评估不同生物标志物…...

使用nginx配置反向代理,负载均衡

首先啥叫反向代理 咋配置呢&#xff0c;那当然是在nginx目录下改conf文件了 具体咋改呢&#xff0c;那就新增一个新的server配置&#xff0c;然后在location里新增你想代理的服务器 实际上负载均衡也就是根据反向代理的思路来的&#xff0c;如下所示 配置的话实际上也与上…...

从 iPhone 备份照片: 保存iPhone图片的5种方法

随着智能手机越来越融入我们的生活&#xff0c;我们的照片已成为我们设备上最有价值的数据形式之一。然而&#xff0c;iPhone内部存储空间仍然有限&#xff0c;因此我们需要将iPhone中的照片备份到另一个地方&#xff0c;以释放空间并确保珍贵的图像记忆的安全。阅读本指南&…...

Spring Ai 从Demo到搭建套壳项目(一)初识与实现与deepseek对话模式

前言 为什么说Java长青&#xff0c;主要是因为其生态圈完善&#xff0c;Spring又做了一款脚手架&#xff0c;把对接各个LLM厂商的sdk做了一遍&#xff0c;形成一系列的spring-ai-starter-** 的依赖。 目前为止版本去到1.0.0.M6&#xff0c;golang跟不上了吧&#xff0c; Make …...

快速上手pytest

1. pytest的基础 1.1 什么是pytest pytest 是 python 中的一个测试框架&#xff0c;用于编写简洁、可扩展的测试代码&#xff0c;帮助开发者验证结果是否与预期相符。 python 中有很多的测试框架&#xff0c;那我们为什么要学习 pytest 呢&#xff1f; pytest 的优势&…...

设备驱动与文件系统:02 键盘

操作系统中键盘驱动的讲解 在这一讲中&#xff0c;我将为大家讲解键盘相关内容。从上一讲开始&#xff0c;我们进入了操作系统第四个部分的学习&#xff0c;也就是操作系统对设备的驱动与管理。 上一讲我们探讨的是显示器&#xff0c;并且提到&#xff0c;一个终端设备是由显示…...

matlab分布式电源接入对配电网的影响

MATLAB仿真的分布式电源接入对于配电网的影响&#xff0c;潮流计算加了固定的pq 分布式电源接入对配电网的影响/bustypes.m , 1004 分布式电源接入对配电网的影响/case34.m , 5385 分布式电源接入对配电网的影响/case9.m , 1366 分布式电源接入对配电网的影响/dSbus_dV.m , 14…...

前端ul-image的src接收base64快捷写法

前端ul-image的src接收base64快捷写法 data:image/png;base64,你的base64数据 注意如果是jpg就改成jpg&#xff0c;中间的逗号格式要注意&#xff0c;/注意不要反了 假设后端返回的detail中的url已经是base64格式&#xff0c;下面是示例 <u-image height"120rpx"…...

交通违法拍照数据集,可识别接打电话,不系安全带的行为,支持YOLO,COCO JSON,VOC XML格式的标注数据集 最高正确识别率可达88.6%

交通违法拍照数据集 数据集概述 数据来源&#xff1a;交通监控摄像头、执法记录仪、公开数据集数据类型&#xff1a;图像、视频、元数据&#xff08;时间、地点、车辆信息&#xff09;违法类型标注&#xff1a;接打电话、未系安全带 数据采集与标注方法 采集设备&#xff1…...

Qt OpenGL 3D 编程入门

Qt 提供了强大的 OpenGL 集成功能&#xff0c;使得在 Qt 应用中实现 3D 图形变得更加简单。以下是使用 Qt 进行 OpenGL 3D 编程的基础知识。 1. 环境配置 创建 Qt 项目 新建 Qt Widgets Application 项目 在 .pro 文件中添加 OpenGL 模块&#xff1a; qmake QT co…...

性能优化 - 工具篇:基准测试 JMH

文章目录 Pre引言1. JMH 简介2. JMH 执行流程详解3. 关键注解详解3.1 Warmup3.2 Measurement3.3 BenchmarkMode3.4 OutputTimeUnit3.5 Fork3.6 Threads3.7 Group 与 GroupThreads3.8 State3.9 Setup 与 TearDown3.10 Param3.11 CompilerControl 4. 示例代码与分析4.1 关键点解读…...

Ubuntu 中安装 PostgreSQL 及常规操作指南

目录 一、安装 PostgreSQL 最新版本安装&#xff08;推荐&#xff09; 安装特定版本&#xff08;如 14&#xff09; 二、基本服务管理 三、连接数据库 四、常规数据库操作 1. 用户与权限管理 2. 数据库管理 3. 表操作 4. 数据操作 五、常用 psql 元命令 六、备份与恢…...

Nginx网站服务:从入门到LNMP架构实战

&#x1f3e1;作者主页&#xff1a;点击&#xff01; Nginx-从零开始的服务器之旅专栏&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年5月30日14点22分 前言 说起Web服务器&#xff0c…...

Java面试八股--08-数据结构和算法篇

1、怎么理解时间复杂度和空间复杂度 时间复杂度和空间复杂度一般是针对算法而言&#xff0c;是衡量一个算法是否高效的重要标准。先纠正一个误区&#xff0c;时间复杂度并不是算法执行的时间&#xff0c;在纠正一个误区&#xff0c;算法不单单指冒泡排序之类的&#xff0c;一个…...

Java面试八股--06-Linux篇

目录 一、Git 1、工作中git开发使用流程&#xff08;命令版本描述&#xff09; 2.Reset与Rebase&#xff0c;Pull与Fetch的区别 3、git merge和git rebase的区别 4、git如何解决代码冲突 5、项目开发时git分支情况 二、Linux 1、Linux常用的命令 2、如何查看测试项目的…...

Ajax技术分析方法全解:从基础到企业级实践(2025最新版)

引言 Ajax技术自2005年正式命名以来,已支撑全球83%的Web应用实现异步交互。2025年最新数据显示,单页面应用(SPA)的Ajax请求密度已达日均120亿次/应用。本文将系统化解析Ajax分析方法论,涵盖从基础原理到企业级工程实践的完整技术栈。 一、Ajax技术架构解构 1.1 核心组件…...

Unity 性能优化终极指南 — GameObject 篇

&#x1f3af; Unity 性能优化终极指南 — GameObject 方法篇 &#x1f9e9; GameObject 是什么&#xff1f;—— Unity世界的核心实体 GameObject 是 Unity 引擎中最核心、最基础的构建单元。它代表了场景中的一个实体对象&#xff0c;可以是一个角色、一个UI元素、一盏灯光、…...

dvwa7——SQL Injection

LOW&#xff1a; f12打开hackbar 一&#xff1a;判断注入类型 输入id1报错 闭合单引号 &#xff0c;页面恢复正常 所以为单引号字符型 二&#xff1a;开始攻击 1.判断列数 ?id1 order by 2-- 到3的时候开始报错&#xff0c;所以一共两列 2.爆回显位置 ?id-1 union s…...

Spring AI 项目实战(四):Spring AI + DeepSeek 超参数优化——智能化机器学习平台(附完整源码)

系列文章 序号文章名称1Spring AI 项目实战(一):Spring AI 核心模块入门2Spring AI 项目实战(二):Spring AI + DeepSeek 深度实战(附完整源码)3Spring AI 项目实战(三):Spring AI + DeepSeek 打造智能客服系统(附完整源码)4Spring AI 项目实战(四):Spring AI +…...

Axure疑难杂症:中继器图片替换功能优化(支持修改已有记录-玩转中继器)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 案例视频: 中继器图片替换功能优化 课程主题:中继器图片替换功能优化(支持修改已有记录) 主要内…...

sqlite3 命令行工具详细介绍

一、启动与退出 启动数据库连接 sqlite3 [database_file] # 打开/创建数据库文件&#xff08;如 test.db&#xff09; sqlite3 # 启动临时内存数据库 (:memory:) sqlite3 :memory: # 显式启动内存数据库文件不存在时自动创建不指定文件名则使用临时内…...