工厂模式 vs 策略模式:设计模式中的 “创建者” 与 “决策者”
在日常工作里,需求变动或者新增功能是再常见不过的事情了。而面对这种情况时,那些耦合度较高的代码就会给我们带来不少麻烦,因为在这样的代码基础上添加新需求往往困难重重。为了保证系统的稳定性,我们在添加新需求时,最好避免直接修改前人编写的代码,否则可能会破坏原有的稳定结构。
接下来,我会为大家介绍两种非常实用的设计模式 —— 工厂模式和策略模式。如果您已经对这两种设计模式了如指掌,那么可以直接跳过下面的介绍内容。
下文中出现的案例代码在:https://gitee.com/dingchen0000/blog-notes.git
工厂模式
介绍
在传统的编程方式里,当我们需要使用某个对象时,就会直接使用new
关键字去创建它,就好像我们自己亲手打造一个工具,打造完成后就能马上使用。但这种方式在项目规模变大、逻辑变复杂后,会带来很多问题,而工厂模式的出现就是为了解决这些问题。
想象一下,我们在一个电子工厂里工作。你创造出了一个智能机器人,它有灵活的手臂(属性)和稳健的轮子(属性),能够高效地替你组装零件(方法)。在传统模式下,你创造出这个机器人后,就可以直接给它下达指令,让它开始工作。同样,你的同事创造了一台智能冰箱,它有超大的存储空间(属性)和智能的温度调节功能(方法),你的同事也能直接操作这台冰箱。
然而,如果有其他部门的同事想要使用你们创造的机器人和冰箱,就会变得很麻烦。他们需要分别找你和你的同事,经过你们的同意才能使用,这无疑增加了沟通成本和使用的复杂性。
为了解决这个问题,工厂引入了一个统一的管理部门(工厂模式)。你和你的同事把创造好的机器人和冰箱都交给这个管理部门,当其他部门的同事需要使用机器人或冰箱时,只需要向这个管理部门提出申请,管理部门就会根据需求提供相应的设备。这样一来,使用者不需要关心设备是如何制造出来的,也不需要和具体的创造者沟通,大大提高了使用效率,降低了各个部门之间的耦合度。
在代码的世界里也是一样。假设我们有不同的促销策略,比如打折、满减、赠品等。如果没有工厂模式,在需要使用这些策略时,我们就得在代码里到处使用new
关键字来创建策略对象,这样会让代码变得混乱,而且一旦策略的创建逻辑发生变化,就需要修改大量的代码。而使用工厂模式,我们可以把这些策略对象的创建逻辑封装在一个工厂类里,当需要使用某个策略时,只需要向工厂类请求,由工厂类来创建并返回相应的对象,这样就实现了对象的创建和使用的分离,降低了代码的耦合度,提高了代码的可维护性和可扩展性。
非工厂案例
在传统编程中,当需要实现打折促销功能时,通常会在业务逻辑代码里直接创建并使用打折策略对象。下面结合你提供的代码片段,以 Java 为例说明传统做法:
传统的模式
// 1. 定义促销策略接口
interface PromotionStrategy {double calculateDiscount(double orderAmount);
}// 2. 实现具体策略类
class DiscountStrategy implements PromotionStrategy {@Overridepublic double calculateDiscount(double orderAmount) {return orderAmount * 0.2; // 8折优惠}
}class FullReduceStrategy implements PromotionStrategy {@Overridepublic double calculateDiscount(double orderAmount) {return orderAmount >= 200 ? 50 : 0; // 满200减50}
}// 3. 业务逻辑直接依赖具体策略
class OrderServiceWithoutFactory {public double calculateFinalPrice(double amount, String strategyType) {PromotionStrategy strategy;// 直接在业务逻辑中创建对象if ("DISCOUNT".equalsIgnoreCase(strategyType)) {strategy = new DiscountStrategy();} else if ("FULL_REDUCE".equalsIgnoreCase(strategyType)) {strategy = new FullReduceStrategy();} else {throw new IllegalArgumentException("未知策略类型");}return amount - strategy.calculateDiscount(amount);}
}// 4. 客户端调用
public class NonFactoryDemo {public static void main(String[] args) {OrderServiceWithoutFactory service = new OrderServiceWithoutFactory();double finalPrice = service.calculateFinalPrice(300, "DISCOUNT");System.out.println("最终价格: " + finalPrice);}
}
-
在非工厂模式中,对象的创建逻辑直接嵌入在业务代码里,这会导致以下几个严重的维护问题:
代码分散问题
在复杂系统中,对象创建可能散落在多个服务类、工具类甚至控制器中。例如:
- 订单服务中直接
new DiscountStrategy()
- 营销活动模块中
new FullReduceStrategy()
- 定时任务里也可能创建相同对象
当需要修改或扩展功能时,你需要:
- 找出所有创建该对象的地方
- 逐一修改,可能遗漏某些角落
- 承担引入新问题的风险
- 订单服务中直接
缺点:
依赖关系复杂
业务类不仅依赖抽象接口,还依赖具体实现类:
public class OrderServiceWithoutFactory {public double calculatePrice(double amount) {// 直接依赖具体类!PromotionStrategy strategy = new DiscountStrategy(); return strategy.calculate(amount);}
}
这种强依赖导致:
- 新增策略时必须修改业务类代码
- 策略类的构造函数变化(如增加参数)会影响所有调用处
- 难以进行单元测试(需实例化真实对象而非模拟对象)
在非工厂模式里,直接在业务代码中「硬编码」创建对象,就像把钥匙藏在家里各个抽屉里 —— 看起来方便,实际用的时候全是麻烦:
代码像撒豆子,改一处得翻遍全项目
想象你要给超市所有收银台的「打折功能」升级:
- 原本「满减策略」的代码,可能藏在:
- 收银台的结账程序里(
new FullReduceStrategy()
) - 会员系统的积分兑换模块里(又一个
new FullReduceStrategy()
) - 甚至后台定时计算报表的脚本里(再来一个
new FullReduceStrategy()
)
- 收银台的结账程序里(
当你想修改满减规则时:
- 得像侦探一样,把全项目里所有写着
FullReduceStrategy
的地方都找出来(可能漏找某个角落) - 每个地方都要改一遍代码(比如把「满 200 减 50」改成「满 300 减 80」)
- 改完还得担心:有没有漏掉某个地方?改完其他功能会不会出错?
业务代码和具体实现「锁死」,牵一发而动全身
举个生活例子:
你开了家奶茶店,菜单上写着「招牌奶茶 = 红茶 + 奶精 + 珍珠」(业务逻辑),但你直接在菜单里写死了「用 A 牌红茶、B 牌奶精」(依赖具体实现类)。
问题来了:
- 新增口味麻烦:想推出「绿茶版奶茶」,必须把菜单上所有「红茶」字样都改成「绿茶」(新增策略必须改业务代码)。
- 供应商换原料就崩溃:如果 A 牌红茶停产,你得把菜单上所有「A 牌红茶」换成「C 牌红茶」(策略类构造函数修改,所有调用处都得改)。
- 没法模拟测试:想试试「用椰奶代替奶精」的效果,必须真的买椰奶回来试(单元测试时必须创建真实对象,没法用模拟数据)。
用代码举例就是:
// 业务代码直接「点名」要某个具体实现类
public class 收银台 {public double 计算价格(double 金额) {// 直接「new」一个具体的「满减策略」,就像直接说「我要A牌红茶」优惠策略 策略 = new 满减策略(); return 金额 - 策略.计算优惠(金额);}
}
这样写死的后果就是:
- 想换「打折策略」?必须改这里的
new 满减策略()
为new 打折策略()
。 - 满减策略的构造函数需要传参(比如
new 满减策略(200, 50)
)?所有用到它的地方都得跟着改参数。
工厂模式案例
// 1. 定义促销策略接口(与非工厂模式相同)
interface PromotionStrategy {double calculateDiscount(double orderAmount);
}// 2. 实现具体策略类(与非工厂模式相同)
class DiscountStrategy implements PromotionStrategy {@Overridepublic double calculateDiscount(double orderAmount) {return orderAmount * 0.2; // 8折优惠}
}class FullReduceStrategy implements PromotionStrategy {@Overridepublic double calculateDiscount(double orderAmount) {return orderAmount >= 200 ? 50 : 0; // 满200减50}
}// 3. 创建工厂类
class PromotionStrategyFactory {public static PromotionStrategy createStrategy(String strategyType) {if ("DISCOUNT".equalsIgnoreCase(strategyType)) {return new DiscountStrategy();} else if ("FULL_REDUCE".equalsIgnoreCase(strategyType)) {return new FullReduceStrategy();} else {throw new IllegalArgumentException("未知策略类型");}}
}// 4. 业务逻辑通过工厂获取策略
class OrderServiceWithFactory {public double calculateFinalPrice(double amount, String strategyType) {// 通过工厂获取策略,不直接依赖具体类PromotionStrategy strategy = PromotionStrategyFactory.createStrategy(strategyType);return amount - strategy.calculateDiscount(amount);}
}// 5. 客户端调用
public class FactoryDemo {public static void main(String[] args) {OrderServiceWithFactory service = new OrderServiceWithFactory();double finalPrice = service.calculateFinalPrice(300, "FULL_REDUCE");System.out.println("最终价格: " + finalPrice);}
}
工厂模式的好处显然就是
- 单一修改点:新增策略只需在工厂类中注册,无需修改业务代码
- 依赖倒置:业务类只依赖工厂和抽象接口,不依赖具体实现
- 代码复用:复杂的初始化逻辑只需在工厂中实现一次
- 统一管理:对象创建规则集中维护,便于新增功能和团队协作
- 可测试性:可以轻松替换工厂实现(如使用模拟工厂)进行单元测试
总结
非工厂模式就像把「建房子的图纸」和「搬砖的步骤」混在一起写:
- 简单场景下看似省事,但项目变大后,代码会像乱成一团的毛线 ——
- 改一个功能要挖地三尺找代码
- 牵一发而动全身,改完一处崩十处
- 想测试新功能,必须把真实对象全跑一遍
而工厂模式就像找了个「专业包工头」(工厂类)专门管搬砖,业务代码只需要告诉包工头「我要盖客厅还是卧室」,剩下的细节全由包工头处理 —— 既干净又省心。
策略模式
介绍
策略模式是一种行为型设计模式,其核心思想是:
- 封装算法族:将不同的算法(或策略)封装成独立的类,使它们可以相互替换。
- 解耦算法与使用:让算法的变化独立于使用算法的客户端,从而提高代码的灵活性和可扩展性
打个比方
你去餐厅吃饭,菜单上有「糖醋排骨」「鱼香肉丝」「麻婆豆腐」等菜品(这就是不同的「策略」)。
- 你不需要自己进厨房炒菜(不用关心具体怎么做菜),只需要告诉服务员「我要哪道菜」(调用策略)。
- 服务员(相当于「上下文类」)会根据你的选择,通知厨房做对应的菜(切换策略)。
策略模式的核心就像这个过程:把不同的「做事方法」封装起来,需要时随时切换,而调用者不用知道具体怎么实现。
案例说明
先定义「优惠规则」的统一标准(策略接口)
就像餐厅菜单上写着「所有菜品都要能算出价格」,我们先定一个接口:
public interface PromotionStrategy {double calculateDiscount(double orderAmount); // 不管怎么优惠,都要能算出优惠金额
}
作用:让所有优惠规则(打折、满减、赠品)都必须遵守这个「规矩」,方便后续统一管理。
每个优惠规则都是一个「独立菜品」(具体策略类)
-
打折策略
:相当于「糖醋排骨」,具体做法是「打 8 折」:
public class DiscountStrategy implements PromotionStrategy {@Overridepublic double calculateDiscount(double orderAmount) {return orderAmount * 0.2; // 直接算优惠金额} }
-
满减策略
:相当于「鱼香肉丝」,具体做法是「满 200 减 50」:
public class FullReduceStrategy implements PromotionStrategy {@Overridepublic double calculateDiscount(double orderAmount) {return orderAmount >= 200 ? 50 : 0; // 满足条件才优惠} }
-
赠品策略:相当于「麻婆豆腐」,做法是「满 100 送赠品」(虽然不直接减钱,但也是一种策略):
public class GiftStrategy implements PromotionStrategy {@Overridepublic double calculateDiscount(double orderAmount) {if (orderAmount >= 100) {System.out.println("送你小风扇!"); // 执行赠品逻辑}return 0; // 金额不变} }
关键点:每个策略类都是「自包含」的,就像厨房的不同厨师各自负责一道菜,互相不干扰。
上下文类:相当于「服务员」,负责切换策略
以前没有上下文类时,你得自己去厨房点菜(业务代码直接调用策略类),现在有了服务员,你只需要告诉她:「我要吃糖醋排骨」(调用上下文类的方法,传入策略类型)。
public class OrderContext {private PromotionStrategy currentStrategy; // 当前使用的策略(默认是空的)// 初始化时选一种策略(比如默认用打折)public OrderContext(PromotionStrategy strategy) {this.currentStrategy = strategy;}// 随时换策略!就像吃饭时突然想换菜,告诉服务员就行public void changeStrategy(PromotionStrategy newStrategy) {this.currentStrategy = newStrategy;}// 计算最终价格:交给当前策略去处理public double calculateFinalPrice(double orderAmount) {return orderAmount - currentStrategy.calculateDiscount(orderAmount);}
}
为什么需要上下文类?
- 解耦调用逻辑:业务代码不用关心「怎么创建策略对象」,只需要告诉上下文「我要用哪个策略」。
- 支持动态切换:比如用户下单时先用「打折策略」,付款前突然发现有满减活动,直接调用
changeStrategy
切换即可,不用改核心计算逻辑。
策略模式+工厂模式
在实际开发的时候呀,很少会只用到一种设计模式,一般都是好几种设计模式一起用。咱们就拿这个优惠策略的例子来说吧。
这个系统里的优惠策略可不止一种哦。要是以后想再增加新的优惠策略,就会在 PromotionStrategyFactory
这个工厂里创建新的对象。比如说以后又有了新的优惠方式,也得在这个工厂里来创建对应的对象。这里用到了策略模式,只要新的优惠策略实现 PromotionStrategy
这个接口,把里面计算折扣优惠的方法重新写一下,然后在工厂里把创建这个新策略对象的逻辑加上就行。
下面是 PromotionStrategyFactory
这个工厂类的代码:
@Component
public class PromotionStrategyFactory {public PromotionStrategy createStrategy(String strategyType) {// 根据传入的策略类型,用大写来判断switch (strategyType.toUpperCase()) {// 如果是 "DISCOUNT",就创建一个折扣策略对象case "DISCOUNT":return new DiscountStrategy();// 如果是 "FULL_REDUCE",就创建一个满减策略对象case "FULL_REDUCE":return new FullReduceStrategy();// 如果是 "GIFT",就创建一个赠品策略对象case "GIFT":return new GiftStrategy();// 如果传入的策略类型不认识,就抛出异常default:throw new IllegalArgumentException("未知策略类型: " + strategyType);}}
}
在控制器层,也就是和客户端交互的地方,代码是这样的:
@RestController
@RequestMapping("/api/promotion")
public class PromotionController {// 自动注入订单上下文对象@Autowiredprivate OrderContext orderContext;// 自动注入优惠策略工厂对象@Autowiredprivate PromotionStrategyFactory strategyFactory;// 处理 GET 请求,计算优惠后的价格@GetMapping("/calculate")public ResponseEntity<Map<String, Object>> calculate(@RequestParam double orderAmount, @RequestParam String strategyType) {try {// 1. 从工厂获取对应的优惠策略实例PromotionStrategy strategy = strategyFactory.createStrategy(strategyType);// 2. 把获取到的策略应用到订单上下文中orderContext.changeStrategy(strategy);// 3. 调用订单上下文的方法,计算出最终的价格double finalPrice = orderContext.calculateFinalPrice(orderAmount);// 4. 把计算结果放到一个 Map 里,作为响应返回Map<String, Object> result = new LinkedHashMap<>();result.put("开始价格", orderAmount);result.put("折扣类型", strategyType);result.put("最后的折扣", finalPrice);return ResponseEntity.ok(result);} catch (IllegalArgumentException e) {// 如果传入的策略类型不合法,就返回一个错误响应Map<String, Object> error = new LinkedHashMap<>();error.put("error", "INVALID_STRATEGY");error.put("message", e.getMessage());return ResponseEntity.badRequest().body(error);}}
}
简单来说呢,就是通过工厂类来创建不同的优惠策略对象,然后在控制器里把这些策略应用到订单上,计算出最终的优惠价格,要是遇到不认识的策略类型,还会给出错误提示。
改进(枚举+sprinboot自动注册)
在咱们现在用的工厂类里,代码采用的是硬编码方式。这就意味着,要是有新的优惠策略类加入,就得去修改工厂方法的代码。可在开发中,频繁修改代码是我们不想看到的,因为这可能会引入新的问题,也不利于代码的维护和扩展。
简单工厂模式确实帮我们把客户端和具体的策略类实现分离开来了,让它们之间的依赖关系没那么紧密。不过呢,工厂类在初始化策略对象(也就是策略 beans)的时候,还是和具体的策略类绑得比较紧。也就是说,工厂类得明确知道有哪些具体的策略类,这就导致一旦有新的策略类出现,工厂类就得跟着改。
为了让它们之间的关系更松散,我们可以借助 Spring 框架里的 InitializingBean
接口和 ApplicationContextAware
接口来自动完成策略对象的装配工作。
下面来详细说说这两个接口的作用:
InitializingBean
接口
InitializingBean
接口有一个 afterPropertiesSet
方法。当 Spring 容器创建并初始化一个实现了 InitializingBean
接口的类的实例时,在设置完所有属性之后,会自动调用 afterPropertiesSet
方法。我们可以在这个方法里完成一些初始化操作。
ApplicationContextAware
接口
ApplicationContextAware
接口有一个 setApplicationContext
方法。实现了这个接口的类可以通过该方法获取到 Spring 的 ApplicationContext
(应用上下文)。ApplicationContext
就像是 Spring 容器的大管家,它能管理所有的 Bean,我们可以通过它获取到容器中所有实现了某个接口的 Bean 实例。
实现自动装配策略对象
结合这两个接口,我们可以在工厂类里这样做:
- 实现
InitializingBean
接口,在afterPropertiesSet
方法中进行策略对象的初始化操作。 - 实现
ApplicationContextAware
接口,通过setApplicationContext
方法获取ApplicationContext
。 - 使用
ApplicationContext
的getBeansOfType
方法获取所有实现了PromotionStrategy
接口的 Bean 实例,并将它们存到一个Map
里。
这样,当有新的策略类添加时,只要它实现了 PromotionStrategy
接口,Spring 容器会自动把它注册为一个 Bean,afterPropertiesSet
方法会把它添加到 Map
中,工厂类就不需要再手动修改代码来创建新的策略对象,从而实现了工厂类和具体策略类的解耦。
@Component
public class PromotionFactory implements InitializingBean {private final Map<PromotionType, PromotionStrategy> strategyMap = new HashMap<>();private final ApplicationContext applicationContext;@Autowiredpublic PromotionFactory(ApplicationContext applicationContext) {this.applicationContext = applicationContext;}/*** 初始化:从Spring容器中获取所有策略Bean,并按枚举类型注册* Key:Bean 的名称(默认是类名首字母小写,如fullReduceStrategy)。* Value:Bean 的实例(实现了PromotionStrategy接口的具体策略类)。*/@Overridepublic void afterPropertiesSet() {// 获取所有实现PromotionStrategy接口的BeanMap<String, PromotionStrategy> beans = applicationContext.getBeansOfType(PromotionStrategy.class);beans.forEach((beanName, strategy) -> {// 从策略Bean中获取对应的枚举类型(需在策略类中增加获取类型的方法)// 这里假设策略类通过枚举类型命名(如FullReduceStrategy对应FULL_REDUCE枚举)PromotionType type = parseTypeFromBeanName(beanName);if (type != null) {strategyMap.put(type, strategy);}});}/*** 从Bean名称解析枚举类型(简化实现,实际可通过注解或接口方法指定类型)*/private PromotionType parseTypeFromBeanName(String beanName) {try {// Bean名称默认驼峰式,转为枚举大写return PromotionType.valueOf(beanName.toUpperCase());} catch (IllegalArgumentException e) {return null;}}/*** 获取策略实例*/public PromotionStrategy getStrategy(PromotionType type) {if (!strategyMap.containsKey(type)) {throw new IllegalArgumentException("未知促销类型:" + type);}return strategyMap.get(type);}
}
@Getter
public enum PromotionType {FULL_REDUCE("满减"),DISCOUNT("打折"),GIFT("赠品");private final String desc;PromotionType(String desc) {this.desc = desc;}
}
@RestController
public class TestController {private final OrderService orderService;public TestController(OrderService orderService) {this.orderService = orderService;}/*** 测试接口* http://localhost:8080/calculate?orderAmount=300&promotionType=FULL_REDUCE*/@GetMapping("/calculate")public String calculatePrice(@RequestParam double orderAmount, @RequestParam PromotionType promotionType) {double finalPrice = orderService.calculateFinalPrice(orderAmount, promotionType);return "订单金额:" + orderAmount + "元,使用" + promotionType.getDesc() + "后,最终价格:" + finalPrice + "元";}
}
调用的模型图
ice = orderService;
}
/*** 测试接口* http://localhost:8080/calculate?orderAmount=300&promotionType=FULL_REDUCE*/
@GetMapping("/calculate")
public String calculatePrice(@RequestParam double orderAmount, @RequestParam PromotionType promotionType) {double finalPrice = orderService.calculateFinalPrice(orderAmount, promotionType);return "订单金额:" + orderAmount + "元,使用" + promotionType.getDesc() + "后,最终价格:" + finalPrice + "元";
}
}
调用的模型图[外链图片转存中...(img-GYDzYNR0-1748529814600)]
相关文章:

工厂模式 vs 策略模式:设计模式中的 “创建者” 与 “决策者”
在日常工作里,需求变动或者新增功能是再常见不过的事情了。而面对这种情况时,那些耦合度较高的代码就会给我们带来不少麻烦,因为在这样的代码基础上添加新需求往往困难重重。为了保证系统的稳定性,我们在添加新需求时,…...
23、Swift框架微调实战(3)-Qwen2.5-VL-7B LORA微调OCR数据集
一、模型介绍 Qwen2.5-VL 是阿里通义千问团队开源的视觉语言模型,具有3B、7B和72B三种不同规模,能够识别常见物体、分析图像中的文本、图表等元素,并具备作为视觉Agent的能力。 Qwen2.5-VL 具备作为视觉Agent的能力,可以推理并动态使用工具,初步操作电脑和手机。在视频处…...

37. Sudoku Solver
题目描述 37. Sudoku Solver 回溯 class Solution {vector<vector<bool>> row_used;vector<vector<bool>> col_used;vector<vector<bool>> box_used;public:void solveSudoku(vector<vector<char>>& board) {row_used.r…...
C# Renci.SshNet 登陆 suse配置一粒
C# 调用Renci.SshNet 的SSH类库,登陆 suse linux系统,如果没有配置,会报错: Renci.SshNet.Common.SshAuthenticationException: No suitable authentication method found to complete 1、需要root登陆os,配置 /etc/ssh/sshd_con…...

RV1126-OPENCV 图像叠加
一.功能介绍 图像叠加:就是在一张图片上放上自己想要的图片,如LOGO,时间等。有点像之前提到的OSD原理一样。例如:下图一张图片,在左上角增加其他图片。 二.OPENCV中图像叠加常用的API 1. copyTo方法进行图像叠加 原理…...

修改 vscode 左侧导航栏的文字大小 (更新版)
1. 起因, 目的: 问题: vscode 左侧的文字太小了!!!我最火的一篇文章,写的就是这个问题。 看来这个问题,是很广泛的一个痛点。我最近更新了 vscode, 这个问题又出现了。再来搞一下。…...
从C++编程入手设计模式2——工厂模式
从C编程入手设计模式 工厂模式 我们马上就要迎来我们的第二个创建型设计模式:工厂方法模式(Factory Method Pattern)。换而言之,我们希望使用一个这样的接口,使用其他手段而不是直接创建的方式(说的有…...

云原生 Cloud Native Build (CNB)使用初体验
云原生 Cloud Native Build(CNB)使用初体验 引言 当“一切皆可云”成为趋势,传统开发环境正被云原生工具重塑。腾讯云CNB(Cloud Native Build)作为一站式开发平台,试图解决多环境协作难题。 本文将分享c…...

格式工厂 FormatFactory v5.20.便携版 ——多功能媒体文件转换工具 长期更新
—————【下 载 地 址】——————— 【本章下载一】:https://pan.xunlei.com/s/VORWF3Q7D0eCVV06LHbzheD-A1?pwdjikz# 【本章下载二】:https://pan.quark.cn/s/8ee59ed83658 【百款黑科技】:https://ucnygalh6wle.feishu.cn/wiki/…...

数据可视化--使用matplotlib绘制高级图表
目录 一、绘制等高线图 contour() 二、绘制矢量场流线图 streamplot() 三、绘制棉棒图 stem() 四、绘制哑铃图 五、绘制甘特图 六、绘制人口金字塔图 barh() 七、绘制漏斗图 简易版漏斗图 八、绘制桑基图 Sankey()---创建桑基图 add()---添加桑基图的选项 finish()…...
卷积神经网络(CNN)完全指南:从原理到实战
卷积神经网络(CNN)完全指南:从原理到实战 引言:为什么CNN改变了计算机视觉? 2012年,AlexNet在ImageNet竞赛中以压倒性优势获胜,将错误率降低了近10个百分点,这标志着卷积神经网络(CNN)时代的开始。如今&a…...

如何做好一个决策:基于 Excel的决策树+敏感性分析应用
决策点: 开发新产品? (是 / 否) 因素 (如果是): 市场接受度 (高 / 中 / 低);概率: 高(0.3), 中(0.5), 低(0.2) 结果值 (NPV): 高(+$1M), 中(+$0.2M), 低(-$0.5M) 不开发成本/收益: $0 开发计算: EMV(市场接受度) = (0.3 * 1M) + (0.5 * 0.2M) + (0.2 * -0.5M) = $0.3M + $…...

【模拟电子电路-工具使用】
模拟电子电路-工具使用 ■ 1. 模拟软件■ 1. circuit JS ■ 2. 万用表■ 3. 示波器■ 4.■ 5.■ 6.■ 7. ■ 1. 模拟软件 ■ 1. circuit JS ■ 2. 万用表 ■ 3. 示波器 ■ 4. ■ 5. ■ 6. ■ 7....

[ElasticSearch] ElasticSearch的初识与基本操作
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...
Spring AI 代理模式(Agent Agentic Patterns)
一、Agentic Patterns 核心思想 根据Anthropic《构建高效代理》研究报告,高效LLM代理的设计应遵循两大核心原则: 简单性优先:避免过度设计,从最简单的解决方案开始可组合性:通过模块化设计实现灵活组合而非复杂框架 …...

搜索引擎2.0(based elasticsearch6.8)设计与实现细节(完整版)
1 简介 1.1 背景 《搜索引擎onesearch 1.0-设计与实现.docx》介绍了1.0特性,搜索schema,agg,表达式搜索映射,本文介绍onesearch 2.0 新特性, 参考第2节 规划特性与发布计划 1.2 关键词 文档 Document elasticsearch 一行数据称为…...
ps中前景色和背景色
在Photoshop(简称PS)中,前景色和背景色是两个非常重要的概念,它们直接影响着绘图、填充、渐变等操作的最终效果。以下是对前景色和背景色的全面、深入解释: 一、前景色与背景色的定义 前景色:指的是当前绘…...
网页前端开发(基础进阶2--JS)
前面学习了html与css,接下来学习JS(JavaScript与Java无关)。 web标准(网页标准)分为3个部分: 1.html主要负责网页的结构(页面的元素和内容) 2.css主要负责网页的表现(…...

Go 即时通讯系统:客户端与服务端 WebSocket 通信交互
客户端和服务端的交互 客户端与服务端建立连接 客户端:客户端通过浏览器或者其他应用程序发起一个 HTTP 请求到服务端的 /socket.io 路径。在请求中会携带用户的 UUID 作为参数(通过 c.Query("user") 获取)。 // router/socket.…...
2025年5月AI科技领域周报(5.19-5.25):大模型多模态突破 具身智能开启机器人新纪元
2025年5月AI科技领域周报(5.19-5.25):大模型多模态突破 具身智能开启机器人新纪元 目录 2025年5月AI科技领域周报(5.19-5.25):大模型多模态突破 具身智能开启机器人新纪元一、本周热点回顾1. 百度发布全球首…...

某航后缀混淆逆向与顶像风控分析
文章目录 1. 写在前面2. 接口分析3. 加密分析4. 风控分析 【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致…...

[Protobuf]常见数据类型以及使用注意事项
[Protobuf]常见数据类型以及使用注意事项 水墨不写bug 文章目录 一、基本数据类型1、字段2、字段的修饰规则 二、自定义数据类型1、message类型2、enum类型3、Any类型4、oneof类型5、map类型 三、小工具1.hexdump2.decode 四、注意事项 一、基本数据类型 protobuf 支持多种基础…...
【C/C++】面试基础题目收集
C 软件开发面试中常见的刷题题目通常可分为以下几大类:数据结构与算法、系统编程、面向对象设计、C 语言特性、并发编程等。 🧠 一、数据结构与算法(力扣/牛客经典题) 掌握 STL 和底层结构实现能力: 📌 数…...

模拟实现线程池(线程数目为定值)和定时器
前言 昨天学习关于定时器的相关知识。今天花时间去模拟实现了一个定时器,同时也去模拟实现了一个线程池(线程数目为定值)。我感觉我收获了很多,对于线程的理解加深了。跟大家分享一下~ 线程池和定时器(这个是主要)的实现 代码 线程池 import java.ut…...

数据结构之队列实验
引言 在计算机科学中,进制转换是基础但重要的操作。例如将一个十进制数转换为二进制或八进制表示时,我们通常使用“短除法”——即不断用目标进制去除当前数,记录余数,直到商为0为止。 这种方法得到的是低位先产生的结果&#x…...
Java求职者面试题详解:计算机网络、操作系统、设计模式与数据结构
Java求职者面试题详解:计算机网络、操作系统、设计模式与数据结构 第一轮:基础概念问题 1. 请解释什么是HTTP协议? HTTP(HyperText Transfer Protocol)是一种用于传输超文本的协议,它定义了客户端和服务…...
每日八股文6.1
每日八股-6.1 Go1.Sync.map的底层实现2.结构体的tag如何获取?3.Go实现单例模式(使用sync.Once)4.Go实现单例模式(不使用sync.Once)5.make和new的区别6.Go项目引用包为什么用_以及包的init()函数7.如何判断一个结构体是…...

【Ubuntu】摸鱼技巧之虚拟机环境复制
前言 提示:所有的操作都需要关闭虚拟机 如何快速在其它电脑布置,linux环境,如果我们有一个环境直接拷贝就有时间摸鱼呀。 1.直接复制简单粗暴 不做赘述,如果不会复制,那么请右击鼠标压缩复制 2.克隆虚拟机 2.1 …...

室内VR全景助力房产营销及装修
在当今的地产行业,VR全景已成为不可或缺的应用工具。从地产直播到楼市VR地图,从效果图到水电家装施工记录,整个地产行业的上下游生态中,云VR全景的身影无处不在。本文将探讨VR全景在房产营销及装修领域的应用,并介绍众…...

jenkins集成gitlab实现自动构建
jenkins集成gitlab实现自动构建 前面我们已经部署了Jenkins和gitlab,本文介绍将二者结合使用 项目源码上传至gitee提供公网访问:https://gitee.com/ye-xiao-tian/my-webapp 1、创建一个群组和项目 2、添加ssh密钥 #生成密钥 [rootgitlab ~]# ssh-keyge…...