设计模式:代理模式与装饰模式

1、装饰者模式与代理模式 (静态代理) 

 在日常开发里面,我们经常需要给某个类的方法增加加某些特定的功能。

例如:有婴儿,婴儿会吃饭和走动,如以下类

 1 package com.scl.designpattern.proxy;
 2
 3 //婴儿类
 4 public class Child implements Human
 5 {
 6     public void eat()
 7     {
 8         System.out.println("eat something....");
 9     }
10
11     @Override
12     public void run()
13     {
14         System.out.println("Child run very slow");
15     }
16 }

婴儿类

  突然有一天,家长发现不行,孩子不能随便吃东西,而且吃饭前一定要洗手。但是孩子太小(被委托方),不会自己洗手。家长(Client 端)又没法照顾孩子。那简单,找个保姆照顾孩子! 让保姆类和婴儿类共同实现同一个接口,让保姆类全程管理小孩,同时在家长眼里,只要看到保姆在帮孩子洗手就可以了。于是,有以下内容。

 1 package com.scl.designpattern.proxy;
 2
 3 //保姆类
 4 public class BabySitter implements Human
 5 {
 6
 7     @Override
 8     public void eat()
 9     {
10
11     }
12
13     @Override
14     public void run()
15     {
16
17     }
18
19 }

保姆类

现在保姆已经有了,孩子也有了,怎么把孩子跟保姆关联起来。让保姆给相应的孩纸洗手。于是保姆类更改如下

 1 package com.scl.designpattern.proxy;
 2
 3 //保姆类
 4 public class BabySitter implements Human
 5 {
 6     private Human human;
 7
 8     public BabySitter(Human human)
 9     {
10         this.human = human;
11     }
12
13     @Override
14     public void eat()
15     {
16         // 添加washHand的方法
17         this.washHandForChild();
18         human.eat();
19     }
20
21     @Override
22     public void run()
23     {
24
25     }
26
27     public void washHandForChild()
28     {
29         System.out.println("help the child to wash his hands");
30     }
31 }

保姆与婴儿类关联

好,那么家长就是给孩纸找了个保姆代理,让他附加了一些婴儿做不了事。同时家长也没有强迫孩纸自己学会洗手(不更改Child类)

 1 package com.scl.designpattern.proxy;
 2
 3 //客户端
 4 public class Client
 5 {
 6     public static void main(String[] args)
 7     {
 8         Human human = new BabySitter(new Child());
 9         human.eat();
10     }
11 }

家长客户端代码

  以上就是一个简单的装饰模式,来看一下这一块完整的类图。

  装饰模式的一个很重要特点就是,在客户端可以看到抽象对象的实例,如Human human = new BabySitter(new Child()); 因为装饰模式通过聚合方式,把内容整合到装饰类里面了。

    装饰者模式能够使用装饰类对抽象对象进行装饰。假如来了个OldMan类手脚不利索。保姆类BabySitter同样能够胜任这个OldMan的饭前洗手操作。

  例子想了很久,也看了不少别人的博客。发现一个很令人迷惑的问题:为什么我用想的代理模式,确是别人口中的装饰模式?装饰者模式和代理模式有什么区别?。大致的代理模式类图如下:

  

  由该类图可知,以上BabySitter代码应该如下:

 1 package com.scl.designpattern.proxy;
 2
 3 //保姆类
 4 public class BabySitter implements Human
 5 {
 6     private Child child;
 7
 8     public BabySitter()
 9     {
10         child = new Child();
11     }
12
13     @Override
14     public void eat()
15     {
16         // 添加washHand的方法
17         this.washHandForChild();
18         human.eat();
19     }
20
21     @Override
22     public void run()
23     {
24
25     }
26
27     public void washHandForChild()
28     {
29         System.out.println("help the child to wash his hands");
30     }
31 }

代理模式下的代理类

 1 package com.scl.designpattern.proxy;
 2
 3 //客户端
 4 public class Client
 5 {
 6     public static void main(String[] args)
 7     {
 8         Human human = new BabySitter();
 9         human.eat();
10     }
11 }

代理模式下的客户端

装饰者模式和代理模式的最后运行的结果都是一样的,显示如下

由代理模式代码可知,客户端不关心代理类了哪个类。但代码控制了客户端对委托类的访问。客户端代码表现为 Human human = new BabySitter( );

所以资料上都说了,装饰模式主要是强调对类中代码的拓展,而代理模式则偏向于委托类的访问限制。两者都一样拥有抽象角色(接口)、真实角色(委托类)、代理类 。

由于代理类实现了抽象角色的接口,导致代理类无法通用。如有天,一个有钱人养了只小猩猩,他要一个保姆在猩猩吃东西前,帮猩猩洗手....保姆根本不懂猩猩的特性(跟猩猩类不是同一类型的,保姆属于Human类,而猩猩可能属于Animal类型。),但洗手这个方法是不变的,用水洗。能不能找一个代理说既可以照看人吃饭前洗手也可以照看猩猩吃饭前洗手?

