工厂模式

工厂模式最重要的意义就是解耦

工厂方法模式的主要角色:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

实现:

使用工厂方法模式对上例进行改进,类图如下:

image-20230521102122950

策略模式

只要代码中有很长的if-else或switch 分支判断都可以采用策略模式优化

image-20220913125209804

如图可以选择不同的策略去实现不同的方案

工厂+策略运用到项目

登录、支付

提供了很多种策略,都让spring容器管理
提供一个工厂:准备策略对象,根据参数提供对象

比如可以微信登录,手机验证码登录,账号密码登录

还可以微信支付,支付宝支付等

AIGC广告推荐项目

使用工厂模式准备一个策略对象,然后定义如协同过滤,基于内容,基于AI等策略模式

责任链模式

类似于SpringMVC中的过滤器链一样

在项目中的运用就是:

下订单:

参数校验 -> 补充订单信息 -> 计算相关信息 -> 订单入库

单例模式

单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这样可以确保在整个应用程序中只有一个实例,从而避免了多次创建相同类的对象,节省了系统资源。

单例模式有几个关键点:

  1. 私有构造方法:确保其他类不能直接实例化该类。
  2. 静态变量:保存类的唯一实例。
  3. 静态方法:提供获取实例的全局访问点。

单例模式又分饿汉和懒汉,饿汉是在类加载的时候就加载了的,懒汉是等用到的时候才加载,故饿汉是没有线程安全问题的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DatabaseConnection {
// 1. 私有静态变量,用于保存唯一实例
private static DatabaseConnection instance = new DatabaseConnection();;

// 2. 私有构造方法,防止外部直接实例化
private DatabaseConnection() {
// 这里可以进行一些数据库连接的初始化操作
System.out.println("创建数据库连接...");
}

// 3. 公共静态方法,用于获取实例
public static synchronized DatabaseConnection getInstance() {
// 如果实例为空,创建一个新实例
if (instance == null) {
instance = new DatabaseConnection();
}
// 返回实例
return instance;
}
}

以下是懒汉单例模式的 Java 代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DatabaseConnection {
// 1. 私有静态变量,用于保存唯一实例
private static DatabaseConnection instance;

// 2. 私有构造方法,防止外部直接实例化
private DatabaseConnection() {
// 这里可以进行一些数据库连接的初始化操作
System.out.println("创建数据库连接...");
}

// 3. 公共静态方法,用于获取实例
public static synchronized DatabaseConnection getInstance() {
// 如果实例为空,创建一个新实例
if (instance == null) {
instance = new DatabaseConnection();
}
// 返回实例
return instance;
}
}

在这个示例中,通过将构造方法设为私有,确保其他类不能直接实例化 DatabaseConnection类。通过提供静态方法 getInstance(),其他类可以获取 DatabaseConnection的唯一实例。在该方法中,如果实例为空,则创建一个新的实例,否则直接返回现有实例。

但是把synchronized加在getInstance()方法上锁的粒度过大了,故采用双重检测锁的方式优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class DatabaseConnection {
// 1. 私有静态变量,用于保存唯一实例
private static DatabaseConnection instance;

// 2. 私有构造方法,防止外部直接实例化
private DatabaseConnection() {
// 这里可以进行一些数据库连接的初始化操作
System.out.println("创建数据库连接...");
}

// 3. 公共静态方法,用于获取实例
public static DatabaseConnection getInstance() {
// 如果实例为空,创建一个新实例
if (instance == null) {
synchronized(instance){
if(instance == null){
instance = new DatabaseConnection();
}
}
}
// 返回实例
return instance;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 全局IO密集型线程池
*/
@Slf4j
public class GlobalThreadPool {
private static volatile ExecutorService instance;

// 私有构造方法,防止外部实例化
private GlobalThreadPool() {
}

// 获取全局唯一线程池实例
public static ExecutorService getInstance() {
// 双重检查锁定,确保线程安全
if (instance == null) {
synchronized (GlobalThreadPool.class) {
if (instance == null) {
instance = createThreadPool();
}
}
}
return instance;
}

// 创建线程池
private static ExecutorService createThreadPool() {
int corePoolSize = 32; // 核心线程数(IO密集型为CPU核数*4)
int maximumPoolSize = 40; // 最大线程数(IO密集型为CPU核数*5)
long keepAliveTime = 60L; // 空闲线程存活时间(秒)
TimeUnit unit = TimeUnit.SECONDS; // 时间单位
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100); // 任务队列
ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂
RejectedExecutionHandler handler = new CustomRejectedExecutionHandler(); // 自定义拒绝策略:用调用者所在的线程来执行任务,发送邮件,记录日志
return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}

// 关闭线程池
public static void shutdown() {
if (instance != null) {
instance.shutdown();
try {
if (!instance.awaitTermination(60, TimeUnit.SECONDS)) {
instance.shutdownNow();
if (!instance.awaitTermination(60, TimeUnit.SECONDS)) {
log.error("线程池未能正常关闭");
}
}
} catch (InterruptedException ie) {
instance.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
}

上述是我写的一个使用单例模式创建的全局唯一的线程池,采用了双重检查锁定

单例模式的同步问题

假设我们没有双重检查锁定,只在 getInstance 方法上使用同步:

1
2
3
4
5
6
public static synchronized ExecutorService getInstance() {
if (instance == null) {
instance = createThreadPool();
}
return instance;
}

在这种情况下,每次调用 getInstance 都会进入同步块,即使 instance 已经被创建。这会导致性能问题,因为同步块会导致线程争用,降低并发性能。

第二次检查:再次检查 instance 是否为 null。由于进入同步块后,可能有其他线程已经创建了实例,所以需要再次检查,以确保只创建一个实例。

门面模式

当前端需要获取到后端的各种返回类型的结果时可以使用门面模式,使用场景为大屏,聚合搜索等

后端将各种数据统一封装到一次的返回中,前端也只需要发送一个统一的,聚合的单次请求,就可以从响应中一次取出所有数据,有减少网络带宽等好处。

适配器模式

其实与策略模式类似,适配器模式做的更多的是提供一个统一的接口,让更多新加入的实现方式都去实现这个接口,达到统一的效果。

和策略模式的区别

  1. 目的不同
    • 策略模式是为了实现算法的切换,让算法独立于客户端变化。
    • 适配器模式是为了解决接口不兼容问题,使原本不兼容的接口能够协同工作。
  2. 应用场景不同
    • 策略模式通常用在算法或行为需要经常切换的场景。
    • 适配器模式通常用在想要使用一个已存在的类,但其接口不符合你的需求时。
  3. 结构上的不同
    • 策略模式中,上下文(Context)会持有一个策略对象的引用,通过这个引用来调用具体的策略。
    • 适配器模式中,适配器(Adapter)会实现目标接口,内部持有一个待适配对象的引用,以此转换接口。
  4. 扩展性
    • 策略模式更容易扩展,添加新的策略只需实现策略接口即可。
    • 适配器模式扩展性相对较差,因为它是为了解决特定的接口兼容问题,添加新的适配器可能需要修改现有的代码。