代理模式(Proxy Pattern)也称为委托模式,是结构型设计模式的一种。
日常生活中的代理:
- 代理上网,连接代理服务器地址
- 快递外卖,收发快件、帮忙送饭
- 请律师打官司,维护自身权益
- 等等
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。
代理模式的使用场景:当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
代理模式的UML类图
代码:
public abstract class Subject {
// 一个业务方法
public abstract void visit();
}
public class RealSubject extends Subject {
@Override
public void visit() {
// 具体逻辑
}
}
public class ProxySubject extends Subject {
private RealSubject mSubject;
public ProxySubject(RealSubject mSubject) {
this.mSubject = mSubject;
}
@Override
public void visit() {
mSubject.visit();
}
}
public class Client {
public static void main(String[] args) {
// 真实对象
RealSubject realSubject = new RealSubject();
// 通过真实对象构建代理对象
ProxySubject proxySubject = new ProxySubject(realSubject);
// 调用代理的方法,最终是调用真实对象方法
proxySubject.visit();
}
}
代理模式大致分为静态代理和动态代理,上面给出的就是静态代理。
静态代理
代理者的代码由程序员或通过一些自动化工具生成固定的代码再对其进行编译,也就是说代码运行前代理类的class编译文件就已存在。
示例: Android中发送通知,使用NotificationManager的notify方法发送一个通知即可。这里要求在不同版本API下发送通知。
public abstract class Notify {
protected Context context;
protected NotificationManager notifyManager;
protected NotificationCompat.Builder notifyBuilder;
protected int requestCode = 0;
public Notify(Context context) {
this.context = context;
notifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notifyBuilder = new NotificationCompat.Builder(context, "notifyId");
notifyBuilder.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(PendingIntent.getActivity(context, requestCode,
new Intent(context, NotifyActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT));
}
// 发送一条通知
public abstract void send();
// 取消一条通知
public abstract void cancel();
}
/**
* 正常视图,即状态栏中出现的64dp的长条状通知视图
*/
public class NormalNotify extends Notify {
public NormalNotify(Context context) {
super(context);
}
@Override
public void send() {
Notification notification = notifyBuilder.build();
notification.contentView = new RemoteViews(context.getPackageName(),
R.layout.remote_nofity_proxy_normal);
notifyManager.notify(requestCode, notification);
}
@Override
public void cancel() {
notifyManager.cancel(requestCode);
}
}
/**
* 大视图,即在API16引入的高度256dp的大视图
*/
public class BigNotify extends Notify {
public BigNotify(Context context) {
super(context);
}
@Override
public void send() {
Notification notification = notifyBuilder.build();
notification.contentView = new RemoteViews(context.getPackageName(),
R.layout.remote_nofity_proxy_normal);
notification.bigContentView = new RemoteViews(context.getPackageName(),
R.layout.remote_nofity_proxy_big);
notifyManager.notify(requestCode, notification);
}
@Override
public void cancel() {
notifyManager.cancel(requestCode);
}
}
/**
* 顶部浮动视图,即在API20(L)引入的headsUpContentView。
* 当APP以全屏方式时,如果收到通知,视图会悬浮在屏幕顶部。
*/
public class HeadsUpNotify extends Notify {
public HeadsUpNotify(Context context) {
super(context);
}
@Override
public void send() {
Notification notification = notifyBuilder.build();
notification.contentView = new RemoteViews(context.getPackageName(),
R.layout.remote_nofity_proxy_normal);
notification.bigContentView = new RemoteViews(context.getPackageName(),
R.layout.remote_nofity_proxy_big);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notification.headsUpContentView = new RemoteViews(context.getPackageName(),
R.layout.remote_notify_proxy_normal);
}
notifyManager.notify(requestCode, notification);
}
@Override
public void cancel() {
notifyManager.cancel(requestCode);
}
}
public class ProxyNotify extends Notify {
private Notify notify;
public ProxyNotify(Context context) {
super(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notify = new HeadsUpNotify(context);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
notify = new BigNotify(context);
} else {
notify = new NormalNotify(context);
}
}
@Override
public void send() {
notify.send();
}
@Override
public void cancel() {
notify.cancel();
}
}
这样客户端在需要发送通知时,直接调用ProxyNofity中的方法就可以了,如在Activity中使用new ProxyNofity(this).send()。
动态代理
动态代理,通过反射机制动态地生成代理者的对象,也就是说在code阶段不需要知道代理谁,代理谁——将会在执行阶段决定。
Java提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其方法invoke。
public class DynamicProxy implements InvocationHandler {
private Object obj; // 被代理类的引用
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用被代理类对象的方法
Object result = method.invoke(obj, args);
return result;
}
}
上面声明了一个Object的引用,该引用将指向被代理类,而调用被代理类的具体方法则在invoke方法中执行。也就是说原来由代理类所做的工作现在由InvocationHandler来处理,不需要关心到底代理谁。
public class DynamicProxyNotify implements InvocationHandler {
private Context context;
public DynamicProxyNotify(Context context) {
this.context = context;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Notify notify;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notify = new HeadsUpNotify(context);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
notify = new BigNotify(context);
} else {
notify = new NormalNotify(context);
}
return method.invoke(notify, args);
}
}
在Activity中,调用Proxy.newProxyInstance(Notify.class.getClassLoader(), new Class[]{Notify.class}, new DynamicProxyNotify(this))。
结论
静态代理只能为给定接口下的实现类做代理,如果接口不同那么就需要重新定义不同代理类,较为复杂,但是静态代理更符合面向对象原则。
动态代理通过一个代理类来代理N个被代理类,其实质是对代理者与被代理者进行解耦,使二者没有直接的耦合关系。