生产环境中常用的设计模式
生产环境中常用的设计模式
设计模式 | 目的 | 使用场景示例 |
---|---|---|
单例模式 | 保证一个类仅有一个实例,并提供一个访问它的全局访问点 | - 日志记录器 - 配置管理器 |
工厂方法模式 | 定义一个创建对象的接口,让子类决定实例化哪个类 | - 各种工厂类(如视频游戏工厂模式创建不同类型的角色) |
抽象工厂模式 | 解决一个系列的工厂,用于创建一组相关或依赖的对象 | - GUI组件库 - 汽车组装线 |
建造者模式 | 分离对象的构建过程和表示,允许逐步构造一个复杂对象 | - 构建复杂对象,如SQL语句构建器 |
原型模式 | 通过复制现有的实例来创建新的实例 | - 需要频繁创建开销较大的对象时 |
适配器模式 | 将不兼容的接口转换为一个可以使用的兼容接口 | - 兼容旧的类库 - 第三方库的集成 |
桥接模式 | 分离抽象部分和实现部分,使它们可以独立地变化 | - 当一个类存在多个变化维度时 |
组合模式 | 将对象组合成树形结构以表示“部分-整体”的层次结构 | - 处理类似文件系统的层次结构 |
装饰器模式 | 动态地添加额外功能到一个对象,而不是通过继承 | - 扩展对象功能,而不改变其类 |
外观模式 | 为子系统中的一组接口提供一个统一的接口 | - 简化复杂的系统接口 |
享元模式 | 运用共享技术有效支持大量细粒度对象 | - 当系统中存在大量相似对象时 |
代理模式 | 为其他对象提供一个代理或占位符,以控制对该对象的访问 | - 访问控制 - 延迟初始化 |
责任链模式 | 使多个对象都有机会处理请求,避免耦合请求的发送者和接收者 | - 审批流程 - 错误处理 |
命令模式 | 将一个请求封装为一个对象,允许使用不同的请求参数化其他对象 | - 宏系统 - 事务系统 |
解释器模式 | 定义一个语言的文法,并建立解释器解释该语言中的句子 | - 解析表达式或指令 |
迭代器模式 | 提供一种顺序访问聚合对象元素的方法,而不暴露其内部表示 | - 遍历聚合对象,而不关心内部结构 |
中介者模式 | 用一个中介对象封装一系列对象之间的交互 | - 集中管理对象间的通信 |
备忘录模式 | 捕获对象的内部状态,并在外部保存该状态 | - 提供对象状态快照,用于撤销操作 |
观察者模式 | 对象间存在一对多关系时使用 | - 事件多级触发 - 发布/订阅系统 |
状态模式 | 对象在其内部状态改变时可改变行为 | - 行为随状态改变而变化的对象 |
策略模式 | 定义一系列算法,并将它们封装起来使其可以互换 | - 运行时动态选择算法 |
访问者模式 | 在不改变类前提下,定义作用于对象结构的新操作 | - 数据结构稳定,但需定义新操作 |
Spring 容器的作用与使用
Spring 容器是 Spring 框架的核心组件之一,用于管理 Java 对象的生命周期和依赖关系。它以 IoC(控制反转) 和 DI(依赖注入) 为核心思想,帮助开发者简化对象的创建、配置、管理及其依赖关系。
1. Spring 容器的核心概念
- 控制反转(IoC):
传统编程中,对象由程序主动创建并管理;而在 IoC 中,对象的创建和生命周期交由 Spring 容器管理,程序通过依赖注入获得所需的对象。 - 依赖注入(DI):
容器负责将需要的依赖(如服务类、工具类)注入到使用它们的组件中,而不需要显式创建这些依赖。
2. Spring 容器的主要实现
Spring 提供了两种核心的 IoC 容器实现:
BeanFactory
:
基础容器,提供最基本的 IoC 功能,按需加载 Bean(延迟初始化)。适合资源受限的场景。ApplicationContext
:
功能更丰富的容器,支持事件发布、国际化、Bean 生命周期回调等功能。常用实现包括:ClassPathXmlApplicationContext
:通过 XML 配置文件加载 Bean。AnnotationConfigApplicationContext
:通过注解配置加载 Bean。WebApplicationContext
:专为 Web 应用设计的容器。
3. 使用 Spring 容器动态获取 Bean
通过 Spring 容器获取 Bean 的方式有多种,以下重点讲解动态获取的场景:
1. 使用 ApplicationContext.getBean
动态获取 Bean
ApplicationContext.getBean
方法可以根据名称或类型动态获取 Bean。例如:
@Autowired
private ApplicationContext applicationContext;public void getBeanExample() {// 根据 Bean 名称获取MyService myServiceByName = (MyService) applicationContext.getBean("myService");// 根据类型获取MyService myServiceByType = applicationContext.getBean(MyService.class);// 根据名称和类型获取MyService myServiceByNameAndType = applicationContext.getBean("myService", MyService.class);
}
2. 使用 @Autowired
注入
@Autowired
是 Spring 提供的注解,用于自动装配依赖。其本质也是通过容器获取对象并注入:
@Autowired
private MyService myService;public void useService() {myService.performTask();
}
4. 配置 Spring 容器的方式
1. 基于注解的配置
推荐使用注解方式配置 Spring 容器:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic MyService myService() {return new MyServiceImpl();}
}
加载 Spring 容器:
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class SpringContainerExample {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);MyService myService = context.getBean(MyService.class);myService.performTask();}
}
2. 基于 XML 的配置
虽然 XML 配置方式较少使用,但仍有必要了解:
XML 配置文件(applicationContext.xml
):
<beans xmlns="http://www.springframework.org/schema/beans" ...><bean id="myService" class="com.example.MyServiceImpl" />
</beans>
加载容器:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringContainerExample {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");MyService myService = (MyService) context.getBean("myService");myService.performTask();}
}
5. 使用场景中的优化策略
- 动态场景:
- 动态获取不同的实现类(如策略模式中的支付方式)。
- 配合
getBean
方法动态注入,提高灵活性。
- 注解优先:
- 使用
@Component
标注类,@Autowired
注入依赖,减少配置代码。
- 使用
- 多环境支持:
- 结合
@Profile
实现多环境配置。
- 结合
- 提升性能:
- 结合
@Lazy
实现延迟加载,优化资源占用。
- 结合
6. 总结
Spring 容器通过 IoC 和 DI 大幅简化了应用程序中对象的管理和依赖的配置,极大提高了开发效率和代码质量。动态获取 Bean、注解配置等功能为复杂业务场景(如策略模式)提供了便捷的实现方式。
1.策略模式
1.1 业务场景
假设有这样的业务场景,大数据系统把文件推送过来,根据不同类型的文件,采取不同的解析方式。多数的小伙伴就会写出以下的代码:
if(type=="A"){//按照A格式解析
}else if(type=="B"){//按B格式解析
}else{//按照默认格式解析
}
这个代码可能会存在哪些问题呢?
- 如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低。
- 如果你需要接入一种新的解析类型,那只能在原有代码上修改。
说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则以及单一原则。
- 开闭原则(对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码
- 单一原则(规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。
如果你的代码就是酱紫:有多个if...else
等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。
1.2 策略模式定义
策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的的客户。这个策略模式的定义是不是有点抽象呢?那我们来看点通俗易懂的比喻:
假设你跟不同性格类型的小姐姐约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去逛街买买买最合适。当然,目的都是为了得到小姐姐的芳心,请看电影、吃小吃、逛街就是不同的策略。
策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
1.3 策略模式使用
策略模式怎么使用呢?酱紫实现的:
- 定义策略接口:一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法)
- 实现不同的策略:不同策略的差异化实现(就是说,不同策略的实现类)
- 使用策略模式
1.3.1 定义策略接口:一个接口,两个方法
public interface IFileStrategy {//属于哪种文件解析类型FileTypeResolveEnum gainFileType();//封装的公用算法(具体的解析方法)void resolve(Object objectparam);
}
1.3.2 实现不同的策略
A 类型策略具体实现
@Component
public class AFileResolve implements IFileStrategy {private static final Logger logger = LoggerFactory.getLogger(AFileResolve.class);//1.匹配类型@Overridepublic FileTypeResolveEnum gainFileType() {return FileTypeResolveEnum.File_A_RESOLVE;}//2.封装的公用算法(具体的解析方法)@Overridepublic void resolve(Object objectparam) {logger.info("A 类型解析文件,参数:{}",objectparam);//A类型解析具体逻辑}
}
B 类型策略具体实现
@Component
public class BFileResolve implements IFileStrategy {private static final Logger logger = LoggerFactory.getLogger(BFileResolve.class);@Overridepublic FileTypeResolveEnum gainFileType() {return FileTypeResolveEnum.File_B_RESOLVE;}@Overridepublic void resolve(Object objectparam) {logger.info("B 类型解析文件,参数:{}",objectparam);//B类型解析具体逻辑}
}
默认类型策略具体实现
@Component
public class DefaultFileResolve implements IFileStrategy {private static final Logger logger = LoggerFactory.getLogger(DefaultFileResolve.class);@Overridepublic FileTypeResolveEnum gainFileType() {return FileTypeResolveEnum.File_DEFAULT_RESOLVE;}@Overridepublic void resolve(Object objectparam) {logger.info("默认类型解析文件,参数:{}",objectparam);//默认类型解析具体逻辑}
}
1.3.3 使用策略模式
如何使用呢?我们借助spring
的生命周期,使用ApplicationContextAware
接口,把对应的策略,初始化到map
里面。然后对外提供resolveFile
方法即可。
@Component
public class StrategyUseService implements ApplicationContextAware{private Map<FileTypeResolveEnum, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>();public void resolveFile(FileTypeResolveEnum fileTypeResolveEnum, Object objectParam) {IFileStrategy iFileStrategy = iFileStrategyMap.get(fileTypeResolveEnum);if (iFileStrategy != null) {iFileStrategy.resolve(objectParam);}}//把不同策略放到map@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {Map<String, IFileStrategy> tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.gainFileType(), strategyService));}
}
2. 工厂模式
核心:
- 利用 Java多态 进行公共功能同一抽象
- 使用 父类接口 指向 子类对象 进行优化
- 根据不同类型 创建对应子类
3. 简单工厂模式
3.1 模式简介
简单工厂模式是一种创建型设计模式,用于通过一个工厂类,根据不同的输入参数,动态决定实例化哪一个类。它将对象的创建逻辑集中在一个工厂类中,从而提高代码的复用性和维护性。
3.2 模式结构
- 工厂类(Factory): 负责创建产品对象的逻辑。
- 抽象产品类(Product): 定义产品的公共接口。
- 具体产品类(ConcreteProduct): 实现抽象产品接口的具体对象。
3.3 实现步骤
3.3.1 定义产品接口和具体实现
定义一个
Shape
接口,所有具体产品实现该接口。/*** 抽象产品接口*/ public interface Shape {void draw(); }/*** 具体产品:圆形*/ public class Circle implements Shape {@Overridepublic void draw() {System.out.println("绘制圆形");} }/*** 具体产品:矩形*/ public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("绘制矩形");} }/*** 具体产品:三角形*/ public class Triangle implements Shape {@Overridepublic void draw() {System.out.println("绘制三角形");} }
3.3.2 创建工厂类
工厂类包含逻辑,根据输入参数返回对应的具体产品实例。
/*** 简单工厂类*/ public class ShapeFactory {public static Shape createShape(String shapeType) {if ("circle".equalsIgnoreCase(shapeType)) {return new Circle();} else if ("rectangle".equalsIgnoreCase(shapeType)) {return new Rectangle();} else if ("triangle".equalsIgnoreCase(shapeType)) {return new Triangle();} else {throw new IllegalArgumentException("未知的图形类型:" + shapeType);}} }
3.3.3 测试简单工厂模式
编写测试类,验证简单工厂模式的功能。
public class SimpleFactoryTest {public static void main(String[] args) {// 创建各种图形Shape circle = ShapeFactory.createShape("circle");circle.draw();Shape rectangle = ShapeFactory.createShape("rectangle");rectangle.draw();Shape triangle = ShapeFactory.createShape("triangle");triangle.draw();} }
3.3.4 运行结果
运行测试代码后,会输出以下内容:
绘制圆形 绘制矩形 绘制三角形
4.4 优缺点分析
优点:
- 封装性强: 工厂类集中管理对象的创建逻辑,避免了重复代码。
- 可维护性好: 如果需要新增产品类型,只需修改工厂类,不影响客户端代码。
缺点:
- 违反开闭原则: 每次增加新产品,都需要修改工厂类。
- 单一职责压力大: 工厂类的职责过于集中,可能会成为系统的性能瓶颈。
3.5 优化方案
使用 工厂方法模式 可以解决简单工厂模式中违反开闭原则的问题。工厂方法模式通过定义多个具体工厂类,每个工厂类负责创建一种产品,符合开闭原则。
2.1 业务场景
工厂模式一般配合策略模式一起使用。用来去优化大量的if...else...
或switch...case...
条件语句。
我们就取第一小节中策略模式那个例子吧。根据不同的文件解析类型,创建不同的解析对象
IFileStrategy getFileStrategy(FileTypeResolveEnum fileType){IFileStrategy fileStrategy ;if(fileType=FileTypeResolveEnum.File_A_RESOLVE){fileStrategy = new AFileResolve();}else if(fileType=FileTypeResolveEnum.File_A_RESOLV){fileStrategy = new BFileResolve();}else{fileStrategy = new DefaultFileResolve();}return fileStrategy;}
其实这就是工厂模式,定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
策略模式的例子,没有使用上一段代码,而是借助spring的特性,搞了一个工厂模式,哈哈,小伙伴们可以回去那个例子细品一下,我把代码再搬下来,小伙伴们再品一下吧:
@Component
public class StrategyUseService implements ApplicationContextAware{private Map<FileTypeResolveEnum, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>();//把所有的文件类型解析的对象,放到map,需要使用时,信手拈来即可。这就是工厂模式的一种体现啦@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {Map<String, IFileStrategy> tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.gainFileType(), strategyService));}
}
2.2 使用工厂模式
定义工厂模式也是比较简单的:
- 一个工厂接口,提供一个创建不同对象的方法。
- 其子类实现工厂接口,构造不同对象
- 使用工厂模式
2.3.1 一个工厂接口
interface IFileResolveFactory{void resolve();
}
2.3.2 不同子类实现工厂接口
class AFileResolve implements IFileResolveFactory{void resolve(){System.out.println("文件A类型解析");}
}class BFileResolve implements IFileResolveFactory{void resolve(){System.out.println("文件B类型解析");}
}class DefaultFileResolve implements IFileResolveFactory{void resolve(){System.out.println("默认文件类型解析");}
}
2.3.3 使用工厂模式
//构造不同的工厂对象
IFileResolveFactory fileResolveFactory;
if(fileType=“A”){fileResolveFactory = new AFileResolve();
}else if(fileType=“B”){fileResolveFactory = new BFileResolve();}else{fileResolveFactory = new DefaultFileResolve();
}fileResolveFactory.resolve();
一般情况下,对于工厂模式,你不会看到以上的代码。工厂模式会跟配合其他设计模式如策略模式一起出现的。
2.4 工厂方法模式 VS 简单工厂模式
特性 | 简单工厂模式 | 工厂方法模式 |
---|---|---|
创建逻辑集中性 | 集中在一个工厂类中 | 分散到多个具体工厂类 |
符合开闭原则 | 不符合 | 符合 |
灵活性 | 较低 | 较高 |
类的数量 | 较少 | 较多 |
使用场景 | 产品种类较少,且变化较少的场景 | 产品种类较多,且变化频繁的场景 |
3. 责任链模式
3.1 业务场景
我们来看一个常见的业务场景:下订单。
下订单接口通常包含以下逻辑:
- 参数非空校验
- 安全校验
- 黑名单校验
- 规则拦截
很多开发者会直接使用异常来实现,但这种做法可能存在以下问题:
- 不便扩展:异常只能返回异常信息,扩展性差。
- 效率低下:用异常做逻辑判断效率低。
解决方式:使用责任链模式。
以下是传统异常实现的代码示例:
public class Order {public void checkNullParam(Object param) {// 参数非空校验throw new RuntimeException();}public void checkSecurity() {// 安全校验throw new RuntimeException();}public void checkBlackList() {// 黑名单校验throw new RuntimeException();}public void checkRule() {// 规则拦截throw new RuntimeException();}public static void main(String[] args) {Order order = new Order();try {order.checkNullParam(null); // 参数非空校验order.checkSecurity(); // 安全校验order.checkBlackList(); // 黑名单校验order.checkRule(); // 规则拦截System.out.println("Order success");} catch (RuntimeException e) {System.out.println("Order fail");}}
}
上述代码的问题包括:
- 使用异常进行逻辑判断,效率低且难以扩展。
- 异常设计初衷是处理意外情况,过度使用违背了设计原则。
这段代码使用了异常来做逻辑条件判断,如果后续逻辑越来越复杂的话,会出现一些问题:如异常只能返回异常信息,不能返回更多的字段,这时候需要自定义异常类。
【强制】 异常不要用来做流程控制,条件控制。说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。
阿里开发手册规定:禁止用异常做逻辑判断。
优化方案:责任链模式。
3.2 责任链模式定义
当希望让多个对象有机会处理某个请求时,可以使用责任链模式。
定义:责任链模式为请求创建一个接收者对象的链,每个对象节点根据条件决定是否处理请求或将其传递给下一个节点。这样实现了请求的发送者和接收者的解耦。
责任链模式的核心是将处理逻辑串成链条,让请求依次传递。
定义:责任链模式为请求创建了一个接收者对象的链。执行链上有多个对象节点,每个对象节点都有机会(条件匹配)处理请求事务,如果某个对象节点处理完了,就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。
责任链模式实际上是一种处理请求的模式,它让多个处理器(对象节点)都有机会处理该请求,直到其中某个处理成功为止。责任链模式把多个处理器串成链,然后让请求在链上传递:
打个比喻:
假设你晚上去上选修课,为了可以走点走,坐到了最后一排。来到教室,发现前面坐了好几个漂亮的小姐姐,于是你找张纸条,写上:“你好, 可以做我的女朋友吗?如果不愿意请向前传”。纸条就一个接一个的传上去了,后来传到第一排的那个妹子手上,她把纸条交给老师,听说老师40多岁未婚…
3.3 责任链模式使用
核心步骤:
- 定义一个接口或抽象类。
- 每个责任节点实现接口或继承抽象类,进行差异化处理。
- 初始化责任链,将节点串联起来。
3.3.1 一个接口或者抽象类
这个接口或者抽象类,需要:
- 有一个指向责任下一个对象的属性
- 一个设置下一个对象的set方法
- 给子类对象差异化实现的方法(如以下代码的doFilter方法)
抽象类包含:
- 指向下一个处理节点的引用。
- 设置下一个处理节点的方法。
- 子类实现的处理逻辑方法。
public abstract class AbstractHandler {//责任链中的下一个对象private AbstractHandler nextHandler;/*** 责任链的下一个对象*/public void setNextHandler(AbstractHandler nextHandler){this.nextHandler = nextHandler;}/*** 具体参数拦截逻辑,给子类去实现*/public void filter(Request request, Response response) {doFilter(request, response);if (getNextHandler() != null) {getNextHandler().filter(request, response);}}public AbstractHandler getNextHandler() {return nextHandler;}abstract void doFilter(Request filterRequest, Response response);}
3.3.2 每个对象差异化处理
责任链上,每个对象的差异化处理,如本小节的业务场景,就有参数校验对象、安全校验对象、黑名单校验对象、规则拦截对象
/*** 参数校验对象**/
@Component
@Order(1) //顺序排第1,最先校验
public class CheckParamFilterObject extends AbstractHandler {@Overridepublic void doFilter(Request request, Response response) {System.out.println("非空参数检查");}
}/*** 安全校验对象*/
@Component
@Order(2) //校验顺序排第2
public class CheckSecurityFilterObject extends AbstractHandler {@Overridepublic void doFilter(Request request, Response response) {//invoke Security checkSystem.out.println("安全调用校验");}
}
/*** 黑名单校验对象*/
@Component
@Order(3) //校验顺序排第3
public class CheckBlackFilterObject extends AbstractHandler {@Overridepublic void doFilter(Request request, Response response) {//invoke black list checkSystem.out.println("校验黑名单");}
}/*** 规则拦截对象*/
@Component
@Order(4) //校验顺序排第4
public class CheckRuleFilterObject extends AbstractHandler {@Overridepublic void doFilter(Request request, Response response) {System.out.println("规则校验");}
}
3.3.3 对象链连起来(初始化)&& 使用
使用Spring的依赖注入将责任节点串联起来。
@Component("ChainPatternDemo")
public class ChainPatternDemo {//自动注入各个责任链的对象@Autowiredprivate List<AbstractHandler> abstractHandleList;private AbstractHandler abstractHandler;//spring注入后自动执行,责任链的对象连接起来@PostConstructpublic void initializeChainFilter(){for(int i = 0;i<abstractHandleList.size();i++){if(i == 0){abstractHandler = abstractHandleList.get(0);}else{AbstractHandler currentHander = abstractHandleList.get(i - 1);AbstractHandler nextHander = abstractHandleList.get(i);currentHander.setNextHandler(nextHander);}}}//直接调用这个方法使用public Response exec(Request request, Response response) {abstractHandler.filter(request, response);return response;}public AbstractHandler getAbstractHandler() {return abstractHandler;}public void setAbstractHandler(AbstractHandler abstractHandler) {this.abstractHandler = abstractHandler;}
}
运行结果
执行代码后输出:
非空参数检查
安全调用校验
校验黑名单
规则校验
4. 模板方法模式
核心思想
- 通过抽象类定义业务流程骨架,子类根据具体需求实现差异化代码。
- 约束子类的业务执行流程,保证流程的一致性和规范性。
实现步骤
- 定义模板方法的抽象类,包含固定流程的定义。
- 实现具体步骤,由子类实现差异化逻辑。
- 使用模板方法,调用统一的处理逻辑。
4.1 业务场景
假设一个内部系统需要处理不同商户的请求,与外部第三方系统进行 HTTP 通信。每个请求需要经过以下固定流程:
每个请求需要经过以下固定流程:
- 查询商户信息。
- 对请求报文加签。
- 发送 HTTP 请求。
- 对返回的报文验签。
此外,不同商户可能采用不同的通信方式:
- 商户 A 通过代理通信。
- 商户 B 直接连接。
原始实现问题
以 A 商户和 B 商户为例,原始实现如下:
// 商户 A 处理逻辑
class CompanyAHandler implements RequestHandler {public Resp handle(Req req) {//查询商户信息queryMerchantInfo();//加签signature();//http请求(A商户假设走的是代理)httpRequestbyProxy()//验签verify();return new Resp();}
}// 商户 B 处理逻辑
class CompanyBHandler implements RequestHandler {public Resp handle(Req req) {//查询商户信息queryMerchantInfo();//加签signature();// http请求(B商户不走代理,直连)httpRequestbyDirect();// 验签verify(); return new Resp();}
}
缺点:
- 代码重复:通用流程在多个子类中重复实现。
- 难以扩展:新增商户需要完整实现所有逻辑。
为了解决这些问题,可以使用模板方法模式。
4.2 模板方法模式定义
模板方法模式:
- 定义一个操作中的算法的骨架流程,把一些步骤的具体实现留给子类。
- 子类可以在不改变算法结构的前提下,重定义某些步骤。
核心思想:定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现,这样不同的子类就可以定义出不同的步骤。
通俗比喻:
模板方法模式 举例:追女朋友需要按顺序完成,牵手 → 拥抱 → 接吻 → 拍拍手。具体用左手还是右手牵,这种细节交给实现者决定,但整体流程固定。
4.3 模板方法使用
- 一个抽象类,定义骨架流程(抽象方法放一起)
- 确定的共同方法步骤,放到抽象类(去除抽象方法标记)
- 不确定的步骤,给子类去差异化实现
我们继续那以上的举例的业务流程例子,来一起用 模板方法优化一下哈:
4.3.1 定义抽象类
抽象类定义通用业务流程骨架:
/*** 抽象类定义业务流程骨架*/
abstract class AbstractMerchantService {// 1.查询商户信息abstract void queryMerchantInfo();// 2.加签abstract void signature();// 3.发送 HTTP 请求abstract void httpRequest();// 4.验签abstract void verifySignature();// 5.Http是否走代理(提供给子类实现)abstract boolean isRequestByProxy();/*** 模板方法:定义处理请求的流程*/public Resp handleTemplate(Req req) {queryMerchantInfo();signature();httpRequest();verifySignature();return new Resp();}
}
4.3.2 子类实现差异化逻辑
商户 A 的实现:
/*** 商户 A 实现类*/
class CompanyAServiceImpl extends AbstractMerchantService {@Overridevoid queryMerchantInfo() {System.out.println("查询商户 A 信息");}@Overridevoid signature() {System.out.println("对商户 A 请求报文加签");}@Overridevoid httpRequest() {System.out.println("通过代理发送商户 A 的 HTTP 请求");}@Overridevoid verifySignature() {System.out.println("验证商户 A 返回报文签名");}// 公司 A 走 http代理@Overrideboolean isRequestByProxy(){return true;}
}
商户 B 的实现:
/*** 商户 B 实现类*/
class CompanyBServiceImpl extends AbstractMerchantService {@Overridevoid queryMerchantInfo() {System.out.println("查询商户 B 信息");}@Overridevoid signature() {System.out.println("对商户 B 请求报文加签");}@Overridevoid httpRequest() {System.out.println("通过直连发送商户 B 的 HTTP 请求");}@Overridevoid verifySignature() {System.out.println("验证商户 B 返回报文签名");}// 公司 B 不走 http代理@Overrideboolean isRequestByProxy(){return false;}
}
4.3.3. 使用模板方法
调用模板方法处理请求:
public class Main {public static void main(String[] args) {AbstractMerchantService serviceA = new CompanyAServiceImpl();AbstractMerchantService serviceB = new CompanyBServiceImpl();// 处理商户 A 请求Resp respA = serviceA.handleTemplate(new Req());System.out.println("商户 A 请求处理完成");// 处理商户 B 请求Resp respB = serviceB.handleTemplate(new Req());System.out.println("商户 B 请求处理完成");}
}
5. 观察者模式(监听模式)
5.1 业务场景
登陆注册应该是最常见的业务场景了。就拿注册来说,我们经常会遇到类似的场景:用户注册成功后,需要给用户发送一条消息、邮件,或者其他通知。通常代码如下:
void register(User user){insertRegisterUser(user);sendIMMessage();sendEmail();
}
这段代码存在的问题是:
- 违反开闭原则:如果产品新增需求,例如注册成功后再给用户发送短信通知,就需要修改
register
方法的代码。
void register(User user){insertRegisterUser(user);sendIMMessage();sendMobileMessage();sendEmail();
}
- 同步阻塞问题:如果调用“发送短信”的接口失败,可能会影响用户注册流程。
为了解决这些问题,可以使用观察者模式进行优化。
5.2 观察者模式定义
观察者模式:定义对象之间的一对多依赖关系,使得一个对象的状态发生改变时,所有依赖它的对象都会收到通知并自动更新。
观察者模式属于行为型模式。一个对象(被观察者)的状态发生改变时,会通知所有依赖对象(观察者)。
核心成员
- 被观察者(Observerable):目标对象,状态发生变化时,通知所有观察者。
- 观察者(Observer):依赖被观察者,并响应其状态变化。
使用场景
- 事件异步通知:例如用户注册成功后,发送消息、邮件等操作。
5.3 观察者模式使用
观察者模式实现的话,还是比较简单的。
- 一个被观察者的类Observerable ;
- 多个观察者Observer ;
- 观察者的差异化实现
- 经典观察者模式封装:EventBus实战
5.3.1 被观察者类 Observerable
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;// 被观察者
public class Observerable {// 使用线程安全的Listprivate final List<Observer> observers = new CopyOnWriteArrayList<>();// 定义状态常量public static final int STATE_NOTIFY = 1;private int state;public int getState() {return state;}// 设置状态并通知观察者public void setState(int state) {this.state = state;notifyAllObservers();}// 添加观察者public void addObserver(Observer observer) {observers.add(observer);}// 移除观察者public void removeObserver(Observer observer) {observers.remove(observer);}// 通知所有观察者private void notifyAllObservers() {if (state != STATE_NOTIFY) {System.out.println("状态非通知状态,不触发通知。");return;}observers.forEach(Observer::doEvent);}
}
5.3.2 观察者接口 Observer
及其实现类
// 观察者接口
public interface Observer {void doEvent();
}// IM 消息观察者
public class IMMessageObserver implements Observer {@Overridepublic void doEvent() {System.out.println("发送 IM 消息");}
}// 短信观察者
public class MobileNoObserver implements Observer {@Overridepublic void doEvent() {System.out.println("发送短信消息");}
}// 邮件观察者
public class EmailObserver implements Observer {@Overridepublic void doEvent() {System.out.println("发送邮件消息");}
}
5.3.3 测试代码
public class ObserverPatternDemo {public static void main(String[] args) {Observerable observerable = new Observerable();// 添加观察者observerable.addObserver(new IMMessageObserver());observerable.addObserver(new MobileNoObserver());observerable.addObserver(new EmailObserver());// 设置状态并触发通知observerable.setState(Observerable.STATE_NOTIFY);}
}
5.3.4 使用 Guava EventBus 实现的观察者模式优化
直接实现观察者模式会略显繁琐,Google 提供的 Guava EventBus
封装了一套基于注解的事件总线,使用起来更加简单方便。
EventBus 中心类
import com.google.common.eventbus.EventBus;public class EventBusCenter {private static final EventBus EVENT_BUS = new EventBus();private EventBusCenter() {// 禁止实例化}public static EventBus getInstance() {return EVENT_BUS;}//添加观察者public static void register(Object obj) {EVENT_BUS.register(obj);}//移除观察者public static void unregister(Object obj) {EVENT_BUS.unregister(obj);}//把消息推给观察者public static void post(Object obj) {EVENT_BUS.post(obj);}
}
观察者类 EventListener
import com.google.common.eventbus.Subscribe;public class EventListener {@Subscribe //加了订阅,这里标记这个方法是事件处理方法 public void handle(NotifyEvent notifyEvent) {System.out.println("发送 IM 消息:" + notifyEvent.getImNo());System.out.println("发送短信消息:" + notifyEvent.getMobileNo());System.out.println("发送邮件消息:" + notifyEvent.getEmailNo());}
}
通知事件类 NotifyEvent
//通知事件类
public class NotifyEvent {private final String mobileNo;private final String emailNo;private final String imNo;public NotifyEvent(String mobileNo, String emailNo, String imNo) {this.mobileNo = mobileNo;this.emailNo = emailNo;this.imNo = imNo;}public String getMobileNo() {return mobileNo;}public String getEmailNo() {return emailNo;}public String getImNo() {return imNo;}
}
测试代码
public class EventBusDemoTest {public static void main(String[] args) {EventListener eventListener = new EventListener();EventBusCenter.register(eventListener);EventBusCenter.post(new NotifyEvent("13372817283", "123@qq.com", "666"));}
}
public class EventBusDemoTest {public static void main(String[] args) {// 注册观察者EventListener eventListener = new EventListener();EventBusCenter.register(eventListener);// 发布通知事件NotifyEvent event = new NotifyEvent("13372817283", "123@qq.com", "666");EventBusCenter.post(event);}
}
输出结果
发送IM消息: 666
发送短信消息: 13372817283
发送Email消息: 123@qq.com
6. 单例模式
6.1 业务场景
单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。I/O与数据库的连接,一般就用单例模式实现de的。Windows里面的Task Manager(任务管理器)也是很典型的单例模式。
来看一个单例模式的例子
public class LanHanSingleton {private static LanHanSingleton instance;private LanHanSingleton(){}public static LanHanSingleton getInstance(){if (instance == null) {instance = new LanHanSingleton();}return instance;}}
以上的例子,就是懒汉式的单例实现。实例在需要用到的时候,才去创建,就比较懒。如果有则返回,没有则新建,需要加下 synchronized
关键字,要不然可能存在线性安全问题。
6.2 单例模式的经典写法
其实单例模式还有有好几种实现方式,如饿汉模式,双重校验锁,静态内部类,枚举等实现方式。
6.2.1 饿汉模式
public class EHanSingleton {private static EHanSingleton instance = new EHanSingleton();private EHanSingleton(){ }public static EHanSingleton getInstance() {return instance;}}
饿汉模式,它比较饥饿、比较勤奋,实例在初始化的时候就已经建好了,不管你后面有没有用到,都先新建好实例再说。这个就没有线程安全的问题,但是呢,浪费内存空间呀。
6.2.2 双重校验锁
public class DoubleCheckSingleton {private volatile static DoubleCheckSingleton instance;private DoubleCheckSingleton() { }public static DoubleCheckSingleton getInstance(){if (instance == null) {synchronized (DoubleCheckSingleton.class) {if (instance == null) {instance = new DoubleCheckSingleton();}}}return instance;}
}
双重校验锁实现的单例模式,综合了懒汉式和饿汉式两者的优缺点。以上代码例子中,在synchronized关键字内外都加了一层 if
条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
6.2.3 静态内部类
public class InnerClassSingleton {private static class InnerClassSingletonHolder{private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();}private InnerClassSingleton(){}public static final InnerClassSingleton getInstance(){return InnerClassSingletonHolder.INSTANCE;}
}
静态内部类的实现方式,效果有点类似双重校验锁。但这种方式只适用于静态域场景,双重校验锁方式可在实例域需要延迟初始化时使用。
6.2.4 枚举
public enum SingletonEnum {INSTANCE;public SingletonEnum getInstance(){return INSTANCE;}
}
枚举实现的单例,代码简洁清晰。并且它还自动支持序列化机制,绝对防止多次实例化。
7.代理模式
代理模式(Proxy Pattern) 是一种结构型设计模式,通过为其他对象提供一种代理以控制对这个对象的访问。代理对象作为实际对象的代表,负责处理客户端与实际对象之间的交互。
7.1 模式简介
代理模式为实际对象提供了一个替代方案或占位符,可以在不改变实际对象的情况下增强功能、控制访问或延迟加载。常见的代理模式包括:
- 静态代理
- 动态代理
- JDK 动态代理
- CGLIB 动态代理
7.2 模式结构
代理模式包含以下主要角色:
- 抽象主题(Subject): 定义实际对象和代理对象的公共接口。
- 实际对象(RealSubject): 实现抽象主题接口的具体类,包含实际业务逻辑。
- 代理对象(Proxy): 通过实现抽象主题接口来代理实际对象,可以在调用实际对象方法时执行额外操作。
7.3 实现步骤
示例场景:用户服务的代理
假设有一个 UserService
接口,提供用户登录功能。我们使用代理模式来扩展日志记录功能。
7.3.1 静态代理实现
Step 1: 定义抽象主题和实际对象
定义 UserService
接口和实现类:
/*** 抽象主题:用户服务接口*/
public interface UserService {void login(String username, String password);
}/*** 实际对象:用户服务实现类*/
public class UserServiceImpl implements UserService {@Overridepublic void login(String username, String password) {System.out.println(username + " 登录成功!");}
}
Step 2: 创建代理对象
代理对象实现与实际对象相同的接口,并在调用实际对象方法时添加额外功能:
/*** 静态代理类:用户服务代理*/
public class UserServiceProxy implements UserService {private final UserService userService;// 通过构造函数传入实际对象public UserServiceProxy(UserService userService) {this.userService = userService;}@Overridepublic void login(String username, String password) {// 添加额外功能:日志记录System.out.println("日志记录:用户 " + username + " 尝试登录。");// 调用实际对象的方法userService.login(username, password);System.out.println("日志记录:用户 " + username + " 登录操作完成。");}
}
Step 3: 测试静态代理
测试代理功能:
public class StaticProxyTest {public static void main(String[] args) {// 创建实际对象UserService userService = new UserServiceImpl();// 创建代理对象UserService proxy = new UserServiceProxy(userService);// 使用代理对象proxy.login("Alice", "123456");}
}
运行结果:
日志记录:用户 Alice 尝试登录。
Alice 登录成功!
日志记录:用户 Alice 登录操作完成。
7.3.2 动态代理实现
动态代理 是在运行时动态生成代理类,无需手动为每个类编写代理代码。
JDK 动态代理
Step 1: 使用 InvocationHandler
接口
动态代理使用 InvocationHandler
处理方法调用:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class LoggingInvocationHandler implements InvocationHandler {private final Object target;// 构造函数接受实际对象public LoggingInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 前置增强:日志记录System.out.println("日志记录:方法 " + method.getName() + " 开始执行。");// 调用实际对象的方法Object result = method.invoke(target, args);// 后置增强:日志记录System.out.println("日志记录:方法 " + method.getName() + " 执行完成。");return result;}
}
Step 2: 生成动态代理类
通过 Proxy.newProxyInstance
方法生成代理对象:
import java.lang.reflect.Proxy;public class DynamicProxyTest {public static void main(String[] args) {// 创建实际对象UserService userService = new UserServiceImpl();// 创建动态代理对象UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),new LoggingInvocationHandler(userService));// 使用代理对象proxy.login("Bob", "654321");}
}
运行结果:
日志记录:方法 login 开始执行。
Bob 登录成功!
日志记录:方法 login 执行完成。
CGLIB 动态代理
CGLIB 动态代理 使用字节码生成技术,可以代理没有实现接口的类。
依赖:
添加 cglib
和 asm
依赖:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
实现:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** CGLIB 动态代理*/
public class CglibProxy implements MethodInterceptor {private final Object target;public CglibProxy(Object target) {this.target = target;}public Object getProxyInstance() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("日志记录:方法 " + method.getName() + " 开始执行。");Object result = proxy.invokeSuper(obj, args);System.out.println("日志记录:方法 " + method.getName() + " 执行完成。");return result;}
}
测试:
public class CglibProxyTest {public static void main(String[] args) {// 创建实际对象UserServiceImpl userService = new UserServiceImpl();// 创建代理对象CglibProxy proxy = new CglibProxy(userService);UserServiceImpl proxyInstance = (UserServiceImpl) proxy.getProxyInstance();// 使用代理对象proxyInstance.login("Charlie", "112233");}
}
7.4 优缺点分析
优点:
- 增强功能: 通过代理对象添加额外功能(如日志、权限检查等)。
- 解耦合: 客户端与实际对象之间的交互通过代理对象实现,便于维护。
- 灵活性: 动态代理可在运行时动态生成代理对象,无需手动编写代理类。
缺点:
- 性能开销: 动态代理涉及反射调用,性能略低于静态代理。
- 复杂性增加: 对新手开发者来说,动态代理的学习曲线较陡。
相关文章:
生产环境中常用的设计模式
生产环境中常用的设计模式 设计模式目的使用场景示例单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点- 日志记录器- 配置管理器工厂方法模式定义一个创建对象的接口,让子类决定实例化哪个类- 各种工厂类(如视频游戏工厂模式创…...
【STM32-学习笔记-4-】PWM、输入捕获(PWMI)
文章目录 1、PWMPWM配置 2、输入捕获配置3、编码器 1、PWM PWM配置 配置时基单元配置输出比较单元配置输出PWM波的端口 #include "stm32f10x.h" // Device headervoid PWM_Init(void) { //**配置输出PWM波的端口**********************************…...
游戏行业销售数据分析可视化
完整源码项目包获取→点击文章末尾名片! 🌟分析: 可看出最近五年用户最喜爱的游戏类型依然还是Action-动作类(当然市场发行的也很多) Sports-运动类和Shooter-射击类顺序互换,但我估计现在大环境局势紧张可以会推动射击…...
微服务网关,如何选择?
什么是API网关 API网关(API Gateway)是微服务架构中的一个关键组件,它充当了客户端与后端服务之间的中间层。其主要功能包括请求路由、协议转换、负载均衡、安全认证、限流熔断等。通过API网关,客户端无需直接与多个微服务交互&a…...
Java开发提速秘籍:巧用Apache Commons Lang工具库
一、Java 开发效率之困 在当今数字化时代,Java 作为一门广泛应用的编程语言,在各类软件开发项目中占据着举足轻重的地位。无论是大型企业级应用、互联网平台,还是移动应用后端,都能看到 Java 的身影。然而,Java 开发者…...
多用户商城系统源码哪家好用?怎么选?
想拥有属于自己的多用户商城系统,但回头一看自己又是个技术小白,看着网上眼花缭乱的b2b2c商城系统,更是不知道如何选起?下面给大家分享一条较为成熟的选择思路,以作参考。 1、从需求上决定系统方向 企业在选型b2b2c商…...
聊聊如何实现Android 放大镜效果
一、前言 很久没有更新Android 原生技术内容了,前些年一直在做跨端方向开发,最近换工作用重新回到原生技术,又回到了熟悉但有些生疏的环境,真是感慨万分。 近期也是因为准备做地图交互相关的需求,功能非常复杂&#x…...
NVIDIA视频编解码
开源了两个项目:英伟达显卡视频编解码、jetson视频编解码。都是基于官方SDK进行的封装,由于官方自带的demo晦涩难懂并且每块都是独立的,我对SDK进行二次封装并形成了一套较为完整的视频编解码流程,调用简单,有完整的测…...
Mysql事务隔离级
什么是事务的隔离级别 数据库事务的隔离级别是指事务在并发执行时,如何控制事务之间相互影响的程度。它决定了多个事务并发执行时,事务中的操作对其他事务的可见性,进而影响数据的一致性和并发性。 为什么会有隔离级别的概念? …...
K210视觉识别模块
K210视觉识别模块是一款功能强大的AI视觉模块,以下是对其的详细介绍: 一、核心特性 强大的视觉识别功能:K210视觉识别模块支持多种视觉功能,包括但不限于人脸识别、口罩识别、条形码和二维码识别、特征检测、数字识别、颜色识别…...
springboot使用websocket
文章目录 一、概述1、简介 二、 使用1、引包2、配置处理器3、前端测试 一、概述 1、简介 简介略,附上官方文档,spring5和spring6的官方文档内容大致是一样的: https://docs.spring.io/spring-framework/docs/5.2.25.RELEASE/spring-framewo…...
线程池底部工作原理
线程池内部是通过线程和队列实现的,当我们通过线程池处理任务时: 如果线程池中的线程数量小于corePoolSize,无论是否有处于空闲的线程,都创建新的线程来处理被添加的任务。 如果线程池中的线程数量等于corePoolSize,…...
DevUI 2024 年度运营报告:开源生态的成长足迹与未来蓝图
在当今数字化飞速发展的时代,开源已成为推动技术创新与协作的重要力量。DevUI 作为开源领域的重要一员,其发展历程与成果备受关注。值此之际,GitCode 精心整理了 DevUI 年度运营报告,为您全面呈现 DevUI 社区在过去一年里的开源之…...
Mybatis面试题
Mybatis面试题 什么是 MyBatis?讲下 MyBatis 的缓存Mybatis 是如何进行分页的?分页插件的原理是什么?简述 Mybatis 的插件运行原理,以及如何编写一个插件?Mybatis 动态 sql 是做什么的?都有哪些动态 sql&am…...
Python获取系统运行时间
有时候想获取系统启动后到现在的运行时间,在Linux C可以使用clock_gettime()来获得,如下, #include <time.h> #include <stdio.h>int main() {struct timespec ts;clock_gettime(CLOCK_MONOTONIC, &ts);printf("syste…...
软考高级5个资格、中级常考4个资格简介及难易程度排序
一、软考高级5个资格 01、网络规划设计师 资格简介:网络规划设计师要求考生具备全面的网络规划、设计、部署和管理能力;该资格考试适合那些在网络规划和设计方面具有较好理论基础和较丰富从业经验的人员参加。 02、系统分析师 资格简介:系统分…...
【18】Word:明华中学-儿童医保❗
目录 题目 NO2 NO3 NO4 NO5 NO6 NO7 NO8 NO9 题目 NO2 布局→页面设置对话框→纸张方向:横向→纸张大小:A3 ;页面设置对话框:直接输入纸张大小的宽度和高度即可→页面设置对话框:上下左右边距→版式&…...
Vue 中实现修改数组,并保持页面渲染数据是响应式更改
如果你在 Vue 中使用数组并希望确保对数组项的修改是响应式的,直接替换数组项可能不会触发 Vue 的响应式更新。为了确保响应式更新,你可以使用 Vue 提供的 Vue.set() 方法(在 Vue 2 中)或使用 this.$set() 方法(在 Vue…...
MATLAB算法实战应用案例精讲-【数模应用】图形变换和复杂图形组合(附python和MATLAB代码实现)
目录 前言 算法原理 变换 1二维变换 1.1缩放 1.2 翻转 1.3剪切 1.4 旋转 2齐次坐标 2.1引入齐次坐标的原因 2.2 二维齐次坐标 2.3二维仿射变换 2.4逆变换 4组合变换 5三维变换(由二维变换推理而来) 5.1三维齐次坐标 5.2 三维仿射变换 5.3 缩放和平移 5.4…...
GCC支持Objective C的故事?Objective-C?GCC只能编译C语言吗?Objective-C 1.0和2.0有什么区别?
GCC支持Objective C的故事 Objective-C 主要由 Stepstone 公司的Brad Cox和 Tom Love 在1980 年左右发明。乔布斯离开苹果公司后成立了NeXT STEP公司, 买下了Objective-C 语言的授权。GCC对Objective-C语言的支持是在1992年加入的,具体是在GCC 1.3版本中…...
自动驾驶占用网格预测
文章目录 需要阅读的文献:github论文仓库论文idea提取BEVFormer 需要阅读的文献: ⭐[ECCV 2024] SparseOcc 纯稀疏3D占用网络和 RayIoU 评估指标 ECCV 2024|OSP:自动驾驶全新建模方法,端到端输出任意位置的占用结果 S…...
1.17组会汇报
STRUC-BENCH: Are Large Language Models Good at Generating Complex Structured Tabular Data? STRUC-BENCH:大型语言模型擅长生成复杂的结构化表格数据吗?23年arXiv.org 1概括 这篇论文旨在评估大型语言模型(LLMs)在生成结构…...
使用 Ansys Motor-CAD 的自适应模板加速创新
应对现代电机设计挑战 电机设计不断发展,Ansys 正在通过创新解决方案引领潮流,不断突破可能的界限。随着电动汽车、工业自动化和可再生能源系统的快速增长,对优化电机的需求从未如此之高。工程师面临着越来越大的压力,他们需要开发…...
用nginx正向代理https网站
目录 1. 缘起2. 部署nginx3. 测试3.1 http测试3.2 https测试4 给centos设置代理访问外网 1. 缘起 最近碰到了一个麻烦事情,就是公司的centos测试服务器放在内网环境,而且不能直接上外网,导致无法通过yum安装软件,非常捉急。 幸…...
PyTorch使用教程(6)一文讲清楚torch.nn和torch.nn.functional的区别
torch.nn 和 torch.nn.functional 在 PyTorch 中都是用于构建神经网络的重要组件,但它们在设计理念、使用方式和功能上存在一些显著的区别。以下是关于这两个模块的详细区别: 1. 继承方式与结构 torch.nn torch.nn 中的模块大多数是通过继承 torch.nn…...
图论DFS:黑红树
我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 往 {\color{Red} {\Huge 往} } 往 期 {\color{Green} {\Huge 期} } 期 文 {\color{Blue} {\Huge 文} } 文 章 {\color{Orange} {\Huge 章}} 章 DFS 算法:记忆化搜索DFS 算法…...
StarRocks 怎么让特定的SQL路由到FE master节点的
背景 本文基于 StarRocks 3.1.7 大家都知道对于Starrocks来说 FE 是分 master和follower的,而只有master节点才能对元数据进行写操作。但是为什么呢?哪里有体现呢? 这其中的原因在网上是搜不到的,所以大家只知道只有master节点才…...
蓝桥杯真题 - 公因数匹配 - 题解
题目链接:https://www.lanqiao.cn/problems/3525/learning/ 个人评价:难度 2 星(满星:5) 前置知识:调和级数 整体思路 题目描述不严谨,没说在无解的情况下要输出什么(比如 n n n …...
Java 8 Stream API
文章目录 Java 8 Stream API1. Stream2. Stream 的创建3. 常见的 Stream 操作3.1 中间操作3.2 终止操作 4. Stream 的并行操作 Java 8 Stream API Java 8 引入了 Stream API,使得对集合类(如 List、Set 等)的操作变得更加简洁和直观。Stream…...
AI刷题-还原原始字符串、大数和中的极值位距离
目录 一、还原原始字符串 问题描述 举例 输入格式 输出格式 输入 输出 输入 输出 输入 输出 输入 输出 输入 输出 输入 输出 数据范围 解题思路: 数据结构选择 最终代码: 运行结果: 二、大数和中的极值位距离 问题…...
Ubuntu20.04取消root账号自动登录的方法,触觉智能RK3568开发板演示
Ubuntu20.04默认情况下为root账号自动登录,本文介绍如何取消root账号自动登录,改为通过输入账号密码登录,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力…...
MySQL 数据库 :SQL 语句规约(不得使用外键与级联,一切外键概念必须在应用层解决。)
文章目录 I 强制规约表名限定数据订正禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。不得使用外键与级联,一切外键概念必须在应用层解决。使用 ISNULL() 来判断是否为 NULL 值NPE 问题不要使用 count(列名) 或 count(常量) 来替代 count(*)II 建议in 操作能避免则…...
深入理解 SQL 中的 DATEDIFF 函数
深入理解 SQL 中的 DATEDIFF 函数 DATEDIFF 函数在 SQL 中是一个用于计算两个日期之间差值的重要工具。不同数据库实现了不同版本的 DATEDIFF,它们在功能和语法上有所不同。本文将详细解析 DATEDIFF 的用法、数据库间差异、复杂场景中的应用,以及替代方…...
【Linux】15.Linux进程概念(4)
文章目录 程序地址空间前景回顾C语言空间布局图:代码1代码2代码3代码4代码5代码6代码7 程序地址空间前景回顾 历史核心问题: pid_t id fork(); if(id 0) else if(id>0) 为什么一个id可以放两个值呢?之前没有仔细讲。 C语言空间布局图&am…...
KubeSphere部署安装,接入KubeKey安装的k8s集群
KubeSphere安装接入KubeKey安装的k8s集群 文章目录 KubeSphere安装接入KubeKey安装的k8s集群 一.NFS安装配置1.服务器安装NFS服务2.下载并部署 NFS Subdir External Provisioner1).下载部署文件2).创建 NameSpace3).创建 RBAC 资源4).配置 deployment.yaml5).部署 Storage Clas…...
opencv3.4 ffmpeg3.4 arm-linux 交叉编译
一些依赖安装: sudo apt-get install pkg-config libgtk2.0-dev libavcodec-dev libavformat-dev libswscale-dev 交叉编译工具链准备:gcc-linaro-6.3.1 1、下载 https://github.com/FFmpeg/FFmpeg 解压后新建目录:Fmpeg-n3.4.13/ffmpeg…...
Java基础(二)
提示:这部分内容对逆向重要,需重点掌握。 1.常见数据类型 1.1 List系列 类似于Python中的列表 List是一个接口,接口下面有两个常见的类型(目的是可以存放动态的多个数据) ArrayList,连续的内存地址存储(内部自动扩容) -> Python列表的特点LinkedList,底层基于链表…...
网络IO与IO多路复用
一、网络IO基础 系统对象: 网络IO涉及用户空间调用IO的进程或线程以及内核空间的内核系统。例如,当进行read操作时,会经历两个阶段: 等待数据准备就绪。将数据从内核拷贝到进程或线程中。 多种网络IO模型的出现原因:…...
C# OpenCvSharp 部署3D人脸重建3DDFA-V3
目录 说明 效果 模型信息 landmark.onnx net_recon.onnx net_recon_mbnet.onnx retinaface_resnet50.onnx 项目 代码 下载 参考 C# OpenCvSharp 部署3D人脸重建3DDFA-V3 说明 地址:https://github.com/wang-zidu/3DDFA-V3 3DDFA_V3 uses the geometri…...
【机器学习实战入门】使用OpenCV进行性别和年龄检测
Gender and Age Detection Python 项目 首先,向您介绍用于此高级 Python 项目的性别和年龄检测中的术语: 什么是计算机视觉? 计算机视觉是一门让计算机能够像人类一样观察和识别数字图像和视频的学科。它面临的挑战大多源于对生物视觉有限的了解。计算机视觉涉及获取、处…...
Android SystemUI——StatusBar视图创建(六)
上一篇文章我们介绍了 StatusBar 的构建过程,在 makeStatusBarView() 中获得 FragmentHostManager,用来管理 StatusBar 的窗口。 一、状态栏视图 在得到 FragmentHostManager 实例对象之后,还会继续调用 addTagListener() 方法设置监听对象,然后获取 FragmentManager 并开…...
解决 Error: Invalid or corrupt jarfile day04_studentManager.jar 报错问题
在 Java 开发过程中,我们可能会遇到这样的报错信息:Error: Invalid or corrupt jarfile day04_studentManager.jar。这个错误通常表示 day04_studentManager.jar 文件可能已损坏或无效,下面将为大家详细介绍如何解决这个问题。 一、错误点分…...
《MambaIR:一种基于状态空间模型的简单图像修复基线方法》学习笔记
paper:2402.15648 目录 摘要 一、引言 1、模型性能的提升依赖于网络感受野的扩大: 2、全局感受野和高效计算之间存在固有矛盾: 3、改进版 Mamba的巨大潜力 4、Mamba 在图像修复任务中仍面临以下挑战: 5、方法 6、主要贡献…...
【转】厚植根基,同启新程!一文回顾 2024 OpenHarmony 社区年度工作会议精彩瞬间
在数字化浪潮奔腾不息的今天,开源技术已成为推动科技创新与产业发展的强大引擎。2025年1月10日-11日,OpenAtom OpenHarmony(开放原子开源鸿蒙,以下简称“OpenHarmony”或“开源鸿蒙”)社区2024年度工作会议于深圳盛大启…...
2024年我的技术成长之路
2024年我的技术成长之路 大家好,我是小寒。又到年底了,一年过得真快啊!趁着这次活动的机会,和大家聊聊我这一年在技术上的收获和踩过的坑。 说实话,今年工作特别忙,写博客的时间比去年少了不少。不过还是…...
最长递增子序列问题(Longest Increasing Subsequence),动态规划法解决,贪心算法 + 二分查找优化
问题描述:在一个大小乱序的数列中,找到一个最大长度的递增子序列,子序列中的数据在原始数列中的相对位置保持不变,可以不连续,但必须递增。 输入描述: 第一行输入数列的长度 n。(1 < n < 200) 第二…...
【Idea】编译Spring源码 read timeout 问题
Idea现在是大家工作中用的比较多的开发工具,尤其是做java开发的,那么做java开发,了解spring框架源码是提高自己技能水平的一个方式,所以会从spring 官网下载源码,导入到 Idea 工具并编译,但是发现build的时…...
基于 HTML5 Canvas 制作一个精美的 2048 小游戏--day2
为了使 2048 游戏的设计更加美观和用户友好,我们可以进行以下几项优化: 改善颜色方案:使用更温馨的颜色组合。添加动画效果:为方块的移动和合并添加渐变效果。优化分数显示:在分数增加时使用动画效果。 以下是改进后…...
服务化架构 IM 系统之应用 MQ
在微服务化系统中,存在三个最核心的组件,分别是 RPC、注册中心和MQ。 在前面的两篇文章(见《服务化架构 IM 系统之应用 RPC》和《服务化架构 IM 系统之应用注册中心》)中,我们站在应用的视角分析了普适性的 RPC 和 注…...
IoTDB 常见问题 QA 第四期
关于 IoTDB 的 Q & A IoTDB Q&A 第四期来啦!我们将定期汇总我们将定期汇总社区讨论频繁的问题,并展开进行详细回答,通过积累常见问题“小百科”,方便大家使用 IoTDB。 Q1:Java 中如何使用 SSL 连接 IoTDB 问题…...