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

设计模式——简单工厂模式(创建型)

摘要

本文主要介绍了简单工厂模式,包括其定义、结构、实现方式、适用场景、实战示例以及思考。简单工厂模式是一种创建型设计模式,通过工厂类根据参数决定创建哪一种产品类的实例,封装了对象创建的细节,使客户端无需关心具体类的创建逻辑。文章详细阐述了其角色组成、类图、时序图,探讨了两种常见的实现方式,分析了适合与不适合的场景,并提供了 Spring 项目和可插拔式策略工厂的实战示例。最后,还提出了支持 SPI 机制、注解标记策略名称和配置中心切换策略等思考方向。

1. 简单工厂模式定义

简单工厂模式是一种创建型设计模式,它通过一个工厂类根据传入的参数决定创建哪一种产品类的实例

核心要点:

  • 核心角色:工厂类(Factory)
    • 负责创建产品对象。
    • 客户端只需传递参数,不关心具体类的创建逻辑。
  • 目标:封装对象创建的细节,将创建对象的逻辑从使用者中分离出来。

组成结构:

角色

说明

Product

抽象产品类(接口或抽象类)

ConcreteProduct

具体产品类,实现 Product 接口

Factory

工厂类,包含创建产品对象的静态方法

Client

客户端,调用工厂方法获取产品对象并使用它

2. 简单工厂模式结构

2.1. 简单工厂类图

2.2. 简单工厂时序图

3. 简单工厂模式实现方式

实现方式主要分为两种:

实现方式

描述

1. 静态方法创建(常用)

工厂方法是 static的,客户端直接通过类名调用,无需实例化工厂

2. 实例方法创建(灵活)

工厂需要先实例化,再调用方法创建对象,适合支持不同配置或依赖注入

3.1. ✅ 实现方式 1:静态方法创建产品对象(最常见)

// 抽象产品
public interface Product {void doWork();
}// 具体产品 A
public class ProductA implements Product {public void doWork() {System.out.println("产品A正在工作");}
}// 具体产品 B
public class ProductB implements Product {public void doWork() {System.out.println("产品B正在工作");}
}// 简单工厂
public class SimpleFactory {public static Product createProduct(String type) {if ("A".equalsIgnoreCase(type)) {return new ProductA();} else if ("B".equalsIgnoreCase(type)) {return new ProductB();}throw new IllegalArgumentException("不支持的产品类型:" + type);}
}// 客户端
public class Client {public static void main(String[] args) {Product product = SimpleFactory.createProduct("A");product.doWork();  // 输出:产品A正在工作}
}

3.2. ✅ 实现方式 2:使用工厂实例方法创建产品对象(便于配置)

// 工厂类
public class SimpleFactory {public Product createProduct(String type) {if ("A".equalsIgnoreCase(type)) {return new ProductA();} else if ("B".equalsIgnoreCase(type)) {return new ProductB();}throw new IllegalArgumentException("不支持的产品类型:" + type);}
}// 客户端
public class Client {public static void main(String[] args) {SimpleFactory factory = new SimpleFactory();Product product = factory.createProduct("B");product.doWork();  // 输出:产品B正在工作}
}

3.3. 🧠 简单工厂实现总结

对比项

静态方法工厂

实例方法工厂

是否需要工厂对象

是否支持配置

较差

支持通过构造函数注入

使用场景

通用工厂、工具类

多配置或状态的工厂类

4. 简单工厂模式适合场景

4.1. ✅ 适合的场景(推荐使用简单工厂):

场景描述

原因

产品种类有限,变化不大

工厂中 if-else/switch 可维护性尚可,结构清晰

客户端不关心对象创建细节

封装了具体类的创建过程,减少耦合

多个子类实现一个接口/抽象类,客户端按条件选择创建哪一个

工厂根据传参决定具体返回哪个子类

代码结构相对简单,开发阶段处于初期

不想引入复杂设计,简单工厂最直观、易懂

希望集中管理对象创建

比 scattered new 更好维护和修改

📌 示例:日志输出、支付方式选择、消息发送器选择(短信/邮件/微信)等。

4.2. ❌ 不适合的场景(不推荐使用简单工厂):

场景描述

问题

1. 产品种类经常变化或增加

每新增一个产品都要修改工厂类,违反“开放封闭原则”

2. 产品种类太多,逻辑复杂

工厂类会膨胀成一个巨大类,不利于维护

3. 需要依赖注入、构造过程复杂

无法灵活注入依赖,缺乏拓展性

4. 对产品创建过程有定制要求

不能对不同产品的创建过程进行细粒度控制

5. 分布式、多模块开发场景

集中式工厂类会形成模块间强依赖,不利于解耦和部署

📌 示例:大型系统中涉及复杂对象生命周期控制的场景(如数据库连接池、线程池、配置驱动类等)。

4.3. ✅ 使用建议总结:

建议

原因

✔ 使用于产品稳定、变化少的小规模项目

简单、易维护

❌ 不建议用于大型、产品体系复杂的项目

违背开闭原则、扩展性差

👉 可结合工厂方法模式或抽象工厂模式演进

更好地支持扩展与解耦

5. 简单工厂模式实战示例

5.1. Spring项目中简单工厂示例

我们以“消息发送系统”为例,演示如何使用简单工厂根据输入创建不同的消息发送器(如短信、邮件、微信等)。

5.1.1. ✅ 场景目标

在 Spring 项目中,根据配置选择不同的消息发送器进行发送,如:

