设计模式9-Proxy

本文最后更新于:3 years ago

代理模式

  在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于 结构型模式
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

如果想了解代理模式的具体的介绍,菜鸟教程介绍得比较详细↓
菜鸟教程-代理模式

结构图


优缺点

优点:

1、职责清晰。
2、高扩展性。
3、智能化。

缺点:

1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

使用场景

按职责来划分,通常有以下使用场景:
1、远程代理。
2、虚拟代理。
3、Copy-on-Write 代理。
4、保护(Protect or Access)代理。
5、Cache代理。
6、防火墙(Firewall)代理。
7、同步化(Synchronization)代理。
8、智能引用(Smart Reference)代理。

实现代码

我这里就介绍一下静态代理模式,动态代理真的难度很大。我可能也说的不是很清楚。代理是最难的的一种设计模式,
然后动态代理就是难上加难。这里我推荐一个也是我最近无意中看到的写的很好的一篇文章,下面第一个评论很好地解释了动态代理,难以理解动态代理的人可以康康

动态代理

提出要求,要求建立一个Main方法,实现Movable接口中的move()方法,接着在move()方法中输出Runing,睡眠5s以内,然后我想记录睡眠的时间。

代码不难,贴出代码:

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 Main implements Movable{
@Override
public void move() {
long start = System.currentTimeMillis();

System.out.println("Moving Moving Moving ...");
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}

long end = System.currentTimeMillis();
System.out.println(end - start);
}

public static void main(String[] args) {
new Main().move();
}
}

interface Movable{
void move();
}

代码不难,接着,甲方继续提出要求,记录时间时,不能够修改方法的源码。

首先想到继承,言出码随:

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
interface Movable{
void move();
}


public class Main implements Movable{
@Override
public void move() {

System.out.println("Moving Moving Moving ...");
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
new Main2().move();
}
}

class Main2 extends Main{

@Override
public void move() {
long start = System.currentTimeMillis();
super.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}

}

但是!有一件事情,我想提醒你,设计模式中是需要慎用继承的,因为耦合度太大了。
于是甲方爸爸就不乐意了,不允许你用继承。难受了,那怎么办啊?

这个时候,就需要用到代理的思想,创建一个代理类,实现Movable接口,这个代理中move()方法记录甲方爸爸想记录的东西,
然后这个类中,定义一个Main类型的变量,创建一个有参构造,每次当你创建这个代理类的时候,需要往里面传入一个Main类型的值。
也就是说只要将甲方爸爸的要求写入这个MainProxy类中就可以了。

先给出代码:

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
interface Movable{
void move();
}


public class Main implements Movable{
@Override
public void move() {

System.out.println("Moving Moving Moving ...");
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
new MainProxy(new Main()).move();
}
}

/**
*
*
* 相比继承,聚合的好处在哪里呢?
*
*/

class MainProxy implements Movable{

Main _main;

public MainProxy(Main _main) {
this._main = _main;
}

@Override
public void move() {
long start = System.currentTimeMillis();
_main.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}

}

move()方法只记录了我想记录的东西,剩下的都交给_main去实现(这就是聚合),相当于MainProxy就是Main的一个代理
但是代理完了,都是同一类型。
举个栗子,代理商代理苹果,最后不可能是厂家卖的是苹果,到了代理商这里卖的是三星。所以他们实现的接口一定是一样的

完成了甲方爸爸的需求,但是甲方爸爸想了想,觉得应该还有日志记录,访客日志…

无奈,但你也只能满足甲方爸爸一次次提出的无礼的要求:

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
interface Movable{
void move();
}


public class Main implements Movable{
@Override
public void move() {

System.out.println("Begin Begin Begin ...");
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
new MainTimeProxy(new Main()).move();
}
}

/**
*/

class MainTimeProxy implements Movable{

Main _main;

public MainTimeProxy(Main _main) {
this._main = _main;
}

@Override
public void move() {
long start = System.currentTimeMillis();
_main.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}

}

class MainLogProxy implements Movable{

Main _main;

public MainLogProxy(Main _main) {
this._main = _main;
}

@Override
public void move() {
System.out.println("start ...");
_main.move();
System.out.println("stop ...");
}
}

这里我们只是多加了一个MainLogProxy类,采用了聚合的方法,这样我们可以实现多种代理方式。
但是这个时候,甲方爸爸又提出了新的要求,我需要满足不同的代理顺序,
比如,我有的地方想实现先时间后日志,有的地方想实现先日志后时间。

这时就继承来讲,如果组合使用这些代理中的方法时,先时间后日志,或者先日志后时间
就需要设置不同的继承顺序,那么这个Main的体系就非常复杂了。

而相比继承而言,聚合就比较好解决这个复杂的问题
但如何通过聚合实现这个功能呢?

分析一下,我们要实现的细节,需要在MainTimeProxy中套着MainLogProxy,MainLogProxy中套着Main
而就目前代码而言,我们是不能够实现代理的组合
再看一下代码,问题在于,Main每个代理类中,都代理的是Main方法,MainTimeProxy不能聚合MainLogProxy的,这样是实现不了我们的需求的

把代理中的Main类改为Movable?😂,对的,就是这样:

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
interface Movable{
void move();
}


public class Main implements Movable{
@Override
public void move() {

System.out.println("Begin Begin Begin ...");
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
/*
start ...
Begin Begin Begin ...
stop ...
1083
*/

new MainTimeProxy(
new MainLogProxy(
new Main()
)
).move();

/*
start ...
Begin Begin Begin ...
1442
stop ...
*/

new MainLogProxy(
new MainTimeProxy(
new Main()
)
).move();
}
}


class MainTimeProxy implements Movable{

Movable m;

public MainTimeProxy(Movable m) {
this.m = m;
}

@Override
public void move() {
long start = System.currentTimeMillis();
m.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}

}

class MainLogProxy implements Movable{

Movable m;

public MainLogProxy(Movable m) {
this.m = m;
}

@Override
public void move() {
System.out.println("start ...");
m.move();
System.out.println("stop ...");
}
}

这个时候再拿出Decorator的结构图



是不是很像,其实学到后来,会发现很多模式都是互通的,这个最后这个系列写完时,总结一下


  就这样,你完成成功了甲方爸爸的所有需求,甲方爸爸很满意。学会了静态代理模式,于是你又愉快的度过了一天。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!