设计模式深入学习



单例模式

1. 饿汉式

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
/**
* 饿汉式(静态变量)
*/
public class SingletonDemo1 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
}

}

class Singleton {
// 1.构造器私有化,外部能new
private Singleton() {

}

// 2.本类内部创建对象实例
private final static Singleton instance = new Singleton();

// 3.提供一个公用的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}

}


// =============================> 优化

// 2.本类内部创建对象实例
private static Singleton2 instance;

// 静态代码块中实例化对象
static {
instance = new Singleton2();
}

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
/**
* 懒汉式(线程不安全)
* 1.起到了懒加载效果,但仅限于单线程下
* 2.如果在多线程下执行,很有可能会产生多个实例
* 3.实际开发中,不推荐使用
*/
public class SingletonDemo3 {
public static void main(String[] args) {
Singleton5 instance1 = Singleton5.getInstance();
Singleton5 instance2 = Singleton5.getInstance();
System.out.println(instance1 == instance2);
}
}

class Singleton3 {
private static Singleton3 instance;

private Singleton3() {
}

/**
* 提供一个静态的公共方法,当使用到该方法时,才会去创建instance
* 可以加synchronized锁上方法,但是效率太低
*/
public static Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
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
/**
* 懒汉式(线程安全)
*/
class Singleton5 {
/**
* 防止指令重排
* 变量共享可见
* 非原子性
*/
private static volatile Singleton5 instance;

private Singleton5() {
}

/**
* 提供一个静态的公共方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
* 同时保证了效率,推荐使用
*/
public static Singleton5 getInstance() {
// 判断是否有了Singleton4对象
if (instance == null) {
// 同步锁
synchronized (Singleton5.class) {
// 保证单例
if (instance == null) {
instance = new Singleton5();
}
}
}
return instance;
}
}

3. 静态内部类

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 SingletonDemo5 {
public static void main(String[] args) {
Singleton5 instance1 = Singleton5.getInstance();
Singleton5 instance2 = Singleton5.getInstance();
System.out.println("静态内部类,推荐使用:" + (instance1 == instance2));
}
}

class Singleton5 {
private static Singleton5 instance;

private Singleton5() {
}

/**
* 提供一个静态代码类,里面有静态属性
*/
private static class SingletonInstance {
private static final Singleton5 INSTANCE = new Singleton5();
}

/**
* 提供一个静态的公共方法,直接返回SingletonInstance.INSTANCE
*/
public static Singleton5 getInstance() {
return SingletonInstance.INSTANCE;
}
}

4. 枚举

  • Effective Java 作者 Josh Bloch 提倡的方式
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
/**
* @author Blue
* @date 2020/11/2
**/
public class SingletonDemo {

public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance == instance2);

System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());

instance.sayOK();
}
}

/**
* 使用枚举,可以实现单例, 推荐
*/
enum Singleton {
/**
* 属性
*/
INSTANCE;

public void sayOK() {
System.out.println("ok~");
}
}

5.单例模式在 JDK 应用的源码分析

  • java.lang.Runtime 就是经典的单例模式(饿汉式)

原型模式

1.基本介绍

原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

原型模式是一种 创建型设计模式,允许一个 对象再创建另外一个可定制的对象,无需知道如何创建的细节

工作原理是: 通过将一个原型对象传给那个要发动创建的对象,这个 要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 原型对象.clone()

2.传统方式解决克隆羊问题

克隆羊问题:
现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 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
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* @author Blue
* @date 2020/11/2
**/
public class Client {

public static void main(String[] args) {

//传统的方法
Sheep sheep = new Sheep("tom", 1, "白色");

Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());


Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//....
}

}

```

- 传统的方式的优缺点

- 优点是比较好理解,简单易操作。
- 在创建新的对象时,总是需要 <font style='background: #eeeeee;' color='#ff7043'>重新获取原始对象的属性</font>,如果创建的对象比较复杂时,效率较低
- 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
- 改进的思路分析:
- Object 类提供了一个 <font style='background: #eeeeee;' color='#ff7043'>clone()方法</font>,该方法可以将一个 <font style='background: #eeeeee;' color='#ff7043'>Java 对象复制一份</font>,
- 实现 clone 的 Java 类必须要实现一个 <font style='background: #eeeeee;' color='#ff7043'>接口 Cloneable</font>,该接口表示 <font style='background: #eeeeee;' color='#ff7043'>该类能够复制且具有复制的能力</font> =>原型模式

<font style='background: #eeeeee;' color='#3399ea'>3.原型模式解决克隆羊问题的应用实例</font>

```java
/**
* @author Blue
* @date 2020/11/3
* 克隆羊类 实现 Cloneable接口
**/
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
private String address = "蒙古羊";
public Sheep friend; //是对象, 克隆是会如何处理, 默认是浅拷贝

