本文最后更新于: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) { new MainTimeProxy( new MainLogProxy( new Main() ) ).move(); 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的结构图
是不是很像,其实学到后来,会发现很多模式都是互通的,这个最后这个系列写完时,总结一下
就这样,你完成成功了甲方爸爸的所有需求,甲方爸爸很满意。学会了静态代理模式,于是你又愉快的度过了一天。