思维导图

在这里插入图片描述

工厂模式

平常编程过程中创建对象的方式就是new一个对象,每new一次对象调用者就相当于多知道了一个类,增加了耦合性。因此可以把对象的生产交给工厂去完成。

简单工厂模式

比如需要外科口罩和N95口罩,生成这两个对象可以交给工厂完成。

1
2
public abstract class Mask {
}
1
2
3
4
5
6
public class N95Mask extends Mask {
@Override
public String toString() {
return "这是N95口罩";
}
}
1
2
3
4
5
public class SurgicalMask extends Mask {
public String toString() {
return "这是医用口罩";
}
}

生产口罩的工厂:

1
2
3
4
5
6
7
8
9
10
11
12
public class MaskFactory {
public Mask create(String type) {
switch (type) {
case "Surgical":
return new SurgicalMask();
case "N95":
return new N95Mask();
default:
throw new IllegalArgumentException("Unsupported mask type");
}
}
}

测试:

1
2
3
4
5
6
7
8
9
public class Client {
@Test
public void test() {
// 简单工厂模式
MaskFactory factory = new MaskFactory();
System.out.println(factory.create("Surgical"));
System.out.println(factory.create("N95"));
}
}

输出:

1
2
这是医用口罩
这是N95口罩
  • 优点:
    1. 降低耦合
    2. 隐藏生产细节提高代码复用
  • 缺点:
    1. 生产产品过多时,工厂类过大,承担过多职责,会形成超级类,违背单一职责原则
    2. 需要生产新产品时,需要在工厂类中添加新分支,不符合开闭原则。类应该对修改封闭,添加新功能时应添加新的类

工厂方法模式

每个对象由单独的工厂来生产,符合开闭与单一职责原则

1
2
3
4
5
public class N95Factory {
public Mask create() {
return new N95Mask();
}
}
1
2
3
4
5
public class SurgicalFactory {
public Mask create() {
return new SurgicalMask();
}
}

测试:

1
2
3
4
5
6
7
8
9
public class Client {
@Test
public void test() {
SurgicalFactory surgicalFactory = new SurgicalFactory();
N95Factory n95Factory = new N95Factory();
System.out.println(surgicalFactory.create());
System.out.println(n95Factory.create());
}
}

抽象工厂模式

提取工厂方法模式中各个工厂的公共接口

1
2
3
public interface IFactory {
Mask create();
}

实现该接口:

1
2
3
4
5
6
public class N95MaskFactory implements IFactory {
@Override
public Mask create() {
return new N95Mask();
}
}
1
2
3
4
5
6
public class SurgicalFactory implements IFactory {
@Override
public Mask create() {
return new SurgicalMask();
}
}

测试:

1
2
3
4
5
6
7
@Test
public void test() {
IFactory factory = new SurgicalFactory();
System.out.println(factory.create());
factory = new N95MaskFactory();
System.out.println(factory.create());
}
  • 优点

    1. 调用者只需要使用接口而不需要知道接口的具体实现是哪个工厂,这样替换工厂变得很方便
    2. 很好的发挥了开闭原则,和依赖倒置原则
  • 缺点

    1. 需要新增抽象方法时,需要修改所有的抽象类,纵向扩展不方便

单例模式

单例模式分为

  • 饿汉式:变量在声明时就初始化
  • 懒汉式:变量在需要使用时初始化

饿汉式

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

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

缺点: 占内存、启动慢

懒汉式

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

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

以上写法线程不安全,在多线程调用时instance会被实例化多次因此需要给判空过程加锁

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

}
public static Singleton getInstance() {
synchronized (Singleton.class){
if (instance == null)
instance = new Singleton();
}
return instance;
}
}

改进后的问题:多个线程调用getInstance时每次都需要执行synchronized同步化方法,严重影响程序执行效率,所以在同步之前需要加上一层检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton {
private static Singleton instance = null;
private Singleton() {

}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}

改进后的问题: 由于JVM的底层优化,可能会改变指令执行顺序

1
instance = new Singleton();

这一行代码中,执行了三条重要的指令:

  • 分配对象的内存空间
  • 初始化对象
  • 将变量 instance 指向刚分配的内存空间

如果第二条指令和第三条指令发生了重排序,可能导致 instance 还未初始化时,其他线程提前通过双检锁外层的 null 检查,获取到“不为 null,但还没有执行初始化”的 instance 对象,发生空指针异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {

}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}

还可以使用静态内部类实现懒汉式:

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

private static Singleton getInstance() {
return SingletonHolder.instance;
}
}

静态内部类在使用时才会被加载,JVM保证了线程安全

建造者模式

将复杂的构建与其表示分离,使同样的构建过程能够创建不同的表示。
就比如制作一杯奶茶的过程是一样的,但是奶茶的规格、是否加冰、是否加珍珠是可选的。

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
public class MilkTea {
private String type;
private String size;
private boolean pearl;
private boolean ice;

private MilkTea() {}

private MilkTea(Builder builder) {
this.type = builder.type;
this.size = builder.size;
this.ice = builder.ice;
this.pearl = builder.pearl;
}

public String getType() {
return type;
}

public String getSize() {
return size;
}

public boolean isPearl() {
return pearl;
}

public boolean isIce() {
return ice;
}

public static class Builder {
private final String type;
private String size = "中杯";
private boolean pearl = true;
private boolean ice = false;

public Builder(String type) { // 必须配置的属性
this.type = type;
}

public Builder size(String size) {
this.size = size;
return this;
}

public Builder pearl(boolean pearl) {
this.pearl = pearl;
return this;
}

public Builder ice(boolean ice) {
this.ice = ice;
return this;
}

public MilkTea builde() {
return new MilkTea(this);
}
}
}

对于必须配置的属性,通过 Builder 的构造方法传入,可选的属性通过 Builder 的链式调用方法传入,如果不配置,将使用默认配置,也就是中杯、加珍珠、不加冰。根据不同的配置可以制作出不同的奶茶

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
public class User {
private void buyMilkTea() {
MilkTea milkTea = new MilkTea.Builder("原味").builde();
show(milkTea);

MilkTea chocolate = new MilkTea.Builder("巧克力味")
.ice(false)
.builde();
show(chocolate);

MilkTea strawberry = new MilkTea.Builder("草莓味")
.size("大杯")
.pearl(false)
.ice(true)
.builde();
show(strawberry);
}

private void show(MilkTea milkTea) {
String pearl;
if (milkTea.isPearl()) {
pearl = "加珍珠";
} else {
pearl = "不加珍珠";
}
String ice;
if (milkTea.isIce()) {
ice = "加冰";
} else {
ice = "不加冰";
}
System.out.println("一份" + milkTea.getSize() + "、"
+ pearl + "、"
+ ice + "的"
+ milkTea.getType() + "奶茶");
}

public static void main(String[] args) {
User user = new User();
user.buyMilkTea();
}
}

原型模式

对已有的对象进行拷贝,创建相同的对象更加方便

1
2
3
4
5
6
7
8
9
10
11
public class MilkTea {
public String type;
public boolean ice;

public MilkTea clone() {
MilkTea milkTea = new MilkTea();
milkTea.ice = this.ice;
milkTea.type = this.type;
return milkTea;
}
}

Java有一个语法糖,不需要手写clone,只需要实现Cloneable接口

1
2
3
4
5
6
7
8
9
10
public class MilkTea implements Cloneable{
public String type;
public boolean ice;

@NonNull
@Override
protected MilkTea clone() throws CloneNotSupportedException {
return (MilkTea) super.clone();
}
}

Git仓库

https://github.com/wuzheng228/designPatternGuide

参考

《深入浅出设计模式》