public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}

@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", address='" + address + '\'' +
", friend=" + friend +
'}';
}

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

//克隆该实例,使用默认的 clone 方法来完成
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
e.getStackTrace();
}
return sheep;
}
}
1
2
3
4
5
6
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.friend = new Sheep("jack", 2, "黑色");
Sheep sheep2 = (Sheep) sheep.clone();

// sheep2 = Sheep{name='tom', age=1, color='白色', address='蒙古羊', friend=Sheep{name='jack', age=2, color='黑色', address='蒙古羊', friend=null}}
System.out.println("sheep2 =" + sheep2 + "sheep2.friend=");

4.原型模式在 Spring 框架中源码分析

Spring 中原型 bean 的创建,就是原型模式的应用(后面代码debug)

5.深入讨论-浅拷贝和深拷贝

  • 浅拷贝的介绍

    • 对于数据类型是 基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该 属性值复制一份给新的对象
    • 对于数据类型是 引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行 引用传递,也就是只是将该 成员变量的引用值(内存地址)复制一份给新的对象。因为 实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
    • 前面我们克隆羊就是浅拷贝
    • 浅拷贝是使用 默认的 clone()方法来实现 sheep = (Sheep) super.clone();
  • 深拷贝基本介绍

    • 复制对象的所有基本数据类型的成员变量值
    • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
    • 深拷贝实现方式:1.重写 clone 方法来实现深拷贝 2.通过对象序列化实现深拷贝(推荐)

6.深拷贝应用实例

7.原型模式的注意事项和细节

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
  • 不用重新初始化对象,而是动态地获得对象运行时的状态
  • 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
  • 在实现深克隆的时候可能需要比较复杂的代码
  • 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则.

建造者模式

1
2
3
4
前言(需要建房子):
这一过程为打桩、砌墙、封顶房子有各种各样的,
比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的.
请编写程序,完成需求.

1.传统模式解决盖房需求

  • 思路分析

  • 代码演示

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
public abstract class AbstractHouse {

//打地基
public abstract void buildBasic();
//砌墙
public abstract void buildWalls();
//封顶
public abstract void roofed();

public void build() {
buildBasic();
buildWalls();
roofed();
}

}

// ==========================================================

public class CommonHouse extends AbstractHouse {

@Override
public void buildBasic() {
System.out.println(" 普通房子打地基 ");
}

@Override
public void buildWalls() {
System.out.println(" 普通房子砌墙 ");
}

@Override
public void roofed() {
System.out.println(" 普通房子封顶 ");
}

}
  • 问题分析

    • 优点是比较好理解,简单易操作。

    • 设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好. 也就是说,这种设计方案,把产品(即:房子) 和 创建产品的过程(即:建房子流程) 封装在一起,耦合性增强了。

    • 解决方案:将产品和产品建造过程解耦 => 建造者模式.

2.基本介绍

1
2
3
4
5
6
建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。
它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法
可以构造出不同表现(属性)的对象。

建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过
指定复杂对象的类型和内容就可以构建它们, 用户不需要知道内部的具体构建细节。

3.四个角色

  • Product(产品角色): 一个具体的产品对象。

  • Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。

  • ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。

  • Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。

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
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
public class Client {

public static void main(String[] args) {
//盖普通房子
CommonHouse commonHouse = new CommonHouse();

//准备创建房子的指挥者
HouseDirector houseDirector = new HouseDirector(commonHouse);

//完成盖房子,返回产品(普通房子)
House house = houseDirector.constructHouse();

//盖高楼
HighBuilding highBuilding = new HighBuilding();
//重置建造者houseDirector.setHouseBuilder(highBuilding);
//完成盖房子,返回产品(高楼) houseDirector.constructHouse();
}
}


=====================================================


public class CommonHouse extends HouseBuilder {

@Override
public void buildBasic() {
System.out.println(" 普通房子打地基 5 米 ");
}

@Override
public void buildWalls() {
System.out.println(" 普通房子砌墙 10cm ");
}

@Override
public void roofed() {
System.out.println(" 普通房子屋顶 ");
}

}

==================================================

public class HighBuilding extends HouseBuilder {

@Override
public void buildBasic() {
System.out.println(" 高楼的打地基 100 米 ");
}

@Override
public void buildWalls() {

}

@Override
public void roofed() {
System.out.println(" 高楼的透明屋顶 ");
}

}