用机器人吧... (编不下去了)。要实现这种功能,必须让代理类与特定的接口分离。在代理的时候能够了解每个委托的特性,这就有可能了。这时候动态代理就出现了。

2、动态代理 

如静态代理的内容所描述的,静态代理受限于接口的实现。动态代理就是通过使用反射,动态地获取抽象接口的类型,从而获取相关特性进行代理。因动态代理能够为所有的委托方进行代理,因此给代理类起个通用点的名字HealthHandle。先看保姆类可以变成什么样。

 1 package com.scl.designpattern.proxy.dynamic;
 2
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6
 7 public class HealthHandle implements InvocationHandler
 8 {
 9
10     private Object proxyTarget;
11
12     public Object getProxyInstance(Object target)
13     {
14         this.proxyTarget = target;
15         return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
16     }
17
18     @Override
19     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
20     {
21         Object methodObject = null;
22         System.out.println("proxy washing hand start");
23         methodObject = method.invoke(proxyTarget, args);
24         System.out.println("proxy washing hand end");
25         return methodObject;
26     }
27 }

由保姆类转化而来的动态代理类

 1 package com.scl.designpattern.proxy.dynamic;
 2
 3 import java.lang.reflect.Proxy;
 4
 5 //客户端
 6 public class Client
 7 {
 8     public static void main(String[] args)
 9     {
10         HealthHandle h = new HealthHandle();
11         Human human = (Human) h.getProxyInstance(new Child());
12         human.eat();
13         human.run();
14     }
15 }

动态代理模式下的客户端代码

  首先,使用动态代理

1. 必须实现InvocationHandler接口,订立一个契约标识该类为一个动态代理执行类。

2. InvocationHandler接口内有一实现方法签名如下: public Object invoke(Object proxy, Method method, Object[] args) 。使用时需要重写这个方法

3. 获取代理类,需要使用 Proxy.newProxyInstance(Clas loader, Class<?>[] interfaces, InvocationHandler h) 这个方法去获取代理对象。

    先看获取代理对象签名解析如下:

      //Clas loader : 类的加载器
  //Class<?>[] interfaces : 委托类实现的接口,保证代理类返回的是同一个实现接口下的类型,保持代理类与抽象角色行为的一致
  //invocationHandler, 该类最重要的一环。一个设计模式:策略模式.即告诉代理类,代理类遇到某个委托类的方法时该调用哪个类下的invoke方法。
  Proxy.newProxyInstance(Class loader, Class<?>[] interfaces, InvocationHandler h) 

  再看实现InvocationHandler方法下的签名 public Object invoke(Object proxy, Method method, Object[] args)

      //第一个参数为代理类
  //第二个参数为委托类的方法对象//第三个参数为委托类的方法参数
  //返回类型为委托类某个方法的返回对象
   public Object invoke(Object proxy, Method method, Object[] args)     总的来说这个方法就是动态获取委托类里面的方法,在调用委托类的方法时在这个方法内进行拓展。 通过上面的Proxy.newProxyInstance方法,告诉底层代码,该去哪个类里面执行invoke方法。

  由上面的描述,在客户端代码的调用后,结果如下:

由此可见,在代理类对系统下每个方法的调用时,都会去调用invocationHandler里面的invoke方法. 包括里面的eat和run方法,可见受作用的范围是多广:每个方法都受代理类作用了。由例子可以看出,如果要实现我们想要的方法上面添加特定的代理,可以通过invoke方法里面的方法反射获取method对象方法名称即可实现(但这样会产生硬编码)。修改可如下:

 1     @Override
 2     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 3     {
 4         Object methodObject = null;
 5         if (method.getName().equals("eat"))
 6         {
 7             System.out.println("proxy washing hand start");
 8             methodObject = method.invoke(proxyTarget, args);
 9             System.out.println("proxy washing hand end");
10         }
11         else
12         {
13             methodObject = method.invoke(proxyTarget, args);
14         }
15         return methodObject;
16     }

由此,我们可以猜想出来动态代理在开发中的用法:

  1. 监控程序下执行某个方法的耗时、性能及日志的编写

  2. 监控程序方法的权限,不再单一地实行Controler→ServiceImpl→DaoImpl这种纵向编程。增加了在同一层面间横向编程的可能(切面编程:AOP)

  另外有一点必须指出的内容:在使用Invoke方法的时候,需要对method的可访问性进行加工吗?不需要,动态代理模式最后返回的是具有抽象角色(顶层接口)的对象。在委托类内被private或者protected关键修饰的方法将不会予以调用,即使允许调用。也无法在客户端使用代理类转换成子类接口,对方法进行调用。

时间: 2024-10-27 13:12:48

设计模式:代理模式与装饰模式的相关文章

设计模式——代理模式与装饰模式的异同

