概述
在有些情况下,客户不能或者不想直接访问目标对象,这时候需要通过代理进行访问目标对象,例如:客户通过火车站代销点购买车票,客户通过“代理”或者“中介”进行海淘,这就是代理模式的原型已经在现实生活的例子
定义和特点
定义:为一个目标对象提供一个代理以提供对这个类的访问
优点:
- 代理在客户和目标对象之间,使客户和目标对象分离,降低了系统的耦合度
- 代理对象可以扩充目标对象的功能
缺点:
- 在客户端和目标对象间增加了一个代理对象,会造成请求速度变慢
- 增加了额外的类,增加了系统的复杂度
实现
(代理模式的UML类图)
如上图所示我们对各个类进行一个说明
- Cliect(客户):用户,使用代理类的入口
- subject (抽象目标类):抽象或者接口类,目标类和代理类的共同接口方法
- RealSubject(真实目标类):实现了抽象目标类,定义了代理所表示的真实对象,负责具体的业务实现
- Proxy(代理类):持有真实目标类的引用,在所实现的接口方法中调用真实目标类中的方法
下面写一个demo
//抽象购物类
public interface Shopping {
Object[] doShopping(long money);
}
/**
* 真实购物类
*/
public class ShoppingImpl implements Shopping {
public static final String TAG = ShoppingImpl.class.getSimpleName();
@Override
public Object[] doShopping(long money) {
Log.e(TAG,"逛淘宝 ,逛商场,买买买!!");
Log.e(TAG,"花了"+money+"元");
Log.e(TAG,"买了 鞋子,衣服,零食");
return new Object[]{"鞋子", "衣服", "零食" };
}
}
/**
* 代理购物类
*/
public class ShoppingProxy implements Shopping {
private Shopping base;
public ShoppingProxy(Shopping shopping){
this.base = shopping;
}
@Override
public Object[] doShopping(long money) {
// 帮忙买东西
Object[] things = base.doShopping(money);
// 偷梁换柱(修改返回值)
if (things != null && things.length > 1) {
things[0] = "被掉包的东西!!";
}
return things;
}
}
/**
* 客户类
*/
private void operate2(){
Shopping shopping = new ShoppingProxy(new ShoppingImpl());
Log.e(TAG,Arrays.toString(shopping.doShopping(100)));
}
如上所以就是一个简单的代理类,这是一个静态代理
动态代理
静态代理模式需要为每一个需要代理的类写一个代理类,如果需要代理的类有几百个那不是要累死?为了更优雅地实现代理模式,JDK提供了动态代理方式
/**
* 动态代理类
*/
public class ShopHandler implements InvocationHandler {
public static final String TAG = ShopHandler.class.getSimpleName();
public Object base;
public ShopHandler(Object base){
this.base = base;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("doShopping".equals(method.getName())) {
// 这里是代理Shopping接口的对象
// 先黑点钱(修改输入参数)
Long money = (Long) args[0];
long readCost = (long) (money * 0.5);
Log.e(TAG,"花了"+readCost+"元");
// 帮忙买东西
Object[] things = (Object[]) method.invoke(base, readCost);
// 偷梁换柱(修改返回值)
if (things != null && things.length > 1) {
things[0] = "被掉包的东西!!";
}
return things;
}
if ("doSomething".equals(method.getName())) {
// 可以代理别的,做些别的事情
return null;
}
if ("doSomethingElse".equals(method.getName())) {
// 做些别的事情
return null;
}
return null;
}
}
/**
* 客户端类
*/
private void operate() {
Shopping shop = new ShoppingImpl();
// 正常购物
Log.e(TAG, Arrays.toString(shop.doShopping(100)));
shop = (Shopping) Proxy.newProxyInstance(Shopping.class.getClassLoader(), shop.getClass().getInterfaces(),new ShopHandler(shop));
Log.e(TAG, Arrays.toString(shop.doShopping(100)));
}
如上所示就实现了动态代理
静态代理与动态代理
- 静态代理:程序在运行前代理类就已经存在了
- 动态代理:代理类在程序运行期间根据反射动态生成
应用场景
当一个对象不能或者不想直接访问另一个对象时,可以通过一个代理对象来间接访问。为保证客户端使用的透明性,委托对象和代理对象要实现同样的接口
被访问的对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容
根据适用范围,代理模式可以分为以下几种:
-
远程代理:为一个对象在不同的地址空间提供局部代表,这样系统可以将Server部分的事项隐藏。
-
虚拟代理:如果要创建一个资源消耗较大的对象,可以先用一个代理对象表示,在真正需要的时候才真正创建。
-
保护代理:用代理对象控制对一个对象的访问,给不同的用户提供不同的访问权限。
-
智能引用:在引用原始对象的时候附加额外操作,并对指向原始对象的引用增加引用计数