Java 利用反射实现C#的委托



一, 观察者模式的缺点

在之前的博文的介绍过观察者模式了.

观察者模式可以让多个观察者同时观察1个被观察者.

也就说被观察者可以1次过执行所有观察者的update()方法.

再通俗d来讲, 就是多个观察者的Update()方法交给被观察者来执行了.




观察者主要应用在Gui 界面的控件事件上,   例如按个按钮可以,令多个其他控件同时产生变化.


但是观察者模式有1个限制, 就是所有观察者类必须实现观察者(Observer)接口. 这个也是回调(callback)方法的1个实现.


现实项目中,  相当于一部分类是不能修改的, 很可能是Jar包发布或者你没有修改权限. (封闭--开放原则)

如果某个类没有实现观察者(Observer)接口,  那么有没有1个办法将这个类的某个方法也交给被观察者来执行呢?




二, 一个题目

假如当前有两个类, 

1个是类A, 里面有1个无返回值无参方法a().

另1个是类B, 里面有1无返回值个无参方法b().


这两个类没有实现任何接口, 也不能被修改.

要求写1个类S,  这个类S类似与观察者模式的被观察者(Subject), 可以添加若干个类A或者类B的对象), 并可以通知它们执行自己的a()/b() 方法.


如果让类A和类B实现观察者(Observer)是很简单的.

难点就是它们都不能被修改.



三, C#的委托实现

C#有一种类叫delegate(委托), 他可以让方法(函数)的本身作为1个参数.

同样, 委托也可以是1个容器, 用于存放不同的对象的方法(前提是这些方法返回值和参数类型一样)


C#是这样实现的.

3.1 类A

class A
    {
        public void a()
        {
            Console.WriteLine("A.a()");
        }

    }


3.2 类B

  class B
    {
        public void b()
        {
            Console.WriteLine("B.b()");
        }

    }


3.3 类S

    public delegate void DelegateNoPra();

    class S
    {
        public DelegateNoPra dg;

        public void sNotify()
        {
            dg();
        }
    }

注意, 这里的委托(delegate void DelegateNoPra是定义在类S的外面的, 也就是跟类S同级.

然后在类S里构造1个DelegateNoPra的对象dg.


这里的dg相当于1个容器.


在sNotify()调用dg(); 相当于调用dg容器内所有的传入的方法.


3.4 客户端代码
  static void Main(string[] args)
        {

            S s = new S();
            s.dg += new A().a;
            s.dg += new B().b;
            s.dg += new B().b;

            s.sNotify();
            Console.Read();
        }

客户端代码很容易看懂,

先实例化1个S对象

然后将1个A对象的方法a, 两个B对象的方法b 放入S对象的委托容器.

最终一次过被执行


输出:

A.a()
B.b()
B.b()


注意, 传入的方法必须是无参方法().   也就是将委托对象必须具有相同的参数类型和

如果有参数的方法怎么传入?

则必须再定义1个对应的delegate委托.

四, Java的反射实现

可以见到, C#的代码相当简洁.

而委托这种东西看起来比观察者模式更加方便.


但是现实上是先有观察者模式, 再有C#的委托的.


而且Java是没有委托(delegate)这种东西的, 但是java有反射, 利用java的反射特性也可以实现上面的功能.

4.1 类A

public class A {
	public void a(){
		System.out.println("A.a()");
	}
}


4.2 类B

public class B {
	public void b(){
		System.out.println("B.b()");
	}
}

4.3 类ObjMethod

这里稍稍想想, 到底如何将类A和类b的指定方法传入到另1个类呢?

其实我们可以把它拆分成两部分:

1. 传送对象本身(Object).

2. 传送方法的名字(String).


至于怎样把这两种不同类型的东西放入类S的容器?  方法有很多种,

这里我新建1个类ObjMethod, 把这两种东西封装在一起.


而且我是打算把它放入HashSet容器的, 所以重写了hashCode()和 equals()方法, 只要上面两个成员相等, 我们就认为是相同的两个对象.


代码:


public class ObjMethod {
	private Object obj;
	private String method;
	
	public ObjMethod(Object obj, String method){
		this.obj = obj;
		this.method = method;
	}

	public String getMethod() {
		return this.method;
	}

	public Object getObj() {
		return this.obj;
	}

	@Override
	public boolean equals(Object o){
		ObjMethod m = (ObjMethod)o;
		return (this.getObj() == m.getObj()) && (this.getMethod().equals(m.getMethod()));
	}
	
	@Override
	public int hashCode(){
		return this.getObj().hashCode() * this.getMethod().hashCode();
	}
}


4.4 类S

类S的Notify()方法要用到反射了.

其实步骤很简单:

1. 从HashSet获取对象Obj 和 方法名method

2. 利用反射特性获得Obj的类.

3. 利用Obj的类和方法名获得那个方法.

4. 执行这个方法.


import java.util.HashSet;
import java.util.Iterator;
import java.lang.reflect.Method;
public class S {
	private HashSet<ObjMethod> methodList = new HashSet<ObjMethod>();
	
	public void attach(Object obj, String method){
		this.methodList.add(new ObjMethod(obj,method));
	}
	
	public void detach(Object obj, String method){
		this.methodList.remove(new ObjMethod(obj,method));
	}
	
	public void sNotify(){
		if (this.methodList.isEmpty()){
			return;
		}
		
		Iterator<ObjMethod> it = this.methodList.iterator();
		while (it.hasNext()){
			ObjMethod m = (ObjMethod)it.next();
			
			Class<?> objClass = m.getObj().getClass(); //get the class of the object
			try{
				Method method = objClass.getMethod(m.getMethod(), new Class[]{}); //no any parameters
				method.invoke(m.getObj(),new Object[]{});//no parameters
			}catch(Exception e){
				e.printStackTrace();
			}
			
		}
	}
	
}


4.5 客户端代码

		S s = new S();
		s.attach(new A(), "a");
		s.attach(new A(), "a");
	
		B b1 = new B();
		s.attach(b1, "b");
		s.sNotify();
		
		System.out.println("Step 2!");
		s.detach(b1,"b");
		s.sNotify();
	

代码也很容易看懂,

跟C#版本差不多. 只不过要把对象和方法名作为两个参数传入到类S对象的HashSet容器.


输出:

A.a()
B.b()
A.a()
Step 2!
A.a()
A.a()



上面的例子跟C#的委托一样, 同样要求传入的方法具有相同的参数类型.

当然, 再完善下甚至可以有一定限度地支持不同的参数类型.


五, 小结

利用Java的反射特性同样实现类似C#委托的功能, 当然代码看起来远远没有C#委托的简洁.

这是因为微软把很多底层的东西封装起来了, 更加方便程序猿的使用.









































































  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nvd11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值