工厂模式
工厂模式最重要的意义就是解耦
工厂方法模式的主要角色:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
实现:
使用工厂方法模式对上例进行改进,类图如下:

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

如图可以选择不同的策略去实现不同的方案
工厂+策略运用到项目
登录、支付
提供了很多种策略,都让spring容器管理
提供一个工厂:准备策略对象,根据参数提供对象
比如可以微信登录,手机验证码登录,账号密码登录
还可以微信支付,支付宝支付等
AIGC广告推荐项目
使用工厂模式准备一个策略对象,然后定义如协同过滤,基于内容,基于AI等策略模式
责任链模式
类似于SpringMVC中的过滤器链一样
在项目中的运用就是:
下订单:
参数校验 -> 补充订单信息 -> 计算相关信息 -> 订单入库
单例模式
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这样可以确保在整个应用程序中只有一个实例,从而避免了多次创建相同类的对象,节省了系统资源。
单例模式有几个关键点:
- 私有构造方法:确保其他类不能直接实例化该类。
- 静态变量:保存类的唯一实例。
- 静态方法:提供获取实例的全局访问点。
单例模式又分饿汉和懒汉,饿汉是在类加载的时候就加载了的,懒汉是等用到的时候才加载,故饿汉是没有线程安全问题的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class DatabaseConnection { private static DatabaseConnection instance = new DatabaseConnection();;
private DatabaseConnection() { System.out.println("创建数据库连接..."); }
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 { private static DatabaseConnection instance;
private DatabaseConnection() { System.out.println("创建数据库连接..."); }
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 { private static DatabaseConnection instance;
private DatabaseConnection() { System.out.println("创建数据库连接..."); }
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
|
@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; int maximumPoolSize = 40; 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
。由于进入同步块后,可能有其他线程已经创建了实例,所以需要再次检查,以确保只创建一个实例。
门面模式
当前端需要获取到后端的各种返回类型的结果时可以使用门面模式,使用场景为大屏,聚合搜索等
后端将各种数据统一封装到一次的返回中,前端也只需要发送一个统一的,聚合的单次请求,就可以从响应中一次取出所有数据,有减少网络带宽等好处。
适配器模式
其实与策略模式类似,适配器模式做的更多的是提供一个统一的接口,让更多新加入的实现方式都去实现这个接口,达到统一的效果。
和策略模式的区别:
- 目的不同:
- 策略模式是为了实现算法的切换,让算法独立于客户端变化。
- 适配器模式是为了解决接口不兼容问题,使原本不兼容的接口能够协同工作。
- 应用场景不同:
- 策略模式通常用在算法或行为需要经常切换的场景。
- 适配器模式通常用在想要使用一个已存在的类,但其接口不符合你的需求时。
- 结构上的不同:
- 策略模式中,上下文(Context)会持有一个策略对象的引用,通过这个引用来调用具体的策略。
- 适配器模式中,适配器(Adapter)会实现目标接口,内部持有一个待适配对象的引用,以此转换接口。
- 扩展性:
- 策略模式更容易扩展,添加新的策略只需实现策略接口即可。
- 适配器模式扩展性相对较差,因为它是为了解决特定的接口兼容问题,添加新的适配器可能需要修改现有的代码。