  • 配置为 sms:发送短信;
  • 配置为 email:发送邮件;
  • 配置为 wechat:发送微信。

5.1.2. ✅ 定义统一接口

public interface MessageSender {void send(String message);
}

5.1.3. ✅ 三种发送器的实现类

@Component("smsSender")
public class SmsSender implements MessageSender {public void send(String message) {System.out.println("发送短信:" + message);}
}@Component("emailSender")
public class EmailSender implements MessageSender {public void send(String message) {System.out.println("发送邮件:" + message);}
}@Component("wechatSender")
public class WechatSender implements MessageSender {public void send(String message) {System.out.println("发送微信:" + message);}
}

5.1.4. ✅ 简单工厂类(使用 Spring 容器管理)

@Component
public class MessageSenderFactory {@Autowiredprivate ApplicationContext applicationContext;public MessageSender createSender(String type) {switch (type.toLowerCase()) {case "sms":return applicationContext.getBean("smsSender", MessageSender.class);case "email":return applicationContext.getBean("emailSender", MessageSender.class);case "wechat":return applicationContext.getBean("wechatSender", MessageSender.class);default:throw new IllegalArgumentException("不支持的消息类型:" + type);}}
}

5.1.5. ✅ 使用示例(Controller 或 Service)

@Service
public class NotifyService {@Autowiredprivate MessageSenderFactory messageSenderFactory;// 模拟从配置文件或参数中读取类型@Value("${message.type:sms}")private String messageType;public void notifyUser(String content) {MessageSender sender = messageSenderFactory.createSender(messageType);sender.send(content);}
}

5.1.6. ✅ 应用入口(或 Controller)

@RestController
public class MessageController {@Autowiredprivate NotifyService notifyService;@GetMapping("/send")public String send(@RequestParam String msg) {notifyService.notifyUser(msg);return "消息已发送";}
}

5.1.7. ✅ application.yml 示例配置

message:type: email

5.1.8. ✅ 简单工厂示例总结

设计点

说明

工厂类

MessageSenderFactory是核心简单工厂类

接口统一

所有发送器实现 MessageSender接口

解耦

NotifyService不依赖具体实现类,依赖工厂和接口

可配置

支持通过配置控制行为,符合 Spring 项目实践

5.2. 可插拔式策略工厂(使用 SPI、Map 注册) 示例

下面是一个 可插拔式策略工厂(基于 Map 注册 + Spring 管理) 的完整示例,非常适用于需要动态切换、按配置扩展的业务策略类,比如:消息发送、支付渠道、风控策略、营销活动处理器等实现一种 灵活可扩展 的策略选择工厂,每个策略自动注册 到一个 Map<String, Strategy> 中,不需要手动 switch-case

5.2.1. ✅ 定义统一接口(策略接口)

public interface MessageSender {String type(); // 每个实现类提供自己的标识void send(String message);
}

5.2.2. ✅ 三种发送实现类(@Component 自动注入)

@Component
public class SmsSender implements MessageSender {public String type() {return "sms";}public void send(String message) {System.out.println("【短信】发送:" + message);}
}@Component
public class EmailSender implements MessageSender {public String type() {return "email";}public void send(String message) {System.out.println("【邮件】发送:" + message);}
}@Component
public class WechatSender implements MessageSender {public String type() {return "wechat";}public void send(String message) {System.out.println("【微信】发送:" + message);}
}

5.2.3. ✅ 自动注册策略工厂(Map 注册方式)

@Component
public class MessageSenderFactory {private final Map<String, MessageSender> senderMap = new HashMap<>();// Spring 会自动注入所有 MessageSender 的实现类@Autowiredpublic MessageSenderFactory(List<MessageSender> senders) {for (MessageSender sender : senders) {senderMap.put(sender.type(), sender);}}public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("不支持的消息类型: " + type);}return sender;}
}

5.2.4. ✅ 使用策略工厂的服务类

@Service
public class NotifyService {@Autowiredprivate MessageSenderFactory messageSenderFactory;public void notifyUser(String type, String content) {MessageSender sender = messageSenderFactory.getSender(type);sender.send(content);}
}

5.2.5. ✅ Controller 示例

@RestController
@RequestMapping("/msg")
public class MessageController {@Autowiredprivate NotifyService notifyService;@GetMapping("/send")public String send(@RequestParam String type, @RequestParam String msg) {notifyService.notifyUser(type, msg);return "消息已发送";}
}

5.2.6. ✅ 示例调用

请求:

GET /msg/send?type=sms&msg=Hello_SMS
GET /msg/send?type=email&msg=Hello_Email
GET /msg/send?type=wechat&msg=Hello_WeChat

输出:

【短信】发送:Hello_SMS
【邮件】发送:Hello_Email
【微信】发送:Hello_WeChat

5.2.7. 🚀 可插拔式策略工厂优势总结

特性

说明

可插拔

新增一个实现类即可生效,无需改动工厂或业务代码

高扩展性

支持多策略、动态注册

易维护

摆脱 if-else / switch

Spring友好

利用 Spring 自动注入 & 生命周期

5.3. 🔌 简单工厂设计模式可拓展方向(高级)

