【Java学习】 —— 学习自《Thinking In Java》


接上篇。

我们已经尝试用接口和装饰器模式“实现”了混型。

这里暂且就叫“实现”了。

不过,上面的方式也都还不太完美,那既然之前学过动态代理。


我们可以思考,上面2种方式,最后混合的类都需要静态的来修改,

也就是如果我们要添加新的混合物或者混合方式,就要改好多代码。

那如果我们能够在混合类实例的时候,将要混合的东西打包放进来,

然后混合类更“智能”的实现这些实例和接口,这样来混合,可拓展性就好多了!

没错,这里就需要用到之前我们了解过的“动态代理”。


OK,不多说,动态代理的混合类(比较“核能”*):

class MixProxy implements InvocationHandler {
	private Map<String, Object> delegatesMethodMap;

	public MixProxy(TwoTuple<Object, Class<Object>>... pairs) {
		delegatesMethodMap = new HashMap<String, Object>();
		for (TwoTuple<Object, Class<Object>> pair : pairs) {
			for (Method method : pair.mB.getMethods()) {
				String methodName = method.getName();
				if (!delegatesMethodMap.containsKey(methodName)) {
					delegatesMethodMap.put(methodName, pair.mA);
				}
			}
		}
	}

	@Override
	public Object invoke(Object arg0, Method arg1, Object[] arg2)
			throws Throwable {
		String methodName = arg1.getName();
		Object delegate = delegatesMethodMap.get(methodName);
		return arg1.invoke(delegate, arg2);
	}

	public static Object newInstance(TwoTuple<Object, Class<Object>>... pairs) {
		Class[] interfaces = new Class[pairs.length];
		for (int i = 0; i < pairs.length; i++) {
			interfaces[i] = (Class) pairs[i].mB;
		}
		ClassLoader loader = pairs[0].mA.getClass().getClassLoader();
		return Proxy.newProxyInstance(loader, interfaces, new MixProxy(pairs));
	}
}
很复杂是不是?我也觉得!

没事,我们一点一点来解释。

1.继承InvocationHandler,这个没什么可说,动态代理,然后实现invoke方法。

2.定义了一个Map,有什么用?

String存放方法名,Object存放实例。

这里的对应关系,就是Object这个实例,该有什么方法,都给记录下来。

3.构造函数,又用到了之前的知识,元组。

这里是2元组,一个是实例,一个是Class。

接受的参数是可变的二元组。

(这里有些问题,会给出一个警告

Type safety: Potential heap pollution via varargs parameter pairs,为什么?

因为这里的元组,是泛型的元组,将泛型元组用在可变参数上,会有什么情况?

就等于是定义了一组参数,这组参数既不确定数量,也不确定类型!

那就是什么阿猫阿狗都可以往这里传…

这肯定不科学啊,谁知道你后面要怎样用这个参数?一不小心就会产生异常!)

4.构造里面的内容,第一件事先实例化。

然后遍历元组,分别将类型信息中的方法取出来,判断是否已经有这个方法了,

然后将方法和这个接口类型记录下来。这里应该很好理解。

5.动态代理的接口方法,invoke!这里就是将每个方法重新反射回去。

这里的方法,都是我们在实例化的时候记录下来的。如果没有,delegate为空,则会有异常!

6.这个静态方法,生成一个混合的实例!这是我们最终要的!

我们最终要一个“混合”的实例,通过上面的信息,给了我们什么启示?

我们已经将混合的每个方法和方法所对应接口的类型信息记录了下来,

那么我们就能够通过这些信息,反射回一个能实现这些接口的类了。

当然,最终return的时候,我们是需要通过Proxy.newProxyInstance方法实现的,

这个方法要接受3个参数(以前也用过)

第一个就是ClassLoader,这个我们可以在pairs里随便取一个(别出界了!取0就好了…)

第二个就是Class<?>[] interfaces,这里我的理解就是要一个接口和类型集,就是最后构造的这个类的类型信息吧。

第三个是我们这个DynamicProxy类(继承了InvocationHandler)。

好,这个完成了,我们来看看main(这里把3个方式的都列出来了,也可以对比一下吧):

public class TestMixin {

	public static void main(String[] args) {
		// 以接口的方式实现
		System.out.println("Mix by Interface:");
		MixtureByInterface mixtureInterface = new MixtureByInterface();
		System.out.println(mixtureInterface.methodA());
		System.out.println(mixtureInterface.methodB());
		System.out.println();

		// 以装饰器的方式实现
		System.out.println("Mix by Decorator:");
		ToMixObjectD mixtureDecorator = new ToMixObjectD(new ToMixObjectD(
				new MixObjectBasic()));
		System.out.println(mixtureDecorator.getValue());
		System.out.println(mixtureDecorator.methodD());
		// 局限性就在这里:只有最外的那层包装的接口可用
		// System.out.println(mixtureDecorator.methodC());
		System.out.println();

		// 以动态代理的方式实现
		System.out.println("Mix by DynamicProxy:");
		Object mixObject = MixProxy.newInstance(new TwoTuple(
				new ToMixObjectA(), IToMixObjectA.class), new TwoTuple(
				new ToMixObjectB(), IToMixObjectB.class));
		IToMixObjectA mixObjectA = (IToMixObjectA) mixObject;
		System.out.println(mixObjectA.methodA());
		IToMixObjectB mixObjectB = (IToMixObjectB) mixObject;
		System.out.println(mixObjectB.methodB());
		System.out.println();
	}
}
输出结果:

Mix by Interface:
I'm method A
I'm method B


Mix by Decorator:
null
I'm method D


Mix by DynamicProxy:
I'm method A
I'm method B


以上信息都是在看书后自己的思考和理解,如果有不对的地方。

欢迎指出和交流!谢谢!



更多推荐

【Java学习】混型尝试(3 - 动态代理)