1.1.1 定义

  委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,如下面的示例所示:

  //Code in C#

  public delegate int PerformCalculation(int x, int y);

  与委托的签名(由返回类型和参数组成)匹配的任何方法都可以分配给该委托。

  简单理解Delegate委托(或代理)是一种数据类型:它的变量可以引用到某一个符合要求的方法上,通过委托可以间接地调用该方法。

  其实.NET的委托类似于C语言的函数指针,区别在于.NET委托是类型安全的,这说明,C中的函数指针只不过是一个指向存储单元的指针,我们无法说出这个指针实际指向什么。

1.1.2 委托使用

  • 使用委托的四部曲:
  • 定义一种委托类型
  • 委托执行时要调用方法
  • 定义一个委托实例
  • 委托实例的调用

  我们先定义一种委托类型如下:

//自定义一种委托类型

public delegate void StringProcessor(string input);

然后我们再定义5中候选的委托方法如下:

void PrintString(string x)

void PrintInteger(int x)

void PrintTwoStrings(string x, string y)

int GetStringLength(string x)

void PrintObject(object x)

  大家猜猜看哪个和上面提供的委托类型签名匹配(签名匹配:参数类型,参数个数和返回类型匹配)。激动时刻到了马上公布答案,和委托类型匹配的方法是PrintString和PrintObject,如果有不明白的请细细考虑一下委托匹配的条件—签名匹配。

图1委托成功输出

  现在对委托有了一定的认识,接下来我们将介绍委托最经常使用的地方—事件。

  我们将从发送器和接受器的角度讨论事件,例如在UI编程中,鼠标单击或键盘按键,发送器就是.NET的CLR,注意事件发送器并不知道接收器是谁,这符合面向对象的原则,而且某个事件接收器有个方法处理该事件,这个时候就要委托,如前面所讲事件发送器对事件接收器一无所知,通过委托作为一个中介,接收器把事件处理方法注册到事件中,这样就实现了由发送器->委托->接收器的过程了。

  我们可以这样认为:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

1.1.3 自定义委托

  前面话有点难以理解,接下来我们通过具体的例子分析一下何谓委托,该如何实现委托。现在不是很喜欢搞多国语言化的吗?看看如何让我们的程序会说多种语言吧!

/// <summary>
/// the English speaker.
/// </summary>
/// <param name="name">The name.</param>
public void EnglishSpeaker(string name)
{
Console.WriteLine(
string.Format("Hello my name is {0} and I am English speaker.\n", name));
}

/// <summary>
/// the Chineses speaker.
/// </summary>
public void ChineseSpeaker(string name)
{
Console.WriteLine(
string.Format("您好我的名字叫{0},我是讲普通话的。\n", name));
}

  好啦现在我们有两个方法分别是说普通话和英语,现在我们的程序会说普通话和英语啦。现在我们考虑究竟什么时候讲普通话什么时候讲英语,那不简单我们加个判断就OK啦,是的我们可以通过switch或者if else就可以实现啦。

/// <summary>
/// 根据上下文调用不同的方法
/// </summary>
/// <param name="name">string</param>
/// <param name="lang">enum</param>
private static void Say(string name, Language lang)
{
switch (lang)
{
case Language.Chinese:
Program.ChineseSpeaker(name);
break;
case Language.English:
Program.EnglishSpeaker(name);
break;
default :
break;
}
}

  但假设我们现在又要增加新的语言西班牙语,同样我们可以增加西班牙语,但我们必须修改switch语句增加判断,这不符合OOP中的OCP(对扩展开放,对修改关闭原则),这时候委托该登场。

/// <summary>
/// Define speak delegate.
/// </summary>
/// <param name="name"></param>
private delegate void SpeakDelegate(string name);

  

  首先我们定义了一种委托类型SpeakDelegate,然后我们通过修改Say方法看看该如何使用委托变量。

/// <summary>
/// The base say function.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="speaker">The speaker.</param>
private static void Say(string name, SpeakDelegate speaker)
{
///Inoke the speaker function.
speaker(name);
}

  现在我们的参数已经不是枚举类型了,而是一个委托类型变量,而且实现的具体代码也没有了switch语句了,比之前简单了许多。现在大家知道如何去调用Say方法吧!没错我们只需传递一个name和一个具体实现函数名就OK了。

