• 创建型模式:创建型模式关注对象的创建过程,主要包括简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。
  • 结构型模式:结构型模式关注对象之间的组合和协作关系,主要包括适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式和代理模式。
  • 行为型模式:行为型模式关注对象之间的交互和通信,主要包括模板方法模式、策略模式、命令模式、职责链模式、状态模式、观察者模式、中介者模式和访问者模式。

1、单例模式

  1. 饿汉模式:在类加载时就完成了初始化,但是加载比较慢,获取对象比较快

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Singleton_hungry {
    //构造函数私有化
    private Singleton_hungry(){}
    //定义私有的引用
    private static Singleton_hungry instance = new Singleton_hungry();
    //对外提供获取实例的方法
    public static Singleton_hungry getInstance(){
    return instance;
    }
    }
  2. 懒汉模式:懒汉式是延时加载,他是在需要的时候才创建对象

    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
    public class Singleton_lazy {
    //构造函数私有化
    private Singleton_lazy(){}
    private static Singleton_lazy instance;
    //对外提供获取实例的接口
    public static Singleton_lazy getInstance(){
    if (instance == null){
    instance = new Singleton_lazy();
    }
    return instance;
    }
    }

    // 懒汉模式存在线程安全问题,用双重校验锁去解决
    public class Singleton_lazy2 {
    private Singleton_lazy2(){};
    private static volatile Singleton_lazy2 instance;
    public Singleton_lazy2 getInstance(){
    //第一次校验singleton是否为空(已经创建实例不用竞争锁,提高效率)
    if(instance == null){
    synchronized (Singleton_lazy2.class){
    //第二次判断是因为假设有两个线程A、B,两个同时通过了第一个if,然后A获取了锁,进入然后判断doubleLock是null,他就实例化了doubleLock,然后他出了锁,
    //这时候线程B经过等待A释放的锁,B获取锁了,如果没有第二个判断,那么他还是会去new DoubleLock(),再创建一个实例,所以为了防止这种情况,需要第二次判断
    if (instance == null){
    instance = new Singleton_lazy2();
    }
    }
    }
    return instance;
    }
    }

在java内存模型中,volatile 关键字作用可以是保证可见性或者禁止指令重排。这里是因为 singleton = new Singleton() ,它并非是一个原子操作,事实上,在 JVM 中上述语句至少做了以下这 3 件事:
第一步是给 singleton 分配内存空间;
第二步开始调用 Singleton 的构造函数等,来初始化 singleton;
第三步,将 singleton 对象指向分配的内存空间(执行完这步 singleton 就不是 null 了)。
这里需要留意一下 1-2-3 的顺序,因为存在指令重排序的优化,也就是说第 2 步和第 3 步的顺序是不能保证的,最终的执行顺序,可能是 1-2-3,也有可能是 1-3-2。
如果是 1-3-2,那么在第 3 步执行完以后,singleton 就不是 null 了,可是这时第 2 步并没有执行,singleton 对象未完成初始化,它的属性的值可能不是我们所预期的值。假设此时线程 2 进入 getInstance 方法,由于 singleton 已经不是 null 了,所以会通过第一重检查并直接返回,但其实这时的 singleton 并没有完成初始化,所以使用这个实例的时候会报错

2、简单工厂模式

简单工厂模式又称为静态工厂方法模式,它通过一个工厂类来创建不同类型的对象,客户端只需要知道具体产品的名称即可。
简单工厂模式的优点是简单易用,但是如果需要添加新的产品类型,则需要修改工厂类的代码,违反了开闭原则

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SimpleFactory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
} else {
return null;
}
}
}
// 使用
Product product = SimpleFactory.createProduct("A");

3、工厂方法模式

工厂方法模式通过定义一个抽象的工厂接口和多个具体的工厂类来创建不同类型的对象,每个具体的工厂类只负责创建一种产品类型。客户端只需要知道具体工厂类的名称即可。
工厂方法模式的优点是更加灵活,可以更方便地添加新的产品类型,但是需要创建多个工厂类,增加了系统的复杂度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 抽象工厂接口
public interface Factory {
Product createProduct();
}
// 具体工厂类
public class ConcreteFactoryA implements Factory {
public Product createProduct() {
return new ConcreteProductA();
}
}

public class ConcreteFactoryB implements Factory {
public Product createProduct() {
return new ConcreteProductB();
}
}
// 使用
Factory factory = new ConcreteFactoryA();
Product product = factory.createProduct();

4、抽象工厂模式

抽象工厂模式通过定义一个抽象的工厂接口和多个具体的工厂类来创建不同类型的对象,每个具体的工厂类负责创建一组相关的产品类型。客户端需要知道具体的工厂类和产品族的名称。
抽象工厂模式的优点是可以创建一组相关的产品,而且更加灵活,但是需要创建多个工厂类和产品族,增加了系统的复杂度

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
// 抽象工厂接口
public interface Factory {
Product createProductA();
Product createProductB();
}

