黑马程序员--装饰设计模式和动态代理

-------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

装饰设计模式解决:对一组类进行功能的增强。
包装:写一个类(包装类)对被包装对象进行包装;
1、包装类和被包装对象要实现同样的接口;
2、包装类要持有一个被包装对象;
3、包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现;
适用范围:
(1)需要扩展一个类的功能,或给一个类添加附加职责。
(2)需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
(3)需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
(4)当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种 组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为 
 类定义不能用于生成子类。
优点:
(1) Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
(2)通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
缺点:
(1)这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
(2)装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
(3)装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。
代理类的作用与原理:
1、生活中的代理:就是常说的代理商,从厂商将商品卖给消费者,消费者不用很麻烦的到厂商在购买了。
2、程序中的代理:要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如异常处理、日志、计算方法的运行时间、事物管理等等。如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。


public class Test {	
	public static void main(String[] args) {
		Subject subject = new ProxyRealSubject();
		subject.action();
	}
}

//接口
interface Subject {
	void action();
}
//被代理类
class RealSubject implements Subject {

	@Override
	public void action() {
		System.out.println("------------");
		System.out.println("------------");
		System.out.println("这是被代理的类");
		System.out.println("------------");
		System.out.println("------------");
	}

}
//代理类:
class ProxyRealSubject implements Subject{
	Subject subject;
	public ProxyRealSubject() {
		System.out.println("这是代理类");
		subject = new RealSubject();
	}

	@Override
	public void action() {
		System.out.println("代理类开始");
		subject.action();
		System.out.println("代理类结束");
	}
}
</span>

面向方面的编程:

1.系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面

2.交叉业务的编程问题即为面向方面的编程(Aspectoriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。

3.使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

 

二、动态代理:

著名的框架spring中的核心技术aop就使用了动态代理技术。

出现的原因:要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!

概念:JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。

1,JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

2,CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。

3,代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:

1.在调用目标方法之前

2.在调用目标方法之后

3.在调用目标方法前后

4.在处理目标方法异常的catch块中

 

创建动态类的实例对象的步骤:

1、用反射获得构造方法

2、编写一个最简单的InvocationHandler类

3、调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去

4、打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。

5、将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。

 

总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?

三个方面:

1、生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;

2、产生的类字节码必须有个一个关联的类加载器对象;

3、生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。

题目: 写一个ArrayList类的代理,实现和ArrayList中完全相同的功能,并可以计算每个方法运行的时间。

package com.itheima;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

public class Test4 {

	/**
	 * 4、 写一个ArrayList类的代理,实现和ArrayList中完全相同的功能, 并可以计算每个方法运行的时间。
	 * 
	 * 思路: 1.写一个获得代理类的通用方法: public static Object getProxy(final Object target,
	 * final Advice advice){} target是被代理的类,Advice是需要插入系统中的功能接口。
	 * 
	 * 2.定义一个接口Advice,为插入的系统功能定义规则interface Advice{}
	 * 
	 * 3. 实现Advice接口,实现具体的系统功能,即:计算每个方法运行的时间。 class MyAdvice implements
	 * Advice{实现方法}
	 * 
	 * @author 鲁中需
	 */
	public static void main(String[] args) {
		// 获取ArrayList的代理
		final ArrayList list = new ArrayList();
		Collection proxy = (Collection) getProxy(list, new MyAdvice());
		// 演示效果:
		proxy.add("flx");
		proxy.add("bxd");
		proxy.add("zxx");
		proxy.add("lxz");
		System.out.println(list.size());
	}

	// 获得代理类的通用方法:
	public static Object getProxy(final Object target, final Advice advice) {
		Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {

			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				// 在调用方法前,调用beforeMethod(Method method)
				advice.beforeMethod(method);
				Thread.sleep(1000);
				Object retVal = method.invoke(target, args);
				// 在调用方法后,调用afterMethod(Method method)
				advice.afterMethod(method);
				return retVal;
			}
		});
		return proxy;

	}
}

// 定义一个接口Advice,为插入的系统功能定义规则
interface Advice {
	public abstract void beforeMethod(Method method);

	public abstract void afterMethod(Method method);
}

// 实现Advice接口,实现具体的系统功能,即:计算每个方法运行的时间。
class MyAdvice implements Advice {

	long beginTime = 0;

	@Override
	public void beforeMethod(Method method) {
		System.out.println("调用方法前的功能");
		beginTime = System.currentTimeMillis();
	}

	@Override
	public void afterMethod(Method method) {
		System.out.println("调用方法后的功能");
		long costTime = System.currentTimeMillis() - beginTime;
		System.out.println("方法:" + method + " running time of " + costTime + "毫秒!");
	}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值