///传递函数名进行委托方法绑定
Program.Say("钧航", ChineseSpeaker);
Program.Say("JK.Rush", EnglishSpeaker);

  自定义委托相信大家都会了,接下来我将介绍一下.NET中委托实现,由于许多使用委托的例子都是事件,所以下面的例子也采用事件。但请大家要注意“可以使用委托,但却没有定义事件”的情况(例如:回调函数)。

1.1.4 .NET中的事件委托

  举一个简单的例子,.NET中经常使用的控件Button,当我们把Button 控件 drap and drop到界面,然后双击界面的Button我们发现程序中自动生成了一个响应Button的事件方法,然后我们给事件方法添加Code之后,当我们点击该Button就响应该方法了,但我们没有看到代码中有任何的委托和事件之类的定义,其实这些.NET都已经做好了。我们可以查看如下文件。

              图2事件委托实现

  如上图所示我们打开Designer文件,事件委托的实现都在这里实现了。

其中,EventHandler就是一个代理类型,可以认为它是一个“类”,是所有返回类型为void,具备两个参数分别是object sender和EventArgs e,第一个参数表示引发事件的控件,或者说它表示点击的那个按钮。通过以下的代码我们细细解析一下。

private void button1_Click(object sender, EventArgs e)
{
//获取被点击Button的实例
Button objBotton = sender as Button;
if (objBotton != null)
{
objBotton.Text = "Hello you click me.";
objBotton.AutoSize = true;
}
else
{
//Exception Handle.
}
}

图3点击产生效果

  

  OK现在明白了sender就是传递一个被点击对象的实例,第二个参数名叫e的EventArgs参数,用于      表示附加的事件关联的事件信息。当点击按钮时,没有附加任何关联的事件信息,如上的点击事件,第二参数并不表示任何有用的信息。但什么时候会用到呢?

  我们先介绍一下EventArgs这个的类型。其实这个类并没有太多的功能,它主要是作为一个基类让其他类去实现具体的功能和定义,当我们搜索EventArgs发现很多类是继承于它的。

public class EventArgs
{
// Fields
public static readonly EventArgs Empty;

// Methods
static EventArgs();
public EventArgs();
}

  举其中的ImageClickEventArgs为例,它继承于EventArgs,而且还添加了自己的字段用来基类X和Y的坐标值(这是一个ImageButton被点击时候响应的),然后获取该按钮的X和Y坐标。

public sealed class ImageClickEventArgs : EventArgs
{
// Fields
public int X;
public int Y;

// Methods
public ImageClickEventArgs(int x, int y)
{
this.X = x;
this.Y = y;
}
}

//ImageButton点击响应时间
protected void ibtnTest_Click(object sender, ImageClickEventArgs e)
{
this.lblCX.Text = e.X.ToString();
this.lblCY.Text = e.Y.ToString();
}

图4获取ImageClickEventArgs关联点击坐标

  前面提到其他事件关联信息类型都是通过继承EventArgs实现的,所以说我们自己也可以自定义一个事件关联信息类型,如下我们只需继承EventArgs就OK了。

/// <summary>
/// 自定义事件关联类
/// </summary>
public class ColorChangedEventArgs : EventArgs
{
private Color color;

/// <summary>
/// Initializes a new instance of the <see cref="ColorChangedEventArgs"/> class.
/// </summary>
/// <param name="c">The c.</param>
public ColorChangedEventArgs(Color c)
{
color = c;
}

/// <summary>
/// Gets the color of the get.
/// </summary>
/// <value>
/// The color of the get.
/// </value>
public Color GetColor
{
get { return color; }
}

}

1.1.5自定义事件委托

多播委托

  前面使用的每个委托都只包含一个方法调用。调用一个委托就调用一个方法调用。如果要通过一个委托调用多个方法,那就需要使用委托的多播特性。如果调用多播委托,就可以按委托添加次序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果,接下来看看多播实现。

namespace Multi_Delegate
{
delegate void StringProcessor();
public class Person
{
private string _Name;
public Person(string name)
{
this._Name = name;
}

public void Say()
{
Console.WriteLine("Hello my name is {0}, what's your name.\n", this._Name);
}

public void Reply()
{
Console.WriteLine("Hello my name is {0} and nice to meet you.\n", this._Name);
}
}

class Program
{
static void Main(string[] args)
{
Person Jack = new Person("Jack");
Person Oliver = new Person("Oliver");
StringProcessor sp = null;
//绑定多播方法调用
sp += Jack.Say;
sp += Oliver.Reply;
sp();
Console.ReadKey();
}
}
}

  也许有人觉得很简单,实现的确简单明了,就是通过“+”把方法调用绑定到委托变量中,如果我们用“-”就可以移除绑定到委托变量方法了。

事件

  前面一直没有解释什么是事件,现在让我用一句话解释事件和委托的关系吧!

  事件和委托关系就像是属性和字段的关系,为了刚好的实现OOP的编程原则,事件对委托进行了封装。

  现在我们修改前面的代码,使用事件对委托进行封装。

/// 使用事件对委托进行封装
/// </summary>
public class Say
{
/// <summary>
/// 封装委托字段
/// </summary>
public static event SpeakDelegate speakDelegate;

/// <summary>
/// 调用委托具体实现方法
/// </summary>
/// <param name="name"></param>
public static void SayManager(string name)
{
speakDelegate(name);
}
}

/// <summary>
/// 客户端调用委托
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Say.speakDelegate += Program.ChineseSpeaker;
Say.speakDelegate += Program.EnglishSpeaker;
Say.SayManager("Jackson");
Console.ReadKey();
}

图5自定义委托

  现在让我们看看编译后Say类就可以充分证明我们的结论:事件是对委托封装。

图6自定义事件编译后的代码

  大家看到在编译后的代码中出现了一个私有的委托变量,然后接下是一个公用事件委托变量,这进一步说明了事件是对委托的封装。

图7自定义事件编译后MSIL代码

1.1.6事件委托实现观察者模式

  前面我们介绍按钮事件响应是从发送者和接收者的角度出发的,现在我们以设计模式中的观察者模式为例。

图8GoF观察者架构

namespace GoFObserver
{
/// <summary>
/// 充当Subject角色
/// </summary>
public class GofTelecom
{
public delegate void GofNews();
public static event GofNews NewEvent;

/// <summary>
/// 发布通知方法
/// </summary>
/// <returns></returns>
public static bool Notify()
{
if (NewEvent != null)
{
NewEvent();
return false;
}
return true;
}
}

public interface IObserver
{
void Update();
}

/// <summary>
/// 观察者
/// </summary>
public class Programmer : IObserver
{

#region IObserver 成员

public void Update()
{
Console.WriteLine("I am a greenhand programmer.\n");
}

#endregion

}

/// <summary>
/// 观察者
/// </summary>
public class Architect : IObserver
{
#region IObserver 成员

public void Update()
{
Console.WriteLine("OH...I am a top banana.\n");
}

#endregion
}

public class Program
{
static void Main(string[] args)
{
IList<IObserver> objObserver = new List<IObserver>();
objObserver.Add(new Programmer());
objObserver.Add(new Architect());
foreach (IObserver ob in objObserver)

委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委 托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式使得我们可以用聚合来替代继 承,它还使我们可以模拟mixin。  “委托”在C#中是一个语言级特性,而在Java语言中没有直接的对应,但是我们可以通过动态代理来实现委托!代码如下: 以通过动态代理来实现委托 代理模式是一个在面向对象编程中重要而广泛被使用的设计模式。JDK1.3中已经介绍了Proxy,你在 Java使用过Proxy吗?动态代理类实现了在运行时所指定的一组接口。在运行时,通过使用InvocationHandler来提供代理的行为。因 此,Proxy是在JAVA反射包中一个重要的类,并且广泛地用于许多JAVA应用程序中。 Proxy的一个限制是它仅仅接受接口。在某些情况下,你不仅需要使用代理模式来代理接口,而且也需要用它来代理抽象类,甚至具体的类。 本文介绍了动态委托(Dynamic Delegation),它能够创建运行时接口和类的代理。 Proxy概述 在JDK1.3中, java.lang.reflect包增加了Proxy类。它能够创建一个具体类,这个类实现了所有在运行时指定的所有接口。动态生成的类将所有定义在接口中的方法调用重定向到InvocationHandler。 给定两个接口,Idel1和Idel2, Proxy将创建一个IdelProxy类作为这两个接口的代理(为了方便起见,使用IdelProxy作为生成的代理类名字)。图1展现了这种结构:   图1. IdelProxy的类图 下面是相关的简要代码:

    Class clazz = Proxy.getProxyClass(Idel1.class.getClassLoader(), new Class[] { Idel1.class, Idel2.class });

委托与代理的比较

Proxy仅仅对接口进行代理。如果想要用Proxy进行接口和类的代理,我们需要做什么呢? java.net的Dunamis project项目介绍了另一种相应的模式—Delegation模式。Delegation使用了与Proxy不同的方法实现。

给定一个名字为TestBean的类,委托类TestBeanDelegation的类图如图2所示:

 
图2. TestBeanDelegation的类图

TestBeanDelegation 实现了Delegation接口并且继承了TestBean类。它也包含了对TestBean和DelegationInvocationHandler 的引用。所有TestBeanDelegation上的方法调用都是对这两者的委托。

以getName()为例,图3描述了方法调用的顺序图。


图3. TestBeanDelegation.getName()调用的顺序图

相关伪码如下:

//The delegation class is a sub-class of the class to be delegated
public class TestBeanDelegation extends TestBeanimplements Delegation {//The object to be delegatedTestBean bean;//The invocation handlerDelegationInvocationHandler handler;...static Method m0 = null;...static {...try {m0 = TestBean.class.getMethod("getName",new Class[] {});} catch (Exception exception) {}...}public TestBeanDelegation(Object bean) {this.bean = (TestBean)bean;}public String getName() {boolean goon = true;String ret = null;Throwable t = null;try {goon = handler.invokeBefore(bean,m0, new Object[] {});if (goon)try {ret = bean.getName();} catch (Throwable throwable) {t = throwable;}if (t != null)ret = handler.invokeAfterException(bean,m0, new Object[] {}, t);elseret = handler.invokeAfter(bean,m0, new Object[] { name }, null);return ret;} catch (RuntimeException e) {throw e;} catch (Error e) {throw e;} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}
}

动态委托的介绍

动态委托概念来自于Jakarta 字节码工程库 (Byte-Code Engineering Library, BCEL)。它能够分析存在的类,并且对于接口,抽象类,甚至运行时的具体类来说,它能够生成以字节编码委托类。

被委托的接口/类应该满足如下条件:
动态委托最多只能委托一个类,但是能够代理多个接口。
这个限制来自于Java的单继承模式。一个Java类最多只有一个父类。既然生成的委托类把被委托类作为它的父类,那么指定多个被委托类是不合理的。如果没有指定被委托类,那么缺省的父类就是Object。

被委托类应该有一个有限定符public或者protected的缺省构造函数。
委托类会在它自己的构造函数中调用父类的缺省构造函数。

被委托类不能是final,而应该对于它的调用者可见。
Proxy生成的代理类是final的。而动态委托却不能接受这种情况。

动态委托不能委托实现了接口Delegation的任何类。
既然类已经是一个委托类,就没有必要重新委托它。

生成的委托类有下面的特点:
委托类是在运行时产生的,没有类文件
委托类实现了所有继承的接口,扩展了被委托的类。
委托类也实现了Delegation接口。
委托类有一个接受Object实例作为参数的构造函数。

DelegationGenerator 是动态委托的主要类。客户程序能够使用它来生成指定类,接口,对象的委托类和对象。DelegationInvocationHandler是一个定义了 所有委托行为的接口,客户程序的开发人员应该实现这些所有的接口。委托对象能够使用定义在Delegation中的 _getInvocationHandler和_setInvocationHandler方法来访问委托对象中的 DelegationInvocationHandler实例。

练习1。创建一个具体类的委托类

代码
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. /*
  5. * @author Liusheng
  6. * 实现“委托”模式,用户需要实现InvocationHandler接口;
  7. * 参考:http://www.uml.org.cn/j2ee/200411036.htm
  8. */
  9. public abstract class Delegator implements InvocationHandler {
  10. //RelegateTo针对每个对象都要生成一个实例,因而非Static的log,代价比较高。
  11. //protected Log _log = LogFactory.getLog(this.getClass());
  12. //private static Log _log = LogFactory.getLog(RelegateTo.class);
  13. //--------------------------------------------
  14. protected Object obj_orgin = null;  //原始对象
  15. protected Object obj_proxy = null;  //代理对象
  16. //--------------------------------------------
  17. public Delegator()  {
  18. //空
  19. }
  20. public Delegator(Object orgin){
  21. this.createProxy(orgin);
  22. }
  23. //--------------------------------------------
  24. protected Object createProxy(Object orgin) {
  25. obj_orgin = orgin;
  26. obj_proxy = Proxy.newProxyInstance(
  27. orgin.getClass().getClassLoader(),  //加载器
  28. orgin.getClass().getInterfaces(),   //接口集
  29. this);  //委托
  30. //_log.debug("# 委托代理:"+obj_proxy);
  31. return obj_proxy;
  32. }
  33. protected Object invokeSuper(Method method, Object[] args)
  34. throws Throwable {
  35. return method.invoke(obj_orgin, args);
  36. }
  37. //--------------实现InvocationHandler接口,要求覆盖------------
  38. public Object invoke(Object obj, Method method, Object[] args)
  39. throws Throwable {
  40. // 缺省实现:委托给obj_orgin完成对应的操作
  41. if (method.getName().equals("toString")) {  //对其做额外处理
  42. return this.invokeSuper(method, args)+"$Proxy";
  43. }else {     //注意,调用原始对象的方法,而不是代理的(obj==obj_proxy)
  44. return this.invokeSuper(method, args);
  45. }
  46. }
  47. }

下面的代码,则是作为一个委托的例子,实现Map的功能。

代码
  1. import java.io.IOException;
  2. import java.lang.reflect.Method;
  3. import java.util.Hashtable;
  4. import java.util.Map;
  5. import org.apache.commons.logging.Log;
  6. import org.apache.commons.logging.LogFactory;
  7. import com.bs2.core.UtilLog;
  8. /**
  9. * @author Liusheng
  10. * 本代码主要用于演示RelegateTo的使用方法
  11. */
  12. public class Delegator4Map extends Delegator {
  13. private static Log _log = LogFactory.getLog(Delegator4Map.class);
  14. private Map orginClass = null;  //原始对象
  15. private Map proxyClass = null;  //代理对象
  16. public Map getOrgin() { return orginClass;  }
  17. public Map getProxy() { return proxyClass;  }
  18. public Delegator4Map(Map orgin) {
  19. super(orgin);
  20. orginClass = orgin;
  21. proxyClass = (Map)super.obj_proxy;
  22. }
  23. public Object invoke(Object obj, Method method, Object[] args)
  24. throws Throwable {
  25. if (method.getName().equals("size")) {  //修改close处理逻辑
  26. _log.debug("原始 size()="+super.invoke(obj, method, args));
  27. Object res2 = new Integer(-1);
  28. _log.debug("修改 size()="+res2);
  29. return res2;
  30. }else {
  31. return super.invoke(obj, method, args);
  32. }
  33. }
  34. public static void main(String[] args) throws IOException {
  35. UtilLog.configureClassPath("resources/log4j.properties", false);
  36. Delegator4Map rtm = new Delegator4Map(new Hashtable());
  37. Map m = rtm.getProxy();
  38. m.size();
  39. _log.debug("代理:"+m.toString());
  40. }
  41. }

注意:UtilLog仅仅是用于配置log4j属性文件位置,如果log4j.properties就在缺省的运行路径下,则无需单独配置。或者用System.out输出来替代_log输出。

Trackback: http://tb.blog.csdn.NET/TrackBack.aspx?PostId=1711118

假定存在一个名字为ConcreteClass的具体类:

//ConcreteClass.java
package org.jingle.util.dydelegation.sample;public class ConcreteClass {public void hello() {System.out.println("Hello from ConcreteClass");}protected void hello2() {System.out.println("Hello again from ConcreteClass");}
}

下面的代码生成ConcreteClass类的委托类

//ConcreteClassTest.java
package org.jingle.util.dydelegation.sample;import org.jingle.util.dydelegation.DelegationGenerator;public class ConcreteClassTest {public static void main(String[] args) {Class clazz = DelegationGenerator.getDelegationClass(new Class[] { ConcreteClass.class });System.out.println("Delegation class name = " +clazz.getName());System.out.println(ConcreteClass.class.isAssignableFrom(clazz));}
}

输出为:

Delegation class name =
org.jingle.util.dydelegation.sample.ConcreteClass_Delegation_0
true

DelegationGenerator.getDelegationClass()接受类数组为参数,返回一个Java类,这个类继承了给定类或者实现了给定接口。缺省情况下,生成的委托类和被委托类是在同一个包中。

委托类能够像下面那样被实例化:

//object to be delegated
Object obj = ...;
//some concrete invocation handler instance
DelegationInvocationHandler h = ...; Constructor c = clazz.getConstructor(new Class[] { Object.class });
Object inst = c.newInstance(new Object[] {obj});
((Delegation) inst)._setInvocationHandler(h);

练习2:创建一个抽象类的委托类

DelegationGenerator也能够生成一个对于抽象类的具体的委托类

//AbstractClass.java
package org.jingle.util.dydelegation.sample;public abstract class AbstractClass {public abstract void wave();
}
//AbstractClassTest.java
package org.jingle.util.dydelegation.sample;import java.lang.reflect.Modifier;import org.jingle.util.dydelegation.DelegationGenerator;public class AbstractClassTest {public static void main(String[] args) {Class clazz = DelegationGenerator.getDelegationClass(new Class[] { AbstractClass.class });System.out.println("Delegation class name = " +clazz.getName());System.out.println(Modifier.isAbstract(clazz.getModifiers()));}
}

输出:

Delegation class name = 
org.jingle.util.dydelegation.sample.AbstractClass_Delegation_0
false

生成的委托类是具体类而不是抽象类。

练习3。创建类和多个接口的委托类

DelegationGenerator.getDelegationClass()能够同时委托一个类和多个接口,生成委托类来委托给定的类和接口。并且,将去除重复的接口。

//Idel1.java
package org.jingle.util.dydelegation.sample.bean;public interface Idel1 {public void idel1();
}
//Idel2.java
package org.jingle.util.dydelegation.sample.bean;public interface Idel2 {public void idel2();
}
//ComplexClassTest.java
package org.jingle.util.dydelegation.sample;import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.sample.bean.Idel1;
import org.jingle.util.dydelegation.sample.bean.Idel2;public class ComplexClassTest {public static void main(String[] args) {Class clazz = DelegationGenerator.getDelegationClass(new Class[] {ConcreteClass.class, Idel1.class, Idel2.class });System.out.println(Idel1.class.isAssignableFrom(clazz));System.out.println(Idel2.class.isAssignableFrom(clazz));System.out.println(ConcreteClass.class.isAssignableFrom(clazz));}
}

输出:

true
true
true

生成的委托类扩展了给定的类ConcreteClass,实现了所有的给定接口:Idel1和Idel2。

练习4。创建单个对象的委托对象

根据一个特定的被委托对象,DelegationGenerator能够直接生成一个委托对象。

// ConcreteClassTest2.java
package org.jingle.util.dydelegation.sample;import java.lang.reflect.Method;import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.DelegationInvocationHandler;
import org.jingle.util.dydelegation.DummyInvocationHandler;public class ConcreteClassTest2 {public static void main(String[] args) {ConcreteClass inst = new ConcreteClass();DelegationInvocationHandler handler =new SimpleHandler();ConcreteClass delegation = (ConcreteClass)DelegationGenerator.newDelegationInstance(inst, handler);delegation.hello();delegation.hello2();System.out.println(delegation.toString());}
}class SimpleHandler extends DummyInvocationHandler {public boolean invokeBefore(Object bean,Method method, Object[] args)throws Throwable {System.out.println("Interrupted by SimpleHandler");return super.invokeBefore(bean, method, args);}
}

输出:

Interrupted by SimpleHandler
Hello from ConcreteClass
Hello again from ConcreteClass
Interrupted by SimpleHandler
org.jingle.util.dydelegation.sample.ConcreteClass@ef5502

DummyInvocationHandler 是一个DelegationInvocationHandler虚拟的实现。它总是在方法invokeBefore()中返回true,在方法 invokeAfter()中直接返回输入的值,并且在invokeAfterException()中直接抛出输入异常throwable。带有 DummyInvocationHandler委托对象和被委托对象有相同的动作。

DelegationGenerator.newDelegationInstance()将一个对象和DelegationInvocationHandler实例作为参数。它返回委托对象来委托给定对象。

所有对委托对象的调用方法将被DelegationInvocationHandler实例委托,除了下面的方法:
没有public限定符的方法。
没有final限定符的方法
有static限定符的方法
定义在一个对象类中,除了hashCode(),equals()和toString()而外的方法。

练习5。创建一个Java核心类对象的委托对象

你曾经想过委托存在的Java核心类对象吗?可以像通常所做的那样委托它。

//DateTest.java
package org.jingle.util.dydelegation.sample;import java.lang.reflect.Method;
import java.util.Date;import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.DelegationInvocationHandler;
import org.jingle.util.dydelegation.DummyInvocationHandler;public class DateTest {public static void main(String[] args) {Date date = new Date();DelegationInvocationHandler handler = new DateClassHandler();Date delegation = (Date) DelegationGenerator.newDelegationInstance(date, handler);System.out.println("Delegation class = " +delegation.getClass().getName());System.out.println("True date = " +date.getTime());System.out.println("Delegation date = " +delegation.getTime());}
}class DateClassHandler extends DummyInvocationHandler {public Object invokeAfter(Object bean,Method method, Object[] args,Object result) throws Throwable {if (method.getName().equals("getTime")) {return new Long(((Long)result).longValue() - 1000);}return super.invokeAfter(bean, method, args, result);}
}

输出

Delegation class = org.jingle.util.dydelegation.Date_Delegation_0
True date = 1099380377665
Delegation date = 1099380376665

当创建一个Java核心类的委托类时,委托类不会和Java核心类在同一个包中,因为Java安全模型不允许用户定义的ClassLoader定义以java开头的包中的类。

DateClassHandler代理在invokeAfter()中的getTime()方法调用,返回的值比正常返回值低1000。

高级用法

练习6。模拟代理行为

委托能够做代理所做的事吗?绝对可以!动态委托覆盖了代理的功能。给定一个适当的委托句柄,它能够模拟Java代理的行为。

// ProxyTest.java
package org.jingle.util.dydelegation.sample;import java.lang.reflect.Method;import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.DelegationInvocationHandler;
import org.jingle.util.dydelegation.DummyInvocationHandler;
import org.jingle.util.dydelegation.sample.bean.Idel1;
import org.jingle.util.dydelegation.sample.bean.Idel2;public class ProxyTest {public static void main(String[] args) {DelegationInvocationHandler handler = new ProxyHandler();Object delegation =DelegationGenerator.newDelegationInstance(null,new Class[] { Idel1.class, Idel2.class },null, handler);((Idel1) delegation).idel1();((Idel2) delegation).idel2();}
}class ProxyHandler extends DummyInvocationHandler {public boolean invokeBefore(Object bean,Method method, Object[] args)throws Throwable {return false;}public Object invokeAfter(Object bean, Method method, Object[] args,Object result) throws Throwable {String name = method.getName();if (name.equals("idel1"))System.out.println("Hello from idel1");else if (name.equals("idel2"))System.out.println("Hello from idel2");return super.invokeAfter(bean, method, args, result);}
}

输出
Hello from idel1
Hello from idel2

ProxyHandler在方法invokeBefore()中返回false,这意味着在委托对象上的所有方法调用都不会代理原来的对象。与代理Proxy相同,它使用方法invokeAfter()来定义委托行为。

DelegationGenerator.newDelegationInstance()方法有另一个版本。它包含4个参数:
被委托的对象
这可能是null。如果它不是null,它必须是所有给定的类和接口的实例

被委托的类数组
这可能包含多个接口,和一个类。

委托类名字
如果是null,系统将会生成一个名字

一个DelegationInvocationHandler实例,使用它来定义委托的行为。
从输出结果,我们可以看到委托对象是两个接口Idel1和Idel2的实例。它的行为就是定义在句柄中的。

练习7.部分委托

直到现在,我们已经委托了指定对象所有的函数。怎么样来进行对象函数的部分委托呢?

//MyDate.java
package org.jingle.util.dydelegation.sample.bean;import java.util.Date;public class MyDate extends Date implements Idel1, Idel2 {public void idel1() {}public void idel2() {}
}
// MyDateTest.java
package org.jingle.util.dydelegation.sample;import java.util.Date;import org.jingle.util.dydelegation.DelegationGenerator;
import org.jingle.util.dydelegation.DelegationInvocationHandler;
import org.jingle.util.dydelegation.DummyInvocationHandler;
import org.jingle.util.dydelegation.sample.bean.Idel1;
import org.jingle.util.dydelegation.sample.bean.Idel2;
import org.jingle.util.dydelegation.sample.bean.MyDate;public class MyDateTest {public static void main(String[] args) {MyDate inst = new MyDate();DelegationInvocationHandler handler =new DummyInvocationHandler();Object delegation = DelegationGenerator.newDelegationInstance(inst,new Class[] { Idel1.class, Idel2.class },null, handler);System.out.println(delegation instanceof Idel1);System.out.println(delegation instanceof Idel2);System.out.println(delegation instanceof Date);}
}

输出

true
true
false

MyDate 扩展了Date类,实现了Idel1和Idel2接口。DelegationGenerator.newDelegationInstance()使用 MyDate实例作为被委托的实例,将委托范围限制在Idel1和Idel2中。换句话说,生成的委托对象是Idel1和Idel2的实例,而不是 Date实例。

JAVA面试要点006---.net中的委托与java中的代理模式和委托相关推荐

  1. java面试要点---ArrayList list = new ArrayList(); 在这个泛型为Integer的ArrayList中存放一个String类型的对象。

    java面试要点---ArrayList list = new ArrayList(); 在这个泛型为Integer的ArrayList中存放一个String类型的对象. ArrayList list ...

  2. C#中的委托和Java中的“委托”(Java8 lambda表达式)

    Java8中新增了有关对lambda表达式的支持,其本质是一种编译阶段的语法糖,效果是双向的,代码看似简洁的前提下牺牲了部分可读性. 我们可以尝试实现一些有意思的东西,先来看段C#委托(delegat ...

  3. Java面试宝典最新整理 下篇(持续更新中)

    51. ThreadLocal 是什么?有哪些使用场景? 线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享. Java提供ThreadLocal类来支持线程局部变量,是一种实现 ...

  4. Java面试要点整理

    1.      Java的GC的理解 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回 ...

  5. java面试没有全部答对_十道java基础面试题,你能保证全答对吗?

    java初级开发面试中经常被问到的问题有好多,下面总结一下常见的问题,先给问题,大家思考一下再看答案. 1.JDK 和 JRE 有什么区别?2.== 和 equals 有什么区别?3.说说final在 ...

  6. 网易Java面试必问:月薪20k+的Java面试都问些什么

    前言 不知道你们发现没有,在很多互联网公司基本上都是80后,90后居多,很少还有超过40岁的程序员.可能很多人心里都有一个疑问,那就是这些40多岁的程序员都干嘛去了呢?创业显然只是极少数的人,至于管理 ...

  7. 拉勾网《32个Java面试必考点》学习笔记之一------Java职业发展路径

    本文为拉勾网<32个Java面试必考点>学习笔记.只是对视频内容进行简单整理,详细内容还请自行观看视频<32个Java面试必考点>.若本文侵犯了相关所有者的权益,请联系:txz ...

  8. java 面试 概率论_编程培训-115个Java面试题和答案B.pdf

    编程培训-115个Java面试题和答案B.pdf "玩转"Java系列 1 题目115个Java面试题和答案终极(下) 第一篇讨论了面向对象编程和它的特点,关于Java和它的功能的 ...

  9. Java面试核心知识点(283页)Java面试题合集最新版(485页)

    阿里.腾讯两大互联网企业传来裁员消息,很多人都陷入担心,不安情绪蔓延-- 其实大家应该更冷静和理性地看待大厂裁员.每年三四月都是大厂人员调整期,这个季节是各个公司战略调整.战略规划的一个关键期,肯定会 ...

最新文章

  1. python 面向对象(三大特性)
  2. mysql sql语句大全
  3. 《单页Web应用:JavaScript从前端到后端》——1.4 小结
  4. android 绘制分割线,Android EditText在其drawable和它的文本之间绘制一个分隔线
  5. 【翻译】What is State Machine Diagram(什么是状态机图)?
  6. 在C# 获取当前应用网址
  7. C#软件授权、注册、加密、解密模块源码解析并制作注册机生成license
  8. ajax get怎么传参数值,如何使用jQuery在GET请求中传递参数
  9. Bandit UCB推导
  10. 1087 1 10 100 1000
  11. session与cookie之间的关系
  12. C# SyncDictionary类
  13. vscode代码对比功能
  14. FL studio 20简易入门教程 -- 第七篇 -- 音频、音源与音色
  15. 说说教育机构教学课程视频加密是如何实现的?
  16. 【从零开始学习YOLOv3】3. YOLOv3的数据加载机制和增强方法
  17. cmd介绍及常用命令
  18. 1189 SEARCH
  19. 逆向工程技术的研究现状及发展趋势
  20. 关于 RTOS 的选择

热门文章

  1. HDU-1867A + B for you again(kmp)
  2. QML学习【一】Basic Types
  3. 46. Permutations 1
  4. 【Django】(3)创建网页:学习笔记主页
  5. 初探 vue 插件开发
  6. JAVA - 守护线程(Daemon Thread)
  7. 为什么都反对XML而支持使用json呢?
  8. Redis PPT abstract
  9. 关于Java String对象创建问题解惑
  10. 【leetcode 简单】 第八十四题 两个数组的交集