// 具体工厂类
public class ConcreteFactory1 implements Factory {
public Product createProductA() {
return new ConcreteProductA1();
}

public Product createProductB() {
return new ConcreteProductB1();
}
}

public class ConcreteFactory2 implements Factory {
public Product createProductA() {
return new ConcreteProductA2();
}

public Product createProductB() {
return new ConcreteProductB2();
}
}

// 使用
Factory factory = new ConcreteFactory1();
Product productA = factory.createProductA();
Product productB = factory.createProductB();

5、原型模式

原型模式是一种创建型设计模式,它允许通过克隆现有对象来创建新对象,而不是通过实例化类来创建。这种模式适用于需要创建多个相似对象的情况,因为它可以减少对象创建的开销和复杂性
原型模式的优点包括:提高了对象创建的效率,减少了重复的代码,简化了对象创建的过程。缺点是如果原型对象的属性较为复杂或包含引用类型的属性,则需要进行深度克隆,否则会影响对象的正确性。

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
public class Prototype implements Cloneable {
private String name;
private List<String> list;

public Prototype(String name, List<String> list) {
this.name = name;
this.list = list;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<String> getList() {
return list;
}

public void setList(List<String> list) {
this.list = list;
}

@Override
public Prototype clone() throws CloneNotSupportedException {
Prototype prototype = (Prototype) super.clone();
// 对包含的引用对象进行深度克隆
prototype.list = new ArrayList<>(this.list);
return prototype;
}
}

6、装饰者模式

装饰者模式是一种结构型设计模式,它允许动态地向一个对象添加新的行为,同时又不影响其原有行为。这种模式的关键在于装饰者类和被装饰者类实现相同的接口,使得装饰者对象可以代替被装饰者对象,从而实现透明性。
在装饰者模式中,有四个角色:

  1. 抽象组件(Component)
  2. 具体组件(ConcreteComponent)
  3. 抽象装饰者(Decorator):实现抽象组件
  4. 具体装饰者(ConcreteDecorator):继承抽象装饰者

核心思想是:将对象的行为分离出来,使得每个行为都可以被单独地扩展和修改,而不会影响到其他行为。这种分离行为的方式是通过将行为封装到不同的类中,并将这些类组合起来形成一个对象链来实现

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
// 抽象组件
interface Component {
void operation();
}

// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("具体组件的操作");
}
}

// 抽象装饰者
class Decorator implements Component {
private Component component;

public Decorator(Component component) {
this.component = component;
}

@Override
public void operation() {
component.operation();
}
}

// 具体装饰者A
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}

@Override
public void operation() {
super.operation();
addedBehavior();
}

private void addedBehavior() {
System.out.println("具体装饰者A的操作");
}
}

// 具体装饰者B
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}

@Override
public void operation() {
super.operation();
addedBehavior();
}

private void addedBehavior() {
System.out.println("具体装饰者B的操作");
}
}

// 客户端
public class Client {
public static void main(String[] args) {
ConcreteComponent concreteComponent = new ConcreteComponent();
ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(concreteComponent);
ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB(concreteDecoratorA);
concreteDecoratorB.operation();
}
}

7、代理模式

代理模式是一种结构型设计模式,主要作用是在访问对象时引入一定程度的间接性,以便更好地控制访问和管理对象。
Java中实现代理模式通常有两种方式:静态代理和动态代理
静态代理是指在编译时就已经确定代理类和被代理类的关系,代理类和被代理类都必须实现相同的接口或者继承相同的父类。静态代理的优点是简单易懂,缺点是需要为每一个被代理类写一个代理类,增加了代码量

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
// 抽象主题接口
interface Subject {
void request();
}

// 实际主题类
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject.request()");
}
}

// 代理类
class ProxySubject implements Subject {
private RealSubject realSubject;

public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}

@Override
public void request() {
System.out.println("ProxySubject.request()");
realSubject.request();
}
}

// 使用
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.request();
}
}

动态代理是指在运行时动态生成代理类,不需要为每一个被代理类写一个代理类,可以节省代码量。Java中提供了两种动态代理机制:基于接口的动态代理和基于类的动态代理

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
// 基于接口的动态代理:
// 抽象主题接口
interface Subject {
void request();
}

// 实际主题类
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject.request()");
}
}

// InvocationHandler实现类
class ProxyHandler implements InvocationHandler {
private Object target;

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

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("ProxySubject.request()");
Object result = method.invoke(target, args);
return result;
}
}