  • 支持 SPI 机制(META-INF/services),用于插件式加载(可用于 jar 扩展、热插拔);
  • 支持 注解标记策略名称
  • 支持配置中心切换策略,如使用 @Value("${message.type}") 注入默认策略。

6. 简单工厂模式思考

6.1. 支持 SPI 机制(META-INF/services),用于插件式加载(可用于 jar 扩展、热插拔);

下面是一个完整的 基于 Java SPI(Service Provider Interface)机制的插件式策略工厂实现示例,它常用于实现模块解耦、可插拔、动态扩展功能。以“消息发送策略”为例,不使用 Spring 注解扫描,而是通过 SPI 机制动态加载所有实现类。

6.1.1. 🧩 定义 SPI 接口(策略接口)

// src/main/java/com/example/spi/MessageSender.java
package com.example.spi;public interface MessageSender {String type();             // 返回类型标识符void send(String message); // 发送消息逻辑
}

6.1.2. 📝 实现类(位于独立模块 / jar 中)

// src/main/java/com/example/impl/SmsSender.java
package com.example.impl;import com.example.spi.MessageSender;public class SmsSender implements MessageSender {@Overridepublic String type() {return "sms";}@Overridepublic void send(String message) {System.out.println("【短信发送】" + message);}
}

6.1.3. 🗂️ 3SPI 注册文件

在 jar 包中的 resources/META-INF/services/ 路径下创建文件:

META-INF/services/com.example.spi.MessageSender

内容如下(每一行一个实现类的全限定名):

com.example.impl.SmsSender

✅ 当你有多个策略实现时,添加多行即可,如:

com.example.impl.SmsSender
com.example.impl.EmailSender

6.1.4. ⚙️ 工厂类:动态加载所有策略实现

package com.example.factory;import com.example.spi.MessageSender;import java.util.*;public class MessageSenderFactory {private final Map<String, MessageSender> senderMap = new HashMap<>();public MessageSenderFactory() {// 加载spi接口所有接口实现类ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);for (MessageSender sender : loader) {senderMap.put(sender.type(), sender);}}public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("不支持的消息类型: " + type);}return sender;}
}
ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);

Java 原生 SPI(Service Provider Interface)机制 的核心。它的底层原理其实并不复杂,是通过读取类路径下的 META-INF/services/ 目录下的文件,并利用反射机制加载实现类。以下是 ServiceLoader 的简化执行逻辑:

// 从 META-INF/services/ 下查找接口对应的配置文件
Enumeration<URL> urls = classLoader.getResources("META-INF/services/" + serviceClass.getName());// 读取文件中的类名
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String implClassName = reader.readLine();// 通过反射实例化对象
Class<?> clazz = Class.forName(implClassName);
Object instance = clazz.newInstance();

每个实现类会被加载成一个对象,并返回一个延迟加载的迭代器。

6.1.5. 🧪 使用示例(main 方法中调用)

public class Main {public static void main(String[] args) {MessageSenderFactory factory = new MessageSenderFactory();MessageSender sender = factory.getSender("sms");sender.send("Hello SPI!");}
}

6.1.6. 🎯 整体结构

src/
├── main/
│   ├── java/
│   │   ├── com.example.spi/MessageSender.java
│   │   ├── com.example.impl/SmsSender.java
│   │   └── com.example.factory/MessageSenderFactory.java
│   └── resources/
│       └── META-INF/services/com.example.spi.MessageSender

6.1.7. 📌 SPI 模式的优点

优点

说明

插件化、低耦合

实现类可来自外部 jar 包,主程序不需修改

动态扩展能力强

实现类可以热插拔(配合 OSGi、模块系统)

框架内部广泛使用

Dubbo、JDBC、Spring Boot 自动配置(spring.factories

6.1.8. 🚫 注意事项

  • SPI 默认是懒加载 + 非线程安全(如有并发需求请做好缓存);
  • IDE 无法提示类名(注册文件中填写错误 IDE 不会报警);
  • 多个 jar 中存在相同接口实现可能冲突;
  • 如果你使用的是 SpringBoot,推荐改用 Spring 的 SPI(如 spring.factoriesspring.factoriesspring/org.springframework.boot.autoconfigure.EnableAutoConfiguration)机制配合。

6.2. 支持注解标记策略名称

策略模式 中,支持通过 注解 来标记每个策略的名称,可以极大提升策略类的可读性和扩展性,尤其适用于 SPI、Map 注册、Spring 容器注册等场景。

  • 多个 MessageSender 策略实现类
  • 每个类通过注解标记其类型名称(如 @MessageType("sms")
  • 使用 Map 注册所有策略,支持动态获取

6.2.1. ✅ 定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MessageType {String value();  // 策略类型标识
}

6.2.2. ✅ 定义接口

public interface MessageSender {void send(String message);
}

6.2.3. ✅ 实现策略类 + 注解标记

@MessageType("sms")
public class SmsSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("发送短信: " + message);}
}@MessageType("email")
public class EmailSender implements MessageSender {@Overridepublic void send(String message) {System.out.println("发送邮件: " + message);}
}

6.2.4. ✅ 自动注册工厂类(注解扫描)

public class MessageSenderFactory {private final Map<String, MessageSender> senderMap = new HashMap<>();public MessageSenderFactory() {// SPI 加载所有 MessageSender 实现类ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);for (MessageSender sender : loader) {MessageType annotation = sender.getClass().getAnnotation(MessageType.class);if (annotation != null) {senderMap.put(annotation.value(), sender);}}}public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("不支持的消息类型: " + type);}return sender;}
}

6.2.5. ✅ 使用示例

public class Main {public static void main(String[] args) {MessageSenderFactory factory = new MessageSenderFactory();factory.getSender("sms").send("验证码123456");factory.getSender("email").send("欢迎邮件");}
}

6.2.6. ✅ 优点

