常用设计模式

单例模式

预加载

1
2
3
4
5
6
7
8
9
10
public class PreloadSingleton {
private static PreloadSingleton instance = new PreloadSingleton();

//私有化构造函数
private PreloadSingleton(){};

public static PreloadSingleton getInstance(){
return instance;
}
}

缺点:可能会浪费内存,因为没有使用该对象时,就已经加载到内存。

懒加载

1
2
3
4
5
6
7
8
9
10
11
12
public class LazySingleton {
private static LazySingleton instance = null;

private LazySingleton(){};

public static LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}

缺点:可能出现线程安全问题,if操作时可能多个线程同时进入。指令重排也可能让线程拿到未初始化的对象。

双检索单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Singleton {
// volatile 确保可见性,防止指令重排序
private static volatile Singleton instance;

// 私有构造函数,防止外部直接实例化
private Singleton() {

}

// 提供对外获取实例的方法
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}

volatile关键字

volatile关键字,确保多线程环境中对该变量的写操作能够立刻被其他线程可见,并防止 JVM 的指令重排序优化。在没有volatile的情况下,可能会发生以下情况:

  • 线程A部分初始化了 Singleton 对象,但尚未完全构造好对象的状态。
  • 线程B看到非空的 instance,但却获取到了未完全初始化的对象。

使用 volatile 关键字避免了这种情况。

双重检查锁定

  • 第一次检查:在进入同步块之前,检查 instance 是否为空。如果不为空,直接返回实例,避免每次都进入同步块,提升性能。
  • 同步块:确保多个线程在第一次创建实例时不发生竞争。只有 instancenull 时,才进入同步块。
  • 第二次检查:在同步块内部再次检查 instance 是否为空。原因是即使多个线程同时通过第一次检查,进入同步块的线程也只有一个能够执行到创建实例的代码。

代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,客户端通过代理对象访问目标对象。代理模式可以用于延迟对象的创建、控制对对象的访问,或者添加额外的功能。

静态代理

静态代理由自己手动创建。代理类和目标类实现相同的接口,通过代理类来调用目标类的方法。

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
// 公共接口
interface Service {
void request();
}

// 目标类,实际业务逻辑实现
class RealService implements Service {
@Override
public void request() {
System.out.println("Executing request in RealService");
}
}

// 代理类
class ServiceProxy implements Service {
private RealService realService;

public ServiceProxy(RealService realService) {
this.realService = realService;
}

@Override
public void request() {
System.out.println("Proxy: Before request");
realService.request(); // 调用实际业务逻辑
System.out.println("Proxy: After request");
}
}

// 客户端代码
public class Client {
public static void main(String[] args) {
RealService realService = new RealService();
Service serviceProxy = new ServiceProxy(realService);
serviceProxy.request(); // 通过代理访问实际服务
}
}

动态代理

动态代理是在运行时动态生成代理类,不需要手动编写代理类。Java提供了基于 java.lang.reflect.Proxy 的动态代理机制,代理类可以在运行时动态创建,并通过 InvocationHandler 来拦截方法调用。

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
// 公共接口
interface Service {
void request();
}

// 目标类
class RealService implements Service {
@Override
public void request() {
System.out.println("Executing request in RealService");
}
}

// 代理类的调用处理器
class ServiceInvocationHandler implements InvocationHandler {
private Object target;

public ServiceInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy: Before request");
Object result = method.invoke(target, args); // 调用实际目标对象的方法
System.out.println("Proxy: After request");
return result;
}
}

// 客户端代码
public class Client {
public static void main(String[] args) {
// 目标对象
RealService realService = new RealService();

// 生成代理对象
Service serviceProxy = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new ServiceInvocationHandler(realService)
);

serviceProxy.request(); // 通过动态代理访问实际服务
}
}

CGLIB代理

CGLIB 代理是通过继承的方式为目标对象创建代理,适用于没有实现接口的类。它通过生成目标类的子类来实现代理功能。

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
// 目标类
class RealService {
public void request() {
System.out.println("Executing request in RealService");
}
}

// CGLIB代理类
class ServiceMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB Proxy: Before request");
Object result = proxy.invokeSuper(obj, args); // 调用实际方法
System.out.println("CGLIB Proxy: After request");
return result;
}
}

// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealService.class);
enhancer.setCallback(new ServiceMethodInterceptor());

// 创建代理对象
RealService serviceProxy = (RealService) enhancer.create();
serviceProxy.request(); // 通过CGLIB代理访问实际服务
}
}

策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。

假设有一个场景:我们想实现一个支付系统,用户可以选择不同的支付方式(如支付宝、微信支付和信用卡支付)。

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
55
56
57
58
59
60
61
62
63
// 策略接口:定义支付方式的行为
interface PaymentStrategy {
void pay(int amount);
}

// 具体策略:支付宝支付
class AliPayStrategy implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("Using Alipay to pay " + amount + " yuan.");
}
}

// 具体策略:微信支付
class WeChatPayStrategy implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("Using WeChatPay to pay " + amount + " yuan.");
}
}

// 具体策略:信用卡支付
class CreditCardPayStrategy implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("Using CreditCard to pay " + amount + " yuan.");
}
}

// 上下文类:持有策略引用,根据不同策略执行相应的支付操作
class PaymentContext {
private PaymentStrategy paymentStrategy;

// 设置具体的支付策略
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}

// 执行支付
public void executePayment(int amount) {
paymentStrategy.pay(amount);
}
}

// 客户端代码
public class Client {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();

// 使用支付宝支付
context.setPaymentStrategy(new AliPayStrategy());
context.executePayment(100); // 输出: Using Alipay to pay 100 yuan.

// 使用微信支付
context.setPaymentStrategy(new WeChatPayStrategy());
context.executePayment(200); // 输出: Using WeChatPay to pay 200 yuan.

// 使用信用卡支付
context.setPaymentStrategy(new CreditCardPayStrategy());
context.executePayment(300); // 输出: Using CreditCard to pay 300 yuan.
}
}

模版方法模式

在模板方法模式中,父类提供一个模板方法,定义了算法的骨架,具体的实现步骤由子类提供。它是一种代码复用的有效手段,适用于算法结构固定而部分实现不同的场景。

抽象类(AbstractClass):定义模板方法,确定算法的骨架,并且包含算法中的某些步骤的具体实现或抽象方法。

具体类(ConcreteClass):实现抽象类中定义的抽象方法,完成算法中的具体步骤。

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// 抽象类:饮料
abstract class Beverage {

// 模板方法:制作饮料的流程
public final void prepareBeverage() {
boilWater();
brew(); // 抽象方法,由子类实现
pourInCup();
if (needsCondiments()) { // 钩子方法,子类可以选择性地覆写
addCondiments();
}
}

// 具体实现:烧水
public void boilWater() {
System.out.println("Boiling water");
}

// 具体实现:倒入杯中
public void pourInCup() {
System.out.println("Pouring into cup");
}

// 抽象方法:泡制饮料
public abstract void brew();

// 抽象方法:添加调料
public abstract void addCondiments();

// 钩子方法:是否需要添加调料,默认需要,子类可以覆盖
public boolean needsCondiments() {
return true;
}
}

// 具体类:茶
class Tea extends Beverage {
@Override
public void brew() {
System.out.println("Steeping the tea");
}

@Override
public void addCondiments() {
System.out.println("Adding lemon");
}
}

// 具体类:咖啡
class Coffee extends Beverage {
@Override
public void brew() {
System.out.println("Dripping coffee through filter");
}

@Override
public void addCondiments() {
System.out.println("Adding sugar and milk");
}

// 覆盖钩子方法:不需要调料
@Override
public boolean needsCondiments() {
return false;
}
}

// 客户端代码
public class Client {
public static void main(String[] args) {
Beverage tea = new Tea();
tea.prepareBeverage();
// 输出:
// Boiling water
// Steeping the tea
// Pouring into cup
// Adding lemon

Beverage coffee = new Coffee();
coffee.prepareBeverage();
// 输出:
// Boiling water
// Dripping coffee through filter
// Pouring into cup
}
}

常用设计模式
https://arthur-boyle.github.io/2024/09/08/常用设计模式/
作者
Arthur-Boyle
发布于
2024年9月8日
许可协议