关于设计模式(主要是思想方面)
001.定义:
是一套反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结
002.目的:
为了可重用代码、让代码更容易被他人理解、保证代码可靠性
设计模式之工厂模式:
定义:
为其他对象提供一种代理,以控制对这个对象的访问
代理对象起到中介作用,可以去掉功能服务或增加额外的服务
优点:
代理模式可以屏蔽用户真正的请求对象,是用户程序和正在的对象解耦
使用代理来担当哪些创建耗时的对象的替身
使用场景:
程序可能不希望用户直接访问该对象,而是提供一个特殊的对象以控制对当前对象的访问
如果对象位于远程主机上,需要为用户提供访问该远程对象的能力
如果我们需要对Jar包里面某个类、方法进行一些修改,此时就可以使用动态代理
举例: 《购买火车票为例》
正常情况下我们购买火车票回去火车站购买,但是我家离的比较远就会选择家附件的火车票代售点购买,而火车票代售点就是火车站的一个代理,代理火车站完成售票的一个过程。
此外,代售点会提供一些额外的服务:电话预约(收取一定的额外费用)
但是,代售点不支持退票服务,退票只能去火车站办理
代理对象就是起到一个中介的作用,去掉功能服务、增加额外服务
代理模式的分类:
远程代理:
为不同地理的对象提供局域网代表对象
类似于客户端、服务器(C/S)这种模式,是远程通讯的缩影
虚拟代理:
将资源消耗很大或者比较复杂的对象延迟,在需要的时候创建
保护代理:
控制对一个对象的访问权限
智能引用代理:
提供对目标对象额外的服务
代理模式两种实现方式(以智能引用代理来讲解这两种代理):
智能引用代理在实际开发中用到的最多、最典型的:日志处理、权限处理、事务处理...
静态代理:
代理和被代理对象在代理之前是确定的,他们都实现相同的接口或继承相同的抽象类
例子分析:
有一辆车,车有一个 行驶方法,通过代理除了要实现行驶的方法还需要增加一个记录行驶时间的方法
具体实现见代码:
普通实现方式:
接口类
/** * 接口类:具体对象car需要实现改接口 */ public interface Moveable { //汽车行驶的方法 void move(); }
具体对象Car(代理对象)
/** * 具体对象:汽车 */ public class Car implements Moveable { private static final String TAG = "Car"; @Override public void move() { //实现开车:给个一秒之内的随机数,模拟汽车行驶 long startTime = System.currentTimeMillis(); Log.i(TAG, "startTime: " + "汽车开始行驶。。。"); try { Thread.sleep(new Random().nextInt(1000)); Log.i(TAG, "move: " + ""); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); Log.i(TAG, "endTime: " + "汽车行驶结束。。。汽车行驶时间:" + (endTime - startTime) + "毫秒!"); } }
测试:
private void testProxy() { //普通模式 Car car = new Car(); car.move(); }
打印:
/com.test.okamiy.factorymode I/Car: startTime: 汽车开始行驶。。。
/com.test.okamiy.factorymode I/Car: move: 汽车行驶中。。。
/com.test.okamiy.factorymode I/Car: endTime: 汽车行驶结束。。。汽车行驶时间:805毫秒!
继承方式的代理模式实现:
接口类(不变)
/** * 接口类:具体对象car需要实现改接口 */ public interface Moveable { //汽车行驶的方法 void move(); }
具体的对象Car(代理对象)
/** * 具体对象:汽车 */ public class Car implements Moveable { private static final String TAG = "Car"; @Override public void move() { //实现开车:给个一秒之内的随机数,模拟汽车行驶 try { Thread.sleep(new Random().nextInt(1000)); Log.i(TAG, "move: " + "汽车行驶中。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
代理对象Car2(继承方式实现代理)
/** * 代理类:继承方式实现代理 */ public class Car2 extends Car { private static final String TAG = "Car2"; /** * 重写父类方法:调用父类的move方法,这样就实现了Car2对Car的代理 * 在Car2中记录一下行驶时间(增加功能),在方法前后增加我的代码逻辑 */ @Override public void move() { long startTime = System.currentTimeMillis(); Log.i(TAG, "startTime: " + "汽车开始行驶。。。"); super.move(); long endTime = System.currentTimeMillis(); Log.i(TAG, "endTime: " + "汽车行驶结束。。。汽车行驶时间:" + (endTime - startTime) + "毫秒!"); } }
测试:
private void testProxy() { // //普通模式 // Car car = new Car(); // car.move(); //继承方式实现代理 Moveable m = new Car2(); m.move(); }
打印:
/com.test.okamiy.factorymode I/Car2: startTime: 汽车开始行驶。。。
/com.test.okamiy.factorymode I/Car: move: 汽车行驶中。。。
/com.test.okamiy.factorymode I/Car2: endTime: 汽车行驶结束。。。汽车行驶时间:97毫秒!
聚合方式的代理模式实现:
接口类:
/** * 接口类:具体对象car需要实现改接口 */ public interface Moveable { //汽车行驶的方法 void move(); }
具体的对象Car(代理对象)
/** * 具体对象:汽车 */ public class Car implements Moveable { private static final String TAG = "Car"; @Override public void move() { //实现开车:给个一秒之内的随机数,模拟汽车行驶 try { Thread.sleep(new Random().nextInt(1000)); Log.i(TAG, "move: " + "汽车行驶中。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
代理对象Car3(聚合实现的代理)
/** * 代理类:聚合方式实现代理 * 聚合:一个类当中调用另一个对象 */ public class Car3 implements Moveable {//和代理对象Car一样实现相同的接口 private static final String TAG = "Car3"; private Car mCar; public Car3(Car mCar) { super(); this.mCar = mCar; } /** * 在move前后增加我们的逻辑 */ @Override public void move() { long startTime = System.currentTimeMillis(); Log.i(TAG, "startTime: " + "汽车开始行驶。。。"); mCar.move();//聚合的体现 long endTime = System.currentTimeMillis(); Log.i(TAG, "endTime: " + "汽车行驶结束。。。汽车行驶时间:" + (endTime - startTime) + "毫秒!"); } }
测试:
private void testProxy() { // //普通模式 // Car car = new Car(); // car.move(); // //继承方式实现代理 // Moveable m = new Car2(); // m.move(); //聚合方式实现代理 Car car = new Car(); Moveable m = new Car3(car); m.move(); }
打印:
/com.test.okamiy.factorymode I/Car3: startTime: 汽车开始行驶。。。
/com.test.okamiy.factorymode I/Car: move: 汽车行驶中。。。
/com.test.okamiy.factorymode I/Car3: endTime: 汽车行驶结束。。。汽车行驶时间:809毫秒!
静态代理的两种方式区别:
分析:比如现在我需要用代理方式做功能叠加,
先做日志后做时间逻辑处理:
继承方式:
我们需要新建一个Car4继承Car2或者Car,在move方法抚前后先做日志处理,再做时间逻辑处理
先做时间处理再做日志处理:
继承方式:
我们需要新建一个Car5继承Car2或者Car,在move方法抚前后先做时间逻辑处理,再做日志逻辑处理
先做权限再做时间后做日志:
继承方式:
我们需要新建Car6。。。
以此类推。。。
使用继承代理进行功能叠加,我们的代理类会无限的膨胀下去,所以不可取。
聚合实现代理的优势:
通过代码实现先记录日志再记录时间:
接口类:
/** * 接口类:具体对象car需要实现改接口 */ public interface Moveable { //汽车行驶的方法 void move(); }
具体的Car对象(代理对象):
/** * 具体对象:汽车 */ public class Car implements Moveable { private static final String TAG = "Car"; @Override public void move() { //实现开车:给个一秒之内的随机数,模拟汽车行驶 try { Thread.sleep(new Random().nextInt(1000)); Log.i(TAG, "move: " + "汽车行驶中。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
时间代理类:
/** * 时间代理类:功能叠加 */ public class CarTimeProxy implements Moveable { private static final String TAG = "CarTimeProxy"; private Moveable m; public CarTimeProxy(Moveable m) { super(); this.m = m; } @Override public void move() { long starttime = System.currentTimeMillis(); Log.i(TAG, "starttime: " + "汽车开始行驶。。。"); m.move(); long endtime = System.currentTimeMillis(); Log.i(TAG, "endtime: " + "汽车行驶结束。。。,行驶时间:" + (endtime - starttime) + "毫秒!"); } }
日志代理类:
/** * 日志代理类:功能叠加 */ public class CarLogProxy implements Moveable { private static final String TAG = "CarLogProxy"; private Moveable m; public CarLogProxy(Moveable m) { super(); this.m = m; } @Override public void move() { Log.i(TAG, "move start: " + "日志开始。。。"); m.move(); Log.i(TAG, "move end: " + "日志结束。。。,"); } }
测试:
private void testProxy() { //初始化代理对象:Car Car car = new Car(); //功能叠加:先记录日志再记录时间 CarTimeProxy time = new CarTimeProxy(car); CarLogProxy log = new CarLogProxy(time); log.move(); }
打印:
/com.test.okamiy.factorymode I/CarLogProxy: move start: 日志开始。。。
/com.test.okamiy.factorymode I/CarTimeProxy: starttime: 汽车开始行驶。。。
/com.test.okamiy.factorymode I/Car: move: 汽车行驶中。。。
/com.test.okamiy.factorymode I/CarTimeProxy: endtime: 汽车行驶结束。。。,行驶时间:889毫秒!
/com.test.okamiy.factorymode I/CarLogProxy: move end: 日志结束。。。,
通过代码实现先记录时间再记录日志,我们只需要在测试类调换一下代理类的顺序即可:
测试:(其余保持不变)
private void testProxy() { //初始化代理对象:Car Car car = new Car(); //功能叠加:先记录日志再记录时间 CarLogProxy log = new CarLogProxy(car); CarTimeProxy time = new CarTimeProxy(log); time.move(); }
打印:
/com.test.okamiy.factorymode I/CarTimeProxy: starttime: 汽车开始行驶。。。
/com.test.okamiy.factorymode I/CarLogProxy: move start: 日志开始。。。
/com.test.okamiy.factorymode I/Car: move: 汽车行驶中。。。
/com.test.okamiy.factorymode I/CarLogProxy: move end: 日志结束。。。,
/com.test.okamiy.factorymode I/CarTimeProxy: endtime: 汽车行驶结束。。。,行驶时间:922毫秒!
由此可见:我们使用聚合会比继承的方式灵活很多,使用继承会使我们的代理类无限的膨胀下去,使用聚合的方式,代理之间是可以相互传递的,互相组合
动态代理:
引入动态代理:
如果我们来一辆自行车、火车。。。怎么实现日志和时间的代理那,还需要在写其他的代理类吗?
不同的类(火车、自行车)需要实现日志、时间的代理,我们能不能统一成一个代理?
答案:能,于是乎动态代理就应运而生!动态产生代理,实现对不同类,不同方法的代理。
JDK动态代理:
分析一:
在代理类ProxySubject和被代理类RealSubject之间加入实现InvocationHandler的一个类,也叫事务处理器,向我们的日志、时间处理都是由他来处理的
分析二:
Interfere InvocationHandler是位于java.lang.reflect包下,只有一个方法invoke(Object obj,Method method,Object[] args),参数一obj是被代理的对象,被代理的方法,代理方法的参数。
这个抽象方法在代理类中动态实现。
分析三:
Proxy是我们产生动态代理的一个类,通过newProxyInstance(ClassLoader loader,Class[] interfere,InvocationHandler h),返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在接口中声明过的方法)
JDK动态代理代码实现:
接口类:
/** * 接口类:具体对象car需要实现改接口 */ public interface Moveable { //汽车行驶的方法 void move(); }
具体的Car对象(代理对象):
/** * 具体对象:汽车 */ public class Car implements Moveable { private static final String TAG = "Car"; @Override public void move() { //实现开车:给个一秒之内的随机数,模拟汽车行驶 try { Thread.sleep(new Random().nextInt(1000)); Log.i(TAG, "move: " + "汽车行驶中。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
时间动态代理类:
/** * 动态时间代理类 */ public class TimeHandler implements InvocationHandler { private static final String TAG = "TimeHandler"; //通过构造方法传递参数 private Object target; public TimeHandler(Object target) { super(); this.target = target; } /** * * @param proxy 被代理的对象 * @param method 被代理对象的方法 * @param args 方法参数 * @return 调用方法的返回值:返回一个动态代理类 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //直接调用被代理对象的方法,在方法的前后增加业务处理逻辑 long starttime = System.currentTimeMillis(); Log.i(TAG, "starttime: " + "汽车开始行驶。。。"); method.invoke(target);//传递过来的对象 long endtime = System.currentTimeMillis(); Log.i(TAG, "endtime: " + "汽车行驶结束。。。,行驶时间:" + (endtime - starttime) + "毫秒!"); return null;//因为我们Car的move方法没有返回值,所以直接返回null } }
测试:
/** * JDK动态代理测试类 */ private void testProxy() { //初始化代理对象:Car Car car = new Car(); //事务处理器:把我们的代理对象传过去 InvocationHandler h =new TimeHandler(car); //类加载器:car的class Class<?> cls = car.getClass(); //动态生成代理对象:Moveable(因为都是实现了Moveable接口) /** * loader 被代理类的类加载器 * interfaces 我们实现的接口 * h Invocationhandler 事件处理器 */ Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),h); //调用返回动态代理的move方法:这样就实现了在Car当中产生了一个对时间动态代理对象Moveable m.move(); }
打印:
/com.test.okamiy.factorymode I/TimeHandler: starttime: 汽车开始行驶。。。
/com.test.okamiy.factorymode I/Car: move: 汽车行驶中。。。
/com.test.okamiy.factorymode I/TimeHandler: endtime: 汽车行驶结束。。。,行驶时间:826毫秒!
总结JDK动态代理:
被代理对象首先要实现某些接口,然后在运行的时候生成它的class对象(代理类),产生的动态代理类不能做具体的事,我们必须声明一个Handler,由他来接管我们的实际的工作。比如我们想实现日志的代理,具体实现是在我们的Handler当中实现的
操作步骤:
01.首先创建一个实现InvocationHandler的类(事物处理器),在invoke方法当中添加具体的业务逻辑处理
02.创建被代理的类(Car)和接口(Moveable)
03.调用Proxy的静态方法,创建一个代理类
04.最后调用代理类的方法
动态代理模式不仅可以使用JDK的方式产生也可以使用CGLIB产生,二者对比:
分析:
JDK动态代理机制是只能代理实现了某些接口的类才能实现动态代理
CGLIB动态机制是针对类来产生动态代理,对指定的目标类产生一个子类,然后覆盖其中的方法,来实现功能的增强。但因为他是使用继承方式,所以不能对final修饰的类进行代理。
代码实现CGLIB动态代理:
首先引入Jar包:链接地址点击下载
代理对象(具体的对象):
/** * 被代理类:具体的类 */ public class Train { private static final String TAG = "Train"; public void move() { Log.i(TAG, "move: " + "火车行驶中。。。"); } }
CGLIB动态代理类:
/** * CGLIB 动态代理类 */ public class CglibProxy implements MethodInterceptor { private static final String TAG = "CglibProxy"; //创建代理类的属性 private Enhancer mEnhancer = new Enhancer(); //创建代理类:根据传入要被代理的类产生代理类 public Object getProxy(Class clazz) { //设置创建子类的类:为我们传入的被代理类产生代理类 mEnhancer.setSuperclass(clazz); //设置回调 mEnhancer.setCallback(this); //创建子类的实例并返回 return mEnhancer.create(); } /** * 拦截所有目标类方法的调用 * * @param o 目标类的实例(Train) * @param method 目标方法的反射对象 * @param objects 方法的参数 * @param methodProxy 代理类的实例 * @return * @throws Throwable */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Log.i(TAG, "intercept: " + "日志处理开始。。。"); //根据代理类的实例调用父类的方法实现调用被代理类的方法:因为CGLIB采用继承的方式,产生的代理类其实就是父类的子类 methodProxy.invokeSuper(o, objects);//参数1:目标类实例 ,参数2:方法参数 Log.i(TAG, "intercept: " + "日志处理结束。。。"); return null; } }
测试:
/** * CJLIB动态代理测试类 */ private void testProxy() { //创建一个代理类对象 CglibProxy proxy = new CglibProxy(); //获取我们要代理类(Train)的代理类对象 Train t= (Train) proxy.getProxy(Train.class); t.move(); }
打印:
日志处理开始。。。
火车行驶中。。。
日志处理结束。。。
终点学习JDK动态代理:
JDK动态代理可以对实现了某些接口的任意类、任意方法,产生任意的代理
动态代理的实现思路如下,具体代码实现可以参照慕课网进行学习:
/** * 动态代理实现思路: * 实现功能:通过Proxy的newProxyInstance返回代理对象 * 1.声明一段源码(动态产生代理类) * 2.编译源码(JDK Compiler API),编译成功后产生新的类(代理类) * 3.将这个类load到内存当中,产生一个新的对象(代理对象) * 4.return 代理对象 */
动态代理实际应用:
我想调用某个jar包里面某个类的某个方法,因为我们不能修改源码,所以我们就可以采用代理模式进行实现在方法前后进行一些操作,这种编程也叫AOP(面向切面编程)。
面向切面编程:
在不改变原有类、方法的情况下,增加一些额外的业务逻辑。
Last:本篇参照慕课网视频学习,有不懂的可以去慕课网看视频学习!