  • 注解直观标记类型,无需硬编码注册
  • 扩展一个新策略只需添加一个类并标注注解
  • ServiceLoader/Spring 配合使用,支持动态发现

6.3. 支持配置中心切换策略,如使用 @Value("${message.type}") 注入默认策略。

你希望实现一种 支持配置中心动态切换策略 的能力,例如通过配置中心的属性(如 Nacos、Apollo、Spring Cloud Config 等)控制当前策略的类型:实现思路(Spring + 注解 + Map 注册 + 配置驱动)

6.3.1. ✅ 定义策略注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MessageType {String value();
}

6.3.2. ✅ 定义通用接口

public interface MessageSender {void send(String message);
}

6.3.3. ✅ 多个策略实现类

@Component
@MessageType("sms")
public class SmsSender implements MessageSender {public void send(String message) {System.out.println("发送短信: " + message);}
}@Component
@MessageType("email")
public class EmailSender implements MessageSender {public void send(String message) {System.out.println("发送邮件: " + message);}
}

6.3.4. ✅ 策略工厂(Spring 容器自动注册 + 配置驱动)

@Component
public class MessageSenderFactory implements InitializingBean {@Value("${message.type:email}")  // 默认email,可由配置中心动态更新private String defaultType;@Autowiredprivate List<MessageSender> senderList;private final Map<String, MessageSender> senderMap = new HashMap<>();@Overridepublic void afterPropertiesSet() {for (MessageSender sender : senderList) {MessageType annotation = sender.getClass().getAnnotation(MessageType.class);if (annotation != null) {senderMap.put(annotation.value(), sender);}}}/** 如果使用默认方式,采用的就是nacos 配置策略*/public MessageSender getSender() {MessageSender sender = senderMap.get(defaultType);if (sender == null) {throw new IllegalArgumentException("未找到对应的策略类型: " + defaultType);}return sender;}/** 使用入参对应的策略*/public MessageSender getSender(String type) {MessageSender sender = senderMap.get(type);if (sender == null) {throw new IllegalArgumentException("未找到对应的策略类型: " + type);}return sender;}
}

6.3.5. ✅ 使用示例

@RestController
public class MessageController {@Autowiredprivate MessageSenderFactory factory;/** 如果使用默认方式,采用的就是nacos配置策略*/@GetMapping("/send")public String send(@RequestParam String msg) {factory.getSender().send(msg);return "消息已发送";}/** 如果使用入参对应方式发送*/@GetMapping("/email/send")public String send(@RequestParam String sendType, @RequestParam String msg) {factory.getSender(sendType).send(msg);return "消息已发送";}
}

6.3.6. ✅ 配置变更如何生效(支持热刷新)?

如果你使用配置中心(如 Nacos),你还可以让 @Value("${message.type}") 自动刷新:

@RefreshScope
@Component
public class MessageSenderFactory implements InitializingBean {...
}

这样,在 Nacos 修改 message.type=emailsms,会实时切换策略。

6.3.7. ✅ 总结优势

特性

实现方式

策略标识

注解 @MessageType

扫描所有策略实现

Spring 注入 List<>

配置驱动选择策略

@Value("${message.type}")

动态刷新策略选择

@RefreshScope+ Nacos

6.4. 使用SPI 机制(META-INF/services),用于插件式加载(可用于 jar 扩展、热插拔);与普通简单工厂方法实现区别?

使用 SPI 机制(META-INF/services普通简单工厂模式 都是实现“根据类型返回不同实现类”的策略工厂思想,但它们在实现机制、应用场景和扩展性上有很大不同。以下是详细对比和说明:

6.4.1. 🔍 核心区别对比表

项目

SPI机制(ServiceLoader

简单工厂模式(自己注册/Map)

加载方式

JDK 内置 ServiceLoader

自动发现实现类

自己手动注册,或用注解 + Spring 扫描

实现类注册位置

META-INF/services/接口全类名

代码中通过 Map<String, Object>

是否依赖容器(如Spring)

否(纯 JDK 原生机制)

是(通常依赖 Spring 注入、配置)

扩展性(插件支持)

强,可动态加入 jar 包扩展实现

弱,必须修改代码或重启注册逻辑

热插拔能力

支持(添加 jar 即生效,重启可用)

不支持,必须重启或改代码

类型标识方式

实现类自己定义类型(如 type()方法)

通常用注解或手动 put(key, impl)

适合插件机制

✅ 非常适合

❌ 不适合

使用门槛

中(需要手动写 SPI 配置文件)

低(代码中实现注册)

6.4.2. 🧩 SPI机制(JDK ServiceLoader)工作原理

  • 你要做的:
    1. 定义一个接口,比如:MessageSender
    2. 实现多个类,比如:EmailSenderSmsSender 并实现该接口
    3. resources/META-INF/services/com.example.spi.MessageSender 文件中列出所有实现类的 全限定类名
com.example.sender.EmailSender
com.example.sender.SmsSender
  • 运行时使用 ServiceLoader 自动扫描并实例化:
ServiceLoader<MessageSender> loader = ServiceLoader.load(MessageSender.class);
for (MessageSender sender : loader) {senderMap.put(sender.type(), sender);
}

📦 实现类可以是外部 jar 包,插件式部署。无需改主程序代码。

6.4.3. 🛠️ 简单工厂模式实现方式

实现逻辑通常在代码里“硬编码”注册每种类型与实现类的关系:

public class SenderFactory {private static final Map<String, MessageSender> map = new HashMap<>();static {map.put("email", new EmailSender());map.put("sms", new SmsSender());}public static MessageSender getSender(String type) {return map.get(type);}
}

🧱 实现类和工厂强耦合,新增类型时,必须改代码、重编、重新部署。

6.4.4. 🚦 使用场景对比

场景

建议使用方式

插件式架构,第三方扩展点

✅ 使用 SPI

配置中心控制策略选择

✅ 使用 Spring + Map

内部服务、类型少且固定

✅ 简单工厂

支持 jar 级别热插拔、可拔插插件

✅ SPI + jar 模块化

多环境配置动态选择实现

✅ Spring 结合 @Value

7. 博文参考

  • 1. 简单工厂模式( Simple Factory Pattern ) — Graphic Design Patterns
  • 创建型 - 简单工厂(Simple Factory) | Java 全栈知识体系

相关文章:

设计模式——简单工厂模式(创建型)

摘要 本文主要介绍了简单工厂模式&#xff0c;包括其定义、结构、实现方式、适用场景、实战示例以及思考。简单工厂模式是一种创建型设计模式&#xff0c;通过工厂类根据参数决定创建哪一种产品类的实例&#xff0c;封装了对象创建的细节&#xff0c;使客户端无需关心具体类的…...

qt ubuntu 20.04 交叉编译

一、交叉编译环境搭建 1.下载交叉编译工具链&#xff1a;https://developer.arm.com/downloads/-/gnu-a 可以根据自己需要下载对应版本&#xff0c;当前最新版本是10.3, 笔者使用10.3编译后的glibc.so版本太高&#xff08;glibc_2.3.3, glibc_2.3.4, glibc_2.3.5&#xff09;…...

java中cocurrent包常用的集合类操作

文章目录 前置ConcurrentHashMapCopyOnWriteArrayList/CopyOnWriteArraySet 前置 常规的集合类&#xff0c;比如 ArrayList&#xff0c;HashMap 当作为多线程下共享的变量时候&#xff0c;操作它们时会涉及线程安全的问题 ConcurrentHashMap 适合&#xff1a;需要频繁读写的…...

晶振频率稳定性:5G 基站与航天设备的核心竞争力

在当今科技飞速发展的时代&#xff0c;电子设备的性能和可靠性至关重要。晶振作为电子设备中的核心部件&#xff0c;为系统提供精确的时间和频率基准。晶振的频率稳定性直接影响着设备的整体性能&#xff0c;从日常生活中广泛使用的智能手机、智能穿戴设备&#xff0c;到对精度…...

基于python脚本进行Maxwell自动化仿真

本文为博主进行Maxwell自动化研究过程的学习记录&#xff0c;同时对Maxwell自动化脚本&#xff08;pythonIron&#xff09;实现方法进行分享。 文章目录 脚本使用方法脚本录制与查看常用脚本代码通用开头定义项目调整设计变量软件内对应位置脚本 设置求解器软件内对应位置脚本…...

Blueprints - List View Widget

一些学习笔记归档&#xff1b; 需要读取动态数据把多个条目显示在UI上的时候&#xff0c;可能用到List View组件&#xff1b;假如有Widget要使用在List View中&#xff0c;此Widget需要继承相关接口&#xff1a; 这样就能在List View控件中选择已经继承接口的Widget组件了&…...

docker-compose搭建prometheus以及grafana

1. 什么是 Prometheus&#xff1f; Prometheus 是一个开源的系统监控和告警工具&#xff0c;由 SoundCloud 于 2012 年开始开发&#xff0c;现为 CNCF&#xff08;Cloud Native Computing Foundation&#xff09;项目之一。它特别适合云原生环境和容器编排系统&#xff08;如 …...

进阶智能体实战八、需求分析助手(基于qwen多模态大模型对图文需求文档分析)(帮你生成 模块划分+页面+表设计、状态机、工作流、ER模型)

🚀 基于通义千问大模型的智能需求分析助手:一键生成需求分析、模块划分、ER 图和工作流! 在软件开发的早期阶段,需求分析是至关重要的一环。然而传统方式往往需要产品经理和架构师投入大量精力分析需求文档、划分模块、设计数据结构,效率低、容易出错。 为了解决这一痛…...

Git -> Git Stash临时保存当前工程分支修改

Git Stash 基本概念 git stash 用于临时保存当前工作目录的修改&#xff0c;让你可以快速切换到一个干净的工作状态&#xff0c;之后再恢复这些修改。 1. 保存当前修改 git stash # 或者添加描述信息 git stash save "修改描述"2. 查看stash列表 git stash list3…...

多线程和并发之线程

线程 前面讲到进程&#xff1a;为了并发执行任务&#xff08;程序&#xff09;&#xff0c;现代操作系统才引进进程的概念 分析&#xff1a; 创建开销问题&#xff1a;创建一个进程开销&#xff1a;大 子进程需要拷贝父进程的整个地址空间 通信开销问题&#xff1a;进程间的通…...

apptrace 的优势以及对 App 的价值

官网地址&#xff1a;AppTrace - 专业的移动应用推广追踪平台 apptrace 的优势以及对 App 的价值​ App 拉起作为移动端深度链接技术的关键应用&#xff0c;能实现从 H5 网页到 App 的无缝跳转&#xff0c;并精准定位到 App 内指定页面。apptrace 凭借专业的技术与丰富的经验…...

android studio debug调试出现 IOException异常

解决Android调试端口无法打开的问题&#xff0c;出现"Unable to open debugger port"错误时&#xff0c;可以进入app设置&#xff0c;选择Debugger选项&#xff0c;将Debug type更改为Java Only模式。这个方法适用于Android Studio调试时遇到的端口连接问题&#xff…...

PySpark 中使用 SQL 语句和表进行计算

PySpark 中使用 SQL 语句和表进行计算 PySpark 完全支持使用 SQL 语句和表进行 Spark 计算。以下是几种常见的使用方式&#xff1a; 1. 使用 Spark SQL from pyspark.sql import SparkSession# 创建 SparkSession spark SparkSession.builder.appName("SQLExample&quo…...

[Python] Python中的多重继承

文章目录 Lora中的例子 Lora中的例子 https://github.com/michaelnny/QLoRA-LLM/blob/main/qlora_llm/models/lora.py#L211C1-L243C10如果继承两个父类&#xff0c;并且父类的__init__参数不一样&#xff0c;则可以显式的调用父类init&#xff1b;如果用super().__init__()则需…...

在 RedHat 系统(RHEL 7/8/9)中安装 ​​pythonnet​​ 和 ​​.NET Core​​ 的完整指南

​1. 安装 .NET Core SDK​​ ​​RHEL 8/9&#xff08;推荐&#xff09;​​ bash # 添加微软仓库 sudo rpm -Uvh https://packages.microsoft.com/config/rhel/8/packages-microsoft-prod.rpm# 安装 .NET 8 SDK&#xff08;包含运行时&#xff09; sudo dnf install -y dot…...

vr中风--数据处理模型搭建与训练

# -*- coding: utf-8 -*- """ MUSED-I康复评估系统&#xff08;增强版&#xff09; 包含&#xff1a;多通道sEMG数据增强、混合模型架构、标准化处理 """ import numpy as np import pandas as pd from sklearn.model_selection import train_te…...

Socket网络编程之UDP套件字

基于的UDP套件字编程流程 UDP传输层的协议&#xff0c;面向无连接&#xff0c;数据报的传输层协议。 “ 无连接 ”&#xff1a;不可靠 在网络环境较好的情况下&#xff0c;UDP效率较高在网络环境较差的情况下&#xff0c;UDP可能存在丢包的情况同时一些“ 实时应用 ” 采用UD…...

前端学习(7)—— HTML + CSS实现博客系统页面

目录 一&#xff0c;效果展示 二&#xff0c;实现博客列表页 2.1 实现导航栏 2.2 实现个人信息 2.3 实现博客列表 三&#xff0c;实现博客正文页 3.2 复用 3.4 实现博客正文 四&#xff0c;实现博客登录页 4.1 版心 4.2 登录框 五&#xff0c;实现博客编辑页 5.1 …...

Bert和GPT区别

BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;和 GPT&#xff08;Generative Pre-trained Transformer&#xff09;都基于 Transformer 架构&#xff0c;但在设计目标、预训练任务和应用场景上有很大区别&#xff1a; 1. 架构方向性 B…...

聊一聊接口测试中缓存处理策略

目录 一、强制绕过缓存 添加时间戳参数 修改请求头 二、主动清除缓存 清除本地缓存 清除服务端缓存&#xff08;需权限&#xff09; 清除CDN缓存 三、测试缓存逻辑 首次请求获取数据 记录响应头中的缓存标识​​​​​ 验证缓存生效 测试缓存过期​​​​​​​ 四…...

RuoYi前后端分离框架实现前后端数据传输加密(二)之前端篇

一、背景 本文是RuoYi前后端分离框架实现前后端数据传输加密(一)之后端篇文章配套的,主要介绍前端对自定义字段传输加密的实现,两篇文章结合可以完整的完成RuoYi前后端分离框架对API通信过程中实现自定义字段加密传输。前端的加解密实现,不涉及到界面的修改,仅仅是方法的…...

如何使用.Net Reactor 批量加密 DLL

若在日常工作中加密操作的使用频率较高&#xff0c;每次启动程序并执行选择 DLL 文件等操作均会显得较为繁琐。在此&#xff0c;分享一种可提升操作效率的方法&#xff1a;通过命令行方式调用脚本&#xff0c;即可实现 DLL 或 Exe 文件的批量加密处理。具体操作如下&#xff1a…...

基于视觉的车道线检测完整代码:让驾驶更安全的开源解决方案

基于视觉的车道线检测完整代码&#xff1a;让驾驶更安全的开源解决方案 【下载地址】基于视觉的车道线检测完整代码 这是一个基于视觉的车道线检测开源项目&#xff0c;提供完整的代码示例&#xff0c;采用滑动窗口算法实现。项目通过逐行扫描图像&#xff0c;精准识别曲线车道…...

Intellij IDEA 查找接口实现类的快捷键

快捷键是 IntelliJ IDEA&#xff08;包括 PyCharm、WebStorm 等 JetBrains 家族 IDE&#xff09; 中非常常用的代码导航功能。下面是对你提到的两个快捷键的详细解释和用途说明&#xff1a; &#x1f50d; 1. 查找接口的实现类&#xff08;或方法的实现&#xff09; &#x1f…...

鸿蒙仓颉开发语言实战教程:自定义tabbar

大家周末好呀&#xff0c;今天继续分享仓颉语言开发商城应用的实战教程&#xff0c;今天要做的是tabbar。 大家都知道ArkTs有Tabs和TabContent容器&#xff0c;能够实现上图的样式&#xff0c;满足基本的使用需求。而仓颉就不同了&#xff0c;它虽然也有这两个组件&#xff0c;…...

03.MySQL表的操作详解

MySQL表的操作详解 MySQL 表的操作概述创建表 2.1 创建表的基本语法查看表结构修改表 4.1 新增列 4.2 修改列属性 4.3 修改列名 4.4 修改表名 4.5 删除列删除表 1. MySQL表的操作概述 MySQL表的操作是数据库开发和管理中的核心内容&#xff0c;主要涉及**数据定义语言&#…...

28 C 语言作用域详解:作用域特性(全局、局部、块级)、应用场景、注意事项

1 作用域简介 作用域定义了代码中标识符&#xff08;如变量、常量、数组、函数等&#xff09;的可见性与可访问范围&#xff0c;即标识符在程序的哪些位置能够被引用或访问。在 C 语言中&#xff0c;作用域主要分为三类&#xff1a; 全局作用域局部作用域块级作用域 需注意&am…...

MySQL 事务解析

1. 事务简介 事务&#xff08;Transaction&#xff09; 是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 经典案例&#xff1…...

题海拾贝:压缩字符串

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞&#xff0c;关注&am…...

【Hive 运维实战】一键管理 Hive 服务:Metastore 与 HiveServer2 控制脚本开发与实践

一、引言 在大数据开发中&#xff0c;Hive 作为重要的数据仓库工具&#xff0c;其核心服务metastore&#xff08;元数据服务&#xff09;和hiveserver2&#xff08;查询服务&#xff09;的启停管理是日常运维的基础操作。手动执行命令启停服务不仅效率低下&#xff0c;还容易因…...