两种模式的特点 装饰模式: 在不改变接口的前提下,动态扩展对象的访问. 动态继承,让类具有在运行期改变行为的能力. 装饰模式,突出的是运行期增加行为,这和继承是不同的,继承是在编译期增加行为. 强调:增强 代理模式: 在不改变接口的前提下,控制对象的访问. 1.从封装的角度讲,是为了解决类与类之间相互调用而由此导致的耦合关系,可以说是接口的另外一个层引用. 比如:在a类->b代理->c类这个关系中,c类的一切行为都隐藏在b中.即调用者不知道要访问的内容与代理了什么对象. 2.从复用的角度讲,可

【设计模式】代理模式和装饰模式之间的区别

学习了代理模式和装饰模式之后,对这两个的模式的区别产生了混淆,因为两个模式都是对一个类的功能进行扩充,而且服务类和客户类都同时实现了统一个接口. 那么,两者之间有什么区别呢? 通过http://www.cnblogs.com/jaredlam/archive/2011/11/08/2241089.html的文章,总结得知,两种模式最大的不同就在于对于访问的控制,以及类之间关系的确定阶段的不同. 代理模式是在服务类中新建一个被代理类的实例,使用者得不到被代理类的具体信息,也无法访问,类似于中介对客

代理模式 vs 装饰模式

代理模式和装饰模式有很大的相似性,二者的类图(几乎)是一样的.下面分别讲解代理模式和装饰模式. 1.代理模式 一般著名的跑步运动员都会有自己的代理人,如果想联系该运动员的比赛事宜,可以直接联系他的代理人就可以了.类图如下所示: IRunner接口如下: public interface IRunner { public void run(); } Runner类如下所示: public class Runner implements IRunner { @Override public void

小菜学设计模式——代理模式

背景 很多时候,因为你的地位特殊而赋予你了不同的权限,那么你拥有别人做不到的事情.这个时候,如果你的朋友很想完成这样一件事情,但是她知道自己可能无法完成,但是你可以帮他处理,同时必要的话还可以中间拿点外快,不过最后要知道你是代理他完成这样一个事情,这就是代理模式出现的原因. 1.使用意图 通过代理角色代理真实角色去完成某一件特定事情,代理为什么会出现,因为代理身份的特殊性. 2.生活实例 找房子的时候会找代理,代理能够帮我找到房子,但是他额外收取了我一个月的房租,不说了,房租好贵! 3.Java

设计模式 - 代理模式(proxy pattern) 未使用代理模式 详解

代理模式(proxy pattern) 未使用代理模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 部分代码参考: http://blog.csdn.net/caroline_wendy/article/details/37698747 如果需要监控(monitor)类的某些状态, 则需要编写一个监控类, 并同过监控类进行监控. 但仅仅局限于本地, 如果需要远程监控, 则需要使用代理模式(proxy pattern). 具体方法: 1. 类中需要提供

设计模式——代理模式

概念 代理模式(Proxy),为其他对象提供一种代理以控制对象的访问. 模式结构 一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理类再 访问真正要访问的对象. 代理模式UML图 代码实战 <span style="font-family:KaiTi_GB2312;font-size:18px;"> //代理模式 class  Proxy : IGiveGift                   //让"代理&qu

23种设计模式----------代理模式(二)

(上一篇)23种设计模式----------代理模式(一) 之前说了基本的代理模式和普通代理模式.接下来开始看下强制代理模式和虚拟代理模式 三,强制代理模式: 一般的代理模式都是通过代理类找到被代理的对象,从而调用被代理类中的方法(即完成被代理类中的任务). 而,强制代理模式则是先找到被代理类自己去完成事情,然后被代理类又将该做的事情转交到代理类中,让代理类来完成. 假如:你有事求助于某位名人. 你告诉名人说有事想请他帮忙,然后他说最近一段时间比较忙,要不你找我的经纪人来办吧. (本来找名人办事

23种设计模式----------代理模式(三) 之 动态代理模式

(上一篇)种设计模式----------代理模式(二) 当然代理模式中,用的最广泛的,用的最多的是  动态代理模式. 动态代理:就是实现阶段不用关系代理是哪个,而在运行阶段指定具体哪个代理. 抽象接口的类图如下: --图来自设计模式之禅 所以动态代理模式要有一个InvocationHandler接口 和 GamePlayerIH实现类.其中 InvocationHandler是JD提供的动态代理接口,对被代理类的方法进行代理. 代码实现如下 抽象主题类或者接口: 1 package com.ye

23种设计模式----------代理模式(一)

代理模式也叫委托模式. 代理模式定义:对其他对象提供一种代理从而控制对这个对象的访问.就是,代理类 代理 被代理类,来执行被代理类里的方法. 一般情况下,代理模式化有三个角色. 1,抽象的主题类(或者接口) IGamePlayer 2,代理类. 3,被代理类. 下面以游戏玩家代理为例. 一,先来看下最基本的代理模式. 代码如下: 主题接口: 1 package com.yemaozi.proxy.base; 2 3 //游戏玩家主题接口 4 public interface IGamePlaye

5.大话设计模式-代理模式

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DesignModel { public class Proxy:IAction { ZQZ zzz = null; public Proxy(ZQZ mm) { zzz = mm; } public void SendFlower() { zzz