==================================================


// 产 品 ->Product
public class House {

private String baise;
private String wall;
private String roofed;

public String getBaise() {
return baise;
}
public void setBaise(String baise) {
this.baise = baise;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getRoofed() {
return roofed;
}
public void setRoofed(String roofed) {
this.roofed = roofed;
}

}



==================================================

// 抽象的建造者
public abstract class HouseBuilder {

protected House house = new House();

//将建造的流程写好, 抽象的方法
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void roofed();

//建造房子好, 将产品(房子) 返回
public House buildHouse() {
return house;
}

}


==================================================


//指挥者,这里去指定制作流程,返回产品
public class HouseDirector {

HouseBuilder houseBuilder = null;

//构造器传入 houseBuilder
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}

//通过 setter 传入 houseBuilder
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}

//如何处理建造房子的流程,交给指挥者
public House constructHouse() {
houseBuilder.buildBasic();
houseBuilder.buildWalls();
houseBuilder.roofed();
return houseBuilder.buildHouse();
}

}

适配器模式(Adapter Pattern)

1.基本介绍

  • 将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)

  • 属于 结构型模式

  • 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

2.工作原理

  • 将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
  • 从用户的角度看不到被适配者,是 解耦的
  • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法

类适配器模式

基本介绍:Adapter 类,通过继承被适配者 类,实现目标类接口,完成 被适配者 到 目标 的适配

代码实现

首先有一个已存在的将被适配的类

1
2
3
4
5
public class Adaptee {
public void adapteeRequest() {
System.out.println("被适配者的方法");
}
}

定义一个目标类接口

1
2
3
public interface Target {
void request();
}

如果通过一个适配器类,实现 Target 接口,同时 继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adaptee 中 adapteeRequest() 即可实现

1
2
3
4
5
6
7
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
System.out.println("Target目标方法");
super.adapteeRequest();
}
}

测试

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
Target adapterTarget = new Adapter();
adapterTarget.request();
// 输出:concreteTarget目标方法
// 被适配者的方法
}
}

对象适配器模式

基本介绍:对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器

修改 Adapter 适配器类 即可

1
2
3
4
5
6
7
8
9
10
11
public class Adapter implements Target{

// 关键:适配者是对象适配器的一个属性
private Adaptee adaptee = new Adaptee();

@Override
public void request() {
System.out.println("Target目标方法");
adaptee.adapteeRequest();
}
}

接口适配器模式

基本介绍:适配器模式(Default Adapter Pattern)或缺省适配器模式,适用于一个接口不想使用其所有的方法的情况

核心思路:当 不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并 为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可 有选择地覆盖父类的某些方法来实现需求

提供接口

1
2
3
4
5
6
public interface InterfaceProvide {
void run1();
void run2();
void run3();
void run4();
}

实现接口,默认实现空方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class AbsAdapter implements InterfaceProvide{
@Override
public void run1() {
// 默认实现
}

@Override
public void run2() {

}

@Override
public void run3() {

}

@Override
public void run4() {

}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {

public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter() {
//只需要去覆盖我们 需要使用 接口方法
@Override
public void run1() {
System.out.println("使用了 run1 的方法");
}
};
// 输出:使用了 run1 的方法
absAdapter.run1();
absAdapter.run2();
}
}

3. 应用:SpringMvc 中的 HandlerAdapter, 就使用了适配器模式

参考:设计模式 | 适配器模式及典型应用

桥接模式(Bridge 模式)

1.基本介绍

  • 将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变
  • 是一种结构型设计模式
  • 基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。

2.思路分析

3.代码

钢笔类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class Pen {

protected Color color;

/**
* 桥接模式的关键,组合实现和抽象
*/
public Pen(Color color){
this.color= color;
}

/**
* 着色方法
*/
public abstract void tinting();
}

钢笔类扩展-小号钢笔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SmallPen extends Pen {

/**
* 着色方法:桥接模式的关键,组合实现和抽象
*/
public SmallPen(Color color) {
super(color);
}

@Override
public void tinting() {
System.out.println(super.color.colour() + "小号钢笔");
}

}

钢笔类扩展-中号钢笔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MiddlePen extends Pen {

/**
* 着色方法:桥接模式的关键,组合实现和抽象
*/
public MiddlePen(Color color) {
super(color);
}

@Override
public void tinting() {
System.out.println(super.color.colour() + "中号钢笔");
}
}

颜色接口

1
2
3
4
5
6
7
8
/**
* 实现类接口
* @author coderblue
*/
public interface Color {
// 返回对应的颜色
String colour();
}