// 使用
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxyHandler proxyHandler = new ProxyHandler(realSubject);
Subject proxySubject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
proxyHandler);
proxySubject.request();
}
}

8、享元模式

享元模式是一种结构型设计模式,它用于减少系统中对象的数量,从而提高系统的性能和效率。在享元模式中,多个对象共享相同的状态和数据,这些状态和数据通常是不可变的,因此可以被多个对象共享使用

  1. 抽象享元(Flyweight):定义享元对象的接口,通常包含一个操作方法。
  2. 具体享元(ConcreteFlyweight):实现抽象享元接口,包含内部状态和外部状态两部分,其中内部状态可以被共享,而外部状态需要在使用时传递给享元对象。
  3. 享元工厂(FlyweightFactory):用于创建和管理享元对象,通常使用工厂模式来实现。

在Java中,可以使用HashMap来存储享元对象,使用享元对象的属性作为HashMap的key,将享元对象作为HashMap的value。这样,当需要创建新的对象时,先在HashMap中查找是否已经存在相同属性的对象,如果存在,则返回已有的对象,否则创建新的对象并将其加入HashMap中

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
import java.util.HashMap;

public class FlyweightFactory {
private static final HashMap<String, Flyweight> flyweights = new HashMap<>();

public static Flyweight getFlyweight(String key) {
Flyweight flyweight = flyweights.get(key);
if (flyweight == null) {
flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}

public interface Flyweight {
void operation();
}

public class ConcreteFlyweight implements Flyweight {
private String key;

public ConcreteFlyweight(String key) {
this.key = key;
}

public void operation() {
System.out.println("ConcreteFlyweight with key " + key + " is performing operation.");
}
}

public class Client {
public static void main(String[] args) {
Flyweight flyweight1 = FlyweightFactory.getFlyweight("key1");
Flyweight flyweight2 = FlyweightFactory.getFlyweight("key2");
Flyweight flyweight3 = FlyweightFactory.getFlyweight("key1");

flyweight1.operation();
flyweight2.operation();
flyweight3.operation();

System.out.println(flyweight1 == flyweight3); // true
}
}

9、策略模式

策略模式是一种行为型设计模式,它允许选择算法的不同实现。在策略模式中,算法被封装在单独的类中,并且可以在运行时进行切换,以便在不同的情况下使用不同的算法
在策略模式中,有三个角色:策略接口、具体策略类和上下文类
上下文类包含一个策略接口的引用,并且可以在运行时切换具体策略类的实现

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
// 策略接口
public interface SortStrategy {
void sort(int[] data);
}
// 策略实现
public class BubbleSortStrategy implements SortStrategy {
@Override
public void sort(int[] data) {
// 冒泡排序算法实现
}
}

public class QuickSortStrategy implements SortStrategy {
@Override
public void sort(int[] data) {
// 快速排序算法实现
}
}

// 上下文类
public class Sorter {
private SortStrategy strategy;

public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}

public void sort(int[] data) {
strategy.sort(data);
}
}

// 使用
int[] data = {4, 2, 1, 5, 3};
Sorter sorter = new Sorter();
sorter.setStrategy(new BubbleSortStrategy());
sorter.sort(data);

sorter.setStrategy(new QuickSortStrategy());
sorter.sort(data);

10、观察者模式

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,所有依赖于它的观察者对象都会得到通知并自动更新
实现观察者模式需要定义两个接口:主题接口和观察者接口。主题接口定义了注册、删除和通知观察者的方法,观察者接口定义了更新自己状态的方法
最典型的应用场景就是消息订阅系统(如微博)

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
// 被观察者类,即微博用户或话题
class Subject {
private List<Observer> observers = new ArrayList<>(); // 观察者列表

public void attach(Observer observer) { // 添加观察者
observers.add(observer);
}

public void detach(Observer observer) { // 移除观察者
observers.remove(observer);
}

public void notifyObservers(String message) { // 通知所有观察者
for (Observer observer : observers) {
observer.update(message);
}
}
}

// 观察者接口,即微博用户
interface Observer {
void update(String message);
}

// 具体观察者类,即关注了某个微博用户或话题的其他用户
class ConcreteObserver implements Observer {
private String name;

public ConcreteObserver(String name) {
this.name = name;
}

@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}

// 测试代码
public class Test {
public static void main(String[] args) {
Subject subject = new Subject();

ConcreteObserver user1 = new ConcreteObserver("User1");
ConcreteObserver user2 = new ConcreteObserver("User2");
ConcreteObserver topic1 = new ConcreteObserver("Topic1");

subject.attach(user1);
subject.attach(user2);
subject.attach(topic1);

subject.notifyObservers("Hello world!"); // 发布新消息

subject.detach(user2); // 取消关注

subject.notifyObservers("Goodbye!"); // 发布新消息
}
}