设计模式系列:
0. Android开发常用设计模式;
小白:最近听完毛毛哥讲的那些设计模式,觉得自己对接口和抽象类顿悟了很多
三毛:“”
小白:那毛毛哥你考考我
一、常见需求场景
三毛:“那我问你,‘像淘宝购物车里面的商品,选中就删除,你用代码模拟,我看看你的思路’”
二、基本解决方法
小白:小case,代码如下
//写个购物车商品接口
public interface IGoods {
void delete();//删除
}
//操作实现类
public class GoodsImpl implements IGoods{
@Override
public void delete() {
System.out.println("请求后台删除操作...");
}
}
//使用
GoodsImpl goods = new GoodsImpl();
goods.delete();
小白:怎么样,毛毛哥,快夸我,快夸我
三毛:“,我的白,看来我前面给你讲的设计模式白讲了”
小白:
三、基本解决方法存在的问题
三毛:”假设突然老板说新版本购物车的商品删除前要先做一些其他操作,删除成功后又要做其他操作,但是不要改变(影响)原来写好的购物车里删除操作,你怎么决解”
小白:因为不能影响原来写好的删除操作,那我肯定不能像下面这样写了(开闭原则)
public class GoodsImpl implements IGoods{
@Override
public void delete() {
//在这里做删除前的一些操作...
System.out.println("请求后台删除操作...");
//在这里做删除后的一些操作...
}
}
三毛:”嗯,那你怎么决解呢”
小白:那我还是在原来基础上加东西,像下面这样
//写个新的接口,用来做删除前的一些其他操作
public interface INewGoods {
void preAction();//删除前操作
void behindAction();//删除后操作
}
//利用Java多实现的特点,多实现个新的接口
public class GoodsImpl implements IGoods,INewGoods{
@Override
public void delete() {
//在这里添加删除前的一些操作...
System.out.println("请求后台删除操作...");
}
@Override
public void preAction() {
//在这里做删除前的一些操作...
}
@Override
public void behindAction() {
//在这里做删除后的一些操作...
}
}
//使用
GoodsImpl goods = new GoodsImpl();
goods.preAction();
goods.delete();
goods.behindAction();
三毛:”嗯,在小项目里或删除商品操作不多的项目里没啥问题,但是在大型复杂项目中,比如淘宝,如果删除商品有30个地方或者更多的时候,你不觉得在每一处删除商品都多写两行代码不累吗,当老板又提多加几个东西时,又多了几行代码呢(代码多了就算复制也不爽吧),如果只写个goods.delete()就可以,那不是更好吗”
小白: 那怎么做呢?
四、代理模式写法
代理模式定义:为其他对象提供一种代理以便控制对这个对象的访问。
三毛:”使用代理模式就可以决解你上面的问题,首先使用静态代理决解你上面的问题”
1、静态代理
//代理模式分为3个部分 -->(1)接口;(2)接口实现类;(3)接口实现类(代理类)
//商品接口(和你上面一样不变)
public interface IGoods {
void delete();
}
//商品实现类(和你上面一样不变)
public class GoodsImpl implements IGoods{
@Override
public void delete() {
System.out.println("请求后台删除操作...");
}
}
//为了决解老板提出之前写好的商品删除不要动它,之后的商品删除前和删除后做一些操作
//我们加多个代理类(可以理解成微商代理),该类也是实现商品接口
public class GoodsProxy implements IGoods {
private IGoods iGoods;
public GoodsProxy(IGoods iGoods) {
this.iGoods = iGoods;
}
@Override
public void delete() {
//在这里做删除前的一些操作...
iGoods.delete();
//在这里做删除后的一些操作...
}
}
//使用(需求修改后的商品删除只需要一句delete,既不影响之前的商品删除,也更加容易扩展了)
GoodsProxy goodsProxy = new GoodsProxy(new GoodsImpl());
goodsProxy.delete();
小白:嗯,确实决解了毛毛哥你上面提到的问题,不过有静态代理了,为嘛还会有动态代理呢
三毛:”这个问题问得很好,静态代理模式需要为每一个需要代理的类写一个代理类,如果需要代理的类有N个,简直MMP,并且我们在使用模式无非为了易于扩展,解耦,但是我们也要优化程序,静态代理就是事先定义好的,就像静态广播事先在配置清单里定义好,不管你有没有都会去加载,但是动态广播就比较灵活,需要的时候才去加载,这样对程序性能肯定会更好,动态代理也是一样的意思,接下来看看怎么写”
1、动态代理
//商品接口(和你上面一样不变)
public interface IGoods {
void delete();
}
//商品实现类(和你上面一样不变)
public class GoodsImpl implements IGoods{
@Override
public void delete() {
System.out.println("请求后台删除操作...");
}
}
//------------------放大招分割线------------------//
//之前的静态代理类究极进化成动态代理类
//静态代理实现的是我们自己定义的接口,只不过动态代理实现的是JDK提供的接口InvocationHandler
public class GoodsProxy implements InvocationHandler {
private IGoods mIGoods;
public GoodsProxy(IGoods mIGoods) {
this.mIGoods = mIGoods;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在这里做删除前的一些操作...
Object invoke = method.invoke(mIGoods, args);
//在这里做删除后的一些操作...
return invoke;
}
}
//使用
GoodsProxy goodsProxy = new GoodsProxy(new GoodsImpl());
IGoods iGoods = (IGoods)Proxy.newProxyInstance(GoodsImpl.class.getClassLoader(),GoodsImpl.class.getInterfaces(), goodsProxy);
iGoods.delete();
//你也可以这样使用,你高兴就好
//GoodsImpl goods = new GoodsImpl();
// GoodsProxy goodsProxy = new GoodsProxy(goods);
// IGoods iGoods = (IGoods) Proxy.newProxyInstance(goods.getClass().getClassLoader(), goods.getClass().getInterfaces(), goodsProxy);
// iGoods.delete();
小白:毛毛哥,刚刚我点进源码看了下Proxy.newProxyInstance(ClassLoader loader,Class‹?›[] interfaces,InvocationHandler h)这个方法,想看看做了什么事情,但是基本看不懂
三毛:“你先告诉我上面的代码执行顺序过程是怎样的,我在给你分析分析newProxyInstance方法做了什么”
小白:我用debug后,得出执行顺序是下面酱紫的
/**
* debug下面的代码:
*
* IGoods iGoods = (IGoods) Proxy.newProxyInstance(GoodsImpl.class.getClassLoader(), GoodsImpl.class.getInterfaces(), goodsProxy);
* iGoods.delete();
*/
执行顺序过程:
(1)得到IGoods对象 iGoosd;
(2)执行iGoosd.delete();
(3)执行GoodsProxy重写的invoke方法;
(4)执行invoke方法里的 method.invoke(Object receiver, Object... args);
(5)执行GoodsImpl类delete()方法;
(6)结束。
三毛:“我的白,你看见这样的执行顺序后有什么想问的嘛 ”
小白:我想知道Proxy.newProxyInstance(…)做了啥事情
三毛:“里面做了如下事情喔”
//Proxy.newProxyInstance做了以下事情:
(1)根据传入的第二第三个参数,动态生成了一个代理类$Proxy0.class文件,如下
public final class $Proxy0 extends Proxy implements IGoods {
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
...
}
(2)根据传入的第一个参数,将上面生成的代理类$Proxy0加载到jvm中;
(3)根据第三个参数,创建一个代理类实例对象,并且用interfaces参数遍历其所有接口的方法,最后把该对象返回给调用者。
小白:奥奥,原来生成的$Proxy0.class文件继承了Proxy和实现了我写的商品接口啊,怪不得我在debug到iGoods.delete()的时候居然调用了GoodsProxy重写的invoke方法,原来如此
小白:那GoodsProxy重写的invoke方法里的method.invoke(…)又做了那些事情捏
三毛:“简单来说就是method.invoke(Object receiver, Object… args)通过反射来调用某一个对象(receiver)的方法(args,方法可以用数组装起来),上面的例子就是通过反射来调用GoodsImpl对象的delete()方法了。想深入了解method.invoke你可以看看这篇文章Method.invoke解释”
小白:毛毛哥,我想消化消化
三毛:“谁缠着要我讲的,,还有一点点,听我BB一下就差不多了”
三毛:”关于使用背景,下面是大话设计模式中的一段话”
什么时候使用代理模式呢?
在对已有的方法进行使用的时候出现需要对原有方法进行改进或修改,这时候有两种改进选择:修改原有方法来适应现在的使用方式,或者使用一个‘第三者’方法来调用原有的方法并且对方法产生的结果进行一定的控制.
三毛:“上面说的第三者其实就是代理模式,代理模式按使用方式大致分为静态代理和动态代理,上面静态和动态代理我们都讲完了,不过那是按使用方式来分,如果按使用范围分,常用的只说4个类型如下,不讲解了”
使用范围分:
远程代理:为摸个对象在不同的内存地址空间提供局部代理,是系统Server部分隐藏,以便Client不用考虑Server的存在。虚拟代理:如果要创建一个资源消耗较大的对象,可以先用一个代理对象表示,在真正需要的时候才真正创建。
保护代理:用代理对象控制对一个对象的访问,给不同的用户提供不同的访问权限。智能引用:在引用原始对象的时候附加额外操作,并对指向原始对象的引用增加引用计数。
五、代理模式和普通写法区别
1、结构清晰,耦合度更低,扩展性更强(xxx模式不都没差吗) )
2、多了代理类,代理对象起到了中介作用,保护目标对象,安全性提高;
3、不要随便使用代理模式。