实现颜色接口 - 绿色

1
2
3
4
5
6
7
public class Green implements Color {

@Override
public String colour() {
return "绿色的";
}
}

实现颜色接口 - 红色

1
2
3
4
5
6
7
public class Red implements Color {

@Override
public String colour() {
return "红色的";
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args) {
// 绿色的中号钢笔
Pen middlePen = new MiddlePen(new Green());
middlePen.tinting();

// 红色的小号钢笔
Pen smallPen = new SmallPen(new Red());
smallPen.tinting();
}
}

装饰者设计模式

1.基本介绍

动态地给一个对象增加一些额外的职责,增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
2.角色

Component(抽象构件)它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
ConcreteComponent(具体构件)它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
Decorator(抽象装饰类)它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
ConcreteDecorator(具体装饰类)它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
装饰模式的核心在于抽象装饰类的设计。

3.思路分析

4.代码

抽象构件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 煎饼抽象类
* @author coderblue
*/
public abstract class ABattercake {
/**
* 描述
*/
protected abstract String getDescrible();

/**
* 价格
*/
protected abstract int cost();
}

具体构件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 煎饼类,继承了煎饼抽象类,一个煎饼 8 块钱(具体构件)
*/
public class Battercake extends ABattercake {

@Override
protected String getDescrible() {
return "煎饼";
}

@Override
protected int cost() {
return 8;
}
}

抽象装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 抽象装饰类
*/
public abstract class AbstractDecorator extends ABattercake {
private ABattercake aBattercake;

public AbstractDecorator(ABattercake aBattercake) {
this.aBattercake = aBattercake;
}

protected abstract void doSomething();

@Override
protected String getDescrible() {
return this.aBattercake.getDescrible();
}

@Override
protected int cost() {
return this.aBattercake.cost();
}
}

具体装饰类

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 EggDecorator extends AbstractDecorator {
public EggDecorator(ABattercake aBattercake) {
super(aBattercake);
}

@Override
protected void doSomething() {

}

@Override
protected String getDescrible() {
return super.getDescrible() + " 加一个鸡蛋";
}

@Override
protected int cost() {
return super.cost() + 1;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 香肠装饰器
*/
public class SausageDecorator extends AbstractDecorator {
public SausageDecorator(ABattercake aBattercake) {
super(aBattercake);
}

@Override
protected void doSomething() {

}

@Override
protected String getDescrible() {
return super.getDescrible() + " 加一根香肠";
}

@Override
protected int cost() {
return super.cost() + 2;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
// 1.购买一个煎饼:煎饼, 销售价格: 8
System.out.println(aBattercake.getDescrible() + ", 销售价格: " + aBattercake.cost());

// 2.购买一个加鸡蛋的煎饼:煎饼 加一个鸡蛋, 销售价格: 9
aBattercake = new EggDecorator(aBattercake);
System.out.println(aBattercake.getDescrible() + ", 销售价格: " + aBattercake.cost());

// 3.购买一个加两个鸡蛋和一根香肠的煎饼: 煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠, 销售价格: 12
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDescrible() + ", 销售价格: " + aBattercake.cost());
}
}

参考:设计模式 | 装饰者模式及典型应用

工厂模式

简单工厂模式(Simple Factory Pattern)

1.基本介绍

定义一个工厂类,它可以 根据参数的不同返回不同类的实例,被创建的实例通常都 具有共同的父类

2.角色

  • Factory(工厂角色):工厂角色即 工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以 被外界直接调用创建所需的产品对象;在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product

  • Product(抽象产品角色):它是 工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。

  • ConcreteProduct(具体产品角色):它是简单工厂模式的 创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个 具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法

3.思路分析

4.代码

抽象产品类 Video,定义了抽象方法 produce()

1
2
3
public abstract class Video {
public abstract void produce();
}

具体产品类 JavaVideo 和 PythonVideo,都继承了抽象产品类 Video

1
2
3
4
5
6
7
8
9
10
11
12
13
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}

public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}

工厂类实现的两种方法:使用 if-else判断 和使用 反射 来创建对象

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
public class VideoFactory {
/**
* 使用if else 判断类型,type 为 Java 则返回 JavaVideo, type为Python则返回 PythonVideo
*/
public Video getVideo(String type) {
// equalsIgnoreCase()作用是用来比较字母的长度和字符是否相同,且不区分大小写。
if ("Java".equalsIgnoreCase(type)) {
return new JavaVideo();
} else if ("Python".equalsIgnoreCase(type)) {
return new PythonVideo();
}
return null;
}

/**
* 使用反射来创建对象
*/
public Video getVideo(Class c) {
Video video = null;
try {
// Java9后创建对象中间增加了 getDeclaredConstructor() 调用链
video = (Video) Class.forName(c.getName()).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return video;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new VideoFactory();
Video video1 = videoFactory.getVideo("python");
if (video1 == null) {
return;
}
// 录制Python课程视频
video1.produce();

Video video2 = videoFactory.getVideo(JavaVideo.class);
if (video2 == null) {
return;
}
// 录制Java课程视频
video2.produce();
}
}

5.适用场景

  • 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

  • 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

参考:设计模式 | 简单工厂模式及典型应用

工厂方法模式(Factory Method Pattern)

1.基本介绍

  • 定义一个 用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个 类的实例化延迟到其子类
  • 简称为 工厂模式(Factory Pattern),又可称作 虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)
  • 工厂方法模式是一种 类创建型模式

2.角色

  • Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类

  • ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。

  • Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。

  • ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类

3.代码思路

4.代码

抽象工厂

1
2
3
4
5
6
7
public abstract class VideoFactory {
/**
* 获得视频
* @return
*/
public abstract Video getVideo();
}

具体工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
public class JavaVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}
}

public class PythonVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new PythonVideo();
}
}

抽象产品

1
2
3
4
5
6
public abstract class Video {
/**
* 视频录制
*/
public abstract void produce();
}

具体产品

1
2
3
4
5
6
7
8
9
10
11
12
13
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}

public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
VideoFactory pythonVideoFactory = new PythonVideoFactory();
VideoFactory javaVideoFactory = new JavaVideoFactory();

// 录制Python课程视频
Video pythonVideo = pythonVideoFactory.getVideo();
pythonVideo.produce();

// 录制Java课程视频
Video javaVideo = javaVideoFactory.getVideo();
javaVideo.produce();
}
}

当需要增加一个产品 KotlinVideo 时,只需要增加 KotlinVideo 具体产品类和 KotlinVideoFactory 具体工厂类即可,不需要修改原有的产品类和工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class KotlinVideo extends Video{
@Override
public void produce() {
System.out.println("录制FE课程视频");
}
}

public class KotlinVideoFactory extends VideoFactory{
@Override
public Video getVideo() {
return new KotlinVideo();
}
}

还可以通过反射机制和配置文件配合,连客户端代码都不需要修改

1
2
3
4
5
6
7
8
// XXVideoFactory在的路径
String factoryName = "cn.coderblue.JavaVideoFactory";
// 通过反射机制获取工厂类
Class c = Class.forName(factoryName);
VideoFactory factory = (VideoFactory)c.getDeclaredConstructor().newInstance();
// 生产产品
Video video = factory.getVideo();
video.produce();

参考:设计模式 | 工厂方法模式及典型应用

抽象工厂模式

1.基本介绍

  • 定义了一个 interface 用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  • 可以将简单工厂模式和工厂方法模式进行整合
  • 从设计层面看,抽象工厂模式就是 对简单工厂模式的改进(或者称为进一步的抽象)。
  • 将工厂抽象成两层,AbsFactory(抽象工厂)具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

2.角色

  • AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。

  • ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。

  • AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法

  • ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

3.代码思路

4.代码

定义我们的抽象产品 Article 和 Video,他们是产品族的抽象类,有一个 Article 就有一个 Video

1
2
3
4
5
6
7
public abstract class Article {
public abstract void produce();
}

public abstract class Video {
public abstract void produce();
}

具体产品 JavaArticle、PythonArticle、PythonVideo、JavaVideo

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
public class JavaArticle extends Article {
@Override
public void produce() {
System.out.println("编写Java课程笔记记");
}
}

public class PythonArticle extends Article {
@Override
public void produce() {
System.out.println("编写Python课程笔记");
}
}

public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}

public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}

定义我们的抽象工厂 CourseFactory,与工厂方法模式不同,工厂方法模式中一个工厂只生产一个产品,而 抽象工厂模式中一个工厂生产一族产品,有 多个工厂方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class JavaCourseFactory implements CourseFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}

@Override
public Article getArticle() {
return new JavaArticle();
}
}

public class PythonCourseFactory implements CourseFactory {
@Override
public Video getVideo() {
return new PythonVideo();
}

@Override
public Article getArticle() {
return new PythonArticle();
}
}

测试:只需要指定具体工厂,就可以获取该工厂生产的一族产品

1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args) {
CourseFactory courseFactory = new JavaCourseFactory();
Video video = courseFactory.getVideo();
Article article = courseFactory.getArticle();
// 录制Java课程视频
video.produce();
// 编写Java课程笔记
article.produce();
}
}

组合模式

1.基本介绍

组合多个对象形成 树形结构 以表示具有 “ 整体—部分“ 关系的层次结构。组合模式对 单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性 ,组合模式又可以称为 “整体—部分”(Part-Whole) 模式,它是一种对象结构型模式。

2.角色

  • Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。

  • Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。

  • Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。

3.代码思路

4.代码

我们来实现一个简单的目录树,有文件夹和文件两种类型,首先需要一个抽象构件类,声明了文件夹类和文件类需要的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class Component {

public String getName() {
throw new UnsupportedOperationException("不支持获取名称操作");
}

public void add(Component component) {
throw new UnsupportedOperationException("不支持添加操作");
}

public void remove(Component component) {
throw new UnsupportedOperationException("不支持删除操作");
}

public void print() {
throw new UnsupportedOperationException("不支持打印操作");
}

public String getContent() {
throw new UnsupportedOperationException("不支持获取内容操作");
}
}

实现一个文件夹类 Folder,继承 Component,定义一个 List 类型的componentList属性,用来存储该文件夹下的文件和子文件夹,并实现 getName、add、remove、print等方法

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
public class Folder extends Component {
private String name;
private List<Component> componentList = new ArrayList<Component>();
public Integer level;

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

@Override
public String getName() {
return this.name;
}

@Override
public void add(Component component) {
this.componentList.add(component);
}

@Override
public void remove(Component component) {
this.componentList.remove(component);
}

@Override
public void print() {
System.out.println(this.getName());
if (this.level == null) {
this.level = 1;
}
String prefix = "";
for (int i = 0; i < this.level; i++) {
prefix += "\t- ";
}
for (Component component : this.componentList) {
if (component instanceof Folder){
((Folder)component).level = this.level + 1;
}
System.out.print(prefix);
component.print();
}
this.level = null;
}
}

文件类 File,继承Component父类,实现 getName、print、getContent等方法

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 File extends Component {
private String name;
private String content;

public File(String name, String content) {
this.name = name;
this.content = content;
}

@Override
public String getName() {
return this.name;
}

@Override
public void print() {
System.out.println(this.getName());
}

@Override
public String getContent() {
return this.content;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
public static void main(String[] args) {
Folder DSFolder = new Folder("设计模式资料");
File note1 = new File("组合模式笔记.md", "组合模式组合多个对象形成树形结构以表示具有 \"整体—部分\" 关系的层次结构");
File note2 = new File("工厂方法模式.md", "工厂方法模式定义一个用于创建对象的接口,让子类决定将哪一个类实例化。");
DSFolder.add(note1);
DSFolder.add(note2);

Folder codeFolder = new Folder("样例代码");
File readme = new File("README.md", "# 设计模式示例代码项目");
Folder srcFolder = new Folder("src");
File code1 = new File("组合模式示例.java", "这是组合模式的示例代码");

srcFolder.add(code1);
codeFolder.add(readme);
codeFolder.add(srcFolder);
DSFolder.add(codeFolder);

DSFolder.print();
}
}

输出:

1
2
3
4
5
6
7
设计模式资料
组合模式笔记.md
工厂方法模式.md
样例代码
README.md
src
组合模式示例.java

外观模式

1.基本介绍

外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节

2.角色

  • 外观类(Facade): 为调用端提供统一的调用接口, 外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
  • 调用者(Client): 外观接口的调用者
  • 子系统的集合:指模块或者子系统,处理 Facade 对象指派的任务,他是功能的实际

3.代码思路

4.代码

这里我给出了两个Subsystem子系统,分别为A、B。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Facade {

//被委托的对象
SubSystemA a;
SubSystemB b;

public Facade() {
a = new SubSystemA();
b = new SubSystemB();
}

//提供给外部访问的方法
public void methodA() {
this.a.dosomethingA();
}

public void methodB() {
this.b.dosomethingB();
}
}

Subsystem子系统角色,这里为了不过多赘述,只放上A的代码,其余子系统类似。

1
2
3
4
5
6
7
public class SubSystemA {

public void dosomethingA() {
System.out.println("子系统方法A");
}

}

测试

1
2
3
4
5
6
7
8
9
10
public class Client {

public static void main(String[] args) {
Facade facade = new Facade();

facade.methodA();
facade.methodB();
}

}

输出:

1
2
子系统方法A
子系统方法B

5.应用场景

  • 何时使用,客户端不需要知道系统内部的复杂联系,整个系统只提供一个“接待员”即可

  • 使用场景:为一个复杂的模块或子系统提供一个外界访问的接口 或 子系统相对独立,外界对子系统的访问只要黑箱操作即可

享元模式

1.基本介绍

说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。

比如我们每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存开销会很大,所以如果第一次创建了字符串对象“adam“,下次再创建相同的字符串”adam“时,只是把它的引用指向”adam“,这样就实现了”adam“字符串再内存中的共享。

2.角色

  • FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现
    - ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务- UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
  • FlyWeightFactory 享元工厂类,用于构建一个池容器(集合), 同时提供从池中获取对象方法

3.代码思路

4.代码

Flyweight抽象类,所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。

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 abstract class Flyweight {

//内部状态
public String intrinsic;
//外部状态
protected final String extrinsic;

//要求享元角色必须接受外部状态
public Flyweight(String extrinsic) {
this.extrinsic = extrinsic;
}

//定义业务操作
public abstract void operate(int extrinsic);

public String getIntrinsic() {
return intrinsic;
}

public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}

}

ConcreteFlyweight类,继承Flyweight超类或实现Flyweight接口,并为其内部状态增加存储空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ConcreteFlyweight extends Flyweight {

//接受外部状态
public ConcreteFlyweight(String extrinsic) {
super(extrinsic);
}

//根据外部状态进行逻辑处理
@Override
public void operate(int extrinsic) {
System.out.println("具体Flyweight:" + extrinsic);
}

}

UnsharedConcreteFlyweight类,指那些不需要共享的Flyweight子类。

1
2
3
4
5
6
7
8
9
10
11
12
public class UnsharedConcreteFlyweight extends Flyweight {

public UnsharedConcreteFlyweight(String extrinsic) {
super(extrinsic);
}

@Override
public void operate(int extrinsic) {
System.out.println("不共享的具体Flyweight:" + extrinsic);
}

}

FlyweightFactory类,一个享元工厂,用来创建并管理Flyweight对象,主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或创建一个实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class FlyweightFactory {

//定义一个池容器
private static HashMap<String, Flyweight> pool = new HashMap<>();

//享元工厂
public static Flyweight getFlyweight(String extrinsic) {
Flyweight flyweight = null;

if(pool.containsKey(extrinsic)) { //池中有该对象
flyweight = pool.get(extrinsic);
System.out.print("已有 " + extrinsic + " 直接从池中取---->");
} else {
//根据外部状态创建享元对象
flyweight = new ConcreteFlyweight(extrinsic);
//放入池中
pool.put(extrinsic, flyweight);
System.out.print("创建 " + extrinsic + " 并从池中取出---->");
}

return flyweight;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {

public static void main(String[] args) {
int extrinsic = 22;

Flyweight flyweightX = FlyweightFactory.getFlyweight("X");
flyweightX.operate(++ extrinsic);

Flyweight flyweightY = FlyweightFactory.getFlyweight("Y");
flyweightY.operate(++ extrinsic);

Flyweight flyweightZ = FlyweightFactory.getFlyweight("Z");
flyweightZ.operate(++ extrinsic);

Flyweight flyweightReX = FlyweightFactory.getFlyweight("X");
flyweightReX.operate(++ extrinsic);

Flyweight unsharedFlyweight = new UnsharedConcreteFlyweight("X");
unsharedFlyweight.operate(++ extrinsic);
}

}

输出

1
2
3
4
5
创建 X 并从池中取出---->具体Flyweight:23
创建 Y 并从池中取出---->具体Flyweight:24
创建 Z 并从池中取出---->具体Flyweight:25
已有 X 直接从池中取---->具体Flyweight:26
不共享的具体Flyweight:27

5.应用场景

  • 何时使用:系统中有大量对象时 或者 这些对象消耗大量内存时
  • 方法:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储
  • 应用实例:String常量池 或 数据库连接池

代理模式

1.基本介绍

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

2.三种代理模式

1.) 静态代理

创建服务类接口

1
2
3
public interface BuyHouse {
void buyHosue();
}

实现服务接口

1
2
3
4
5
6
7
public class BuyHouseImpl implements BuyHouse {

@Override
public void buyHosue() {
System.out.println("我要买房");
}
}

创建代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BuyHouseProxy implements BuyHouse {

private BuyHouse buyHouse;

public BuyHouseProxy(final BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}

@Override
public void buyHosue() {
System.out.println("买房前准备");
buyHouse.buyHosue();
System.out.println("买房后装修");

}
}

测试

1
2
3
4
5
6
7
8
public class ProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
buyHouse.buyHosue();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();
}
}

静态代理总结:

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
~
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。
同时接口一旦发生改变,代理类也得相应修改。

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
32
33
34
35
36
37
38
39
/**
* JDK动态代理类
*/
public class DynamicProxyHandler implements InvocationHandler {

/** 需要代理的目标对象 */
private Object targetObject;

/**
* 将目标对象传入进行代理
*/
public Object newProxy(Object targetObject) {
this.targetObject = targetObject;
//返回代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}

/**
* invoke方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 一般我们进行逻辑处理的函数比如这个地方是模拟检查权限
checkPopedom();
// 设置方法的返回值
Object ret = null;
// 调用invoke方法,ret存储该方法的返回值
ret = method.invoke(targetObject, args);
return ret;
}

/**
* 模拟检查权限的例子
*/
private void checkPopedom() {
System.out.println("======检查权限checkPopedom()======");
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import main.java.proxy.BuyHouse;
import main.java.proxy.impl.BuyHouseImpl;
import main.java.proxy.impl.DynamicProxyHandler;

import java.lang.reflect.Proxy;

public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}

Proxy.newProxyInstance()方法接收三个参数

static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h ) ~ ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的 Class[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

总结

代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

3.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
public class CglibProxy implements MethodInterceptor {
//维护目标对象
private Object target;

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

//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");

//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
import dan.proxy.BuyHouse;
import dan.proxy.impl.BuyHouseImpl;
import dan.proxy.impl.CglibProxy;

public class CglibProxyTest {
public static void main(String[] args){
BuyHouse buyHouse = new BuyHouseImpl();
CglibProxy cglibProxy = new CglibProxy();
BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse);
buyHouseCglibProxy.buyHosue();
}
}

总结

  • CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。
  • 对于单例的对象,因为 无需频繁创建对象,用CGLIB合适 ,反之使用JDK方式要更为合适一些。
  • 由于CGLib由于是采用 动态创建子类的方法,对于final修饰的方法无法进行代理
  • 继承只能使用CGLib,因为JDK代理生成的代理类,默认会继承一个类,由于java是单继承,所以当原始类继承一个类的时候,只能使用CGLib动态代理
  • 底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类

访问者模式

迭代器模式

观察者模式

1.基本介绍

观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个 主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

2.角色

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

3.代码

抽象观察者,里面定义了一个更新的方法

1
2
3
public interface Observer {
public void update(String message);
}

具体观察者(ConcrereObserver),微信用户是观察者,里面实现了更新的方法:

1
2
3
4
5
6
7
8
9
10
11
public class WeixinUser implements Observer {
// 微信用户名
private String name;
public WeixinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + "-" + message);
}
}

抽象被观察者(Subject),抽象主题,提供了attach、detach、notify三个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Subject {
/**
* 增加订阅者
* @param observer
*/
public void attach(Observer observer);
/**
* 删除订阅者
* @param observer
*/
public void detach(Observer observer);
/**
* 通知订阅者更新消息
*/
public void notify(String message);
}

具体被观察者(ConcreteSubject),微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法:

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 SubscriptionSubject implements Subject {
//储存订阅公众号的微信用户
private List<Observer> weixinUserlist = new ArrayList<Observer>();

@Override
public void attach(Observer observer) {
weixinUserlist.add(observer);
}

@Override
public void detach(Observer observer) {
weixinUserlist.remove(observer);
}

@Override
public void notify(String message) {
for (Observer observer : weixinUserlist) {
observer.update(message);
}
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Client {
public static void main(String[] args) {

// 创建微信用户
WeixinUser user1 = new WeixinUser("杨影枫");
WeixinUser user2 = new WeixinUser("月眉儿");
WeixinUser user3 = new WeixinUser("紫轩");

// 创建微信公众号
SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
// 订阅公众号
mSubscriptionSubject.attach(user1);
mSubscriptionSubject.attach(user2);
mSubscriptionSubject.attach(user3);

// 公众号更新发出消息给订阅的微信用户
mSubscriptionSubject.notify("廖雪峰的专栏更新了");
}
}

输出

1
2
3
杨影枫-刘望舒的专栏更新了
月眉儿-刘望舒的专栏更新了
紫轩-刘望舒的专栏更新了

总结

  • 优点:解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。

  • 缺点:在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

中介者模式

备忘录模式

解释器模式

状态模式

策略模式

职责链模式

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  1. © 2020 coderblue    湘ICP备20003709号

请我喝杯咖啡吧~

支付宝
微信