设计模式解析之代理模式
本文关键词:设计模式解析,由笔耕文化传播整理发布。
设计模式解析之代理模式
设计模式-代理模式代理模式的概念 代理模式(proxy pattern)是一种结构型的设计模式,代理模式在程序开发中的运用非常广泛。简单地描述代理模式就是:为其他对象(被代理对象)提供一种代理以控制对原有对象的操作。实际的行为是由被代理对象完成的。
代理模式可以分为两部分,静态代理 和 动态代理,它们的区别将在下面详细介绍。
角色介绍: Suject: 抽象主题类
该类的主要职责是申明真是主题与代理的共同接口方法,该类既可以是个抽象类也可以是个接口(具有抽象方法)。
RealSubject: 真实主题类
该类也称为委托类或者被代理类,改类定义了代理所表示的真是对象(也就是实现了抽象方法),由其执行具体的业务逻辑。
ProxySubject:代理类
这个类的对象持有一个对真实主题的引用,在这个类所实现的接口方法中调用真实主题类中相应的方法执行,这样就实现了代理的目的。
Client:客户类
也就是使用代理类的类型,客户类通过代理类间接地调用了真实主题类中定义的方法。
代理模式的实现简单的例子针对,上方的角色介绍,举一个简单的例子:在现实的世界中,打公司一般有,原告 和原告的代理律师这样两个角色,他们要做的事情是 辩护。于是,我们可以实现以下几个类。
抽象主题类:interface IDefender,这个接口中有个 抽象方法 abstract public void defend(); 表示辩护这个行为。
[code]public interface IDefender {
abstract public void defend();//辩护行为
}
真实主题类: class Accuser,实现IDefender这个接口,具有具体的逻辑行为
[code] public class Accuser implements IDefender {
@Override
public void defend() {
System.out.println("被告严重侵犯了公民的人身自由权...哔哩哔哩");
}
}
代理类:AccuserProxy 类,原告请了个代理律师帮助它打官司,也就是AccuserProxy,由代理律师控制原告的表现
[code]public class AccuserProxy implements IDefender {
//持有被代理类的引用
private IDefender iDefend;
public AccuserProxy(IDefender iDefend) {
this.iDefend = iDefend;
}
@Override
public void defend() {
beforeDefend();
// 被代理类的行为
iDefend.defend();
afterDefend();
}
//修饰的方法
private void beforeDefend(){
System.out.print("我是原告律师,以下是我方辩词");
}
//修饰的方法
private void afterDefend(){
System.out.print("辩护完毕");
}
}
客户端:Client类,使用代理类的角色
[code]public class Client {
public static void main(String[] args) {
Accuser accuser = new Accuser();
accuser.defend();//此时输出 被告严重侵犯了公民的人身自由权...哔哩哔哩
AccuserProxy accuserProxy = new AccuserProxy(accuser);
accuserProxy.defend();
// 输出:我是原告律师,以下是我方辩词,被告严重侵犯了公民的人身自由权...哔哩哔哩,辩护完毕
/////////////////////////////////////////////////////
// 因为,我们的代理类和被代理类 实现的是同一接口,如果将引用类型 写成IDfender,
// 那么在调用 defend(),方法的使用 客户完全感觉不到被代理类的存在,当然因为我们这里的
// 被代理类是通过构造函数传进去的,软件开发中,有时候直接在被代理类中实例化代理类,这样使用起来就更完美了。
IDefender accuser2 = new Accuser();
IDefender accuser2Proxy = new AccuserProxy(accuser2);
accuser2.defend();
}
}
代理模式的运用符合开闭原则的定义,软件中的对象(类、模块、函数)应该对于拓展是开发的,对于修改是封闭的。我们不需要修改被代理类 (Accuser),使用代理模式就可以实现对原有功能的加强。以上代码只是一个简答的运用场景,现实开发中代理模式的运用非常广泛,它可以解决多方面的问题。
Androd 开发中的运用例子 Android API的版本迭代很快,在每次的版本更新中 通常会加强一些原有的功能,对原有的类会增加新的接口,而每个版本能够调用的API 可能会都不同,比如 状态栏 Notification 。
Notfification可以分为4类,一类是正常视图,也就是我们通常在状态栏看到的 高度为 64dp 的长条状通知视图;一类是在 API16 中引入的以 Style 方式展示的 MediaStyle、InboxStyle、BigTextStyle 和 BigPictureStyle 四种Notification 风格样式;一类也是在 API16 中引入的可以将通知视图显示为256dp 高度大视图的 bingContentView;最后一类是在 L 中引入的 headsUpContentVIew
现在,我们的 App需要根据不同的版本实例化不同 Notification 显示不同的视图,在高版本显示的视图当然就更丰富,低版本更单调。如果你直接在 Activity 中判断版本,加上一坨的 switch 或者 if else 语句当然也是可以的。但是,我们现在来看一下 运用设计模式的思想,如何将代码抽离,对于客户端来说(Activity or Fragment),不应该关心这些细节。
以下的代码来自《Android源码设计模式解析与实战》一书。
抽象主题类: Notify类, Notify抽象类 声明了 NotificationManager 和NotificatoinCompat.Builder 2个成员变量来处理和通知的一些逻辑。在构造函数中初始化所有子类都会调用的逻辑方法。
[code]public abstract class Notify {
protected Context context;
protected NotificationManager nm;
protected NotificationCompat.Builder builder;
public Notify(Context context){
this.context = context;
nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(PendingIntent.getActivities(
context,
0,
new Intent[]{new Intent(context, NotifyActivity.class)},
PendingIntent.FLAG_UPDATE_CURRENT));
}
/**
* 发送一条通知
*/
public abstract void send();
/**
* 取消一条通知
*/
public abstract void cancel();
}
真实主题类:NotifyNormal ,继承抽象主题类,简单重写抽象方法
[code]public class NotifyNormal extends Notify{
public NotifyNormal(Context context) {
super(context);
}
@Override
public void send() {
Notification n = builder.build();
n.contentView = new RemoteViews(context.getPackageName(),
R.layout.remote_notify_proxy_normal);
nm.notify(0,n);
}
@Override
public void cancel() {
nm.cancel(0);
}
}
真实主题类 : 与NormalNotify的不同在于 还实现了bigContentView 的初始化,,这是在 API 16以上才能调用的
[code]public class NotifyBig extends Notify{
public NotifyBig(Context context) {
super(context);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void send() {
Notification n = builder.build();
n.contentView = new RemoteViews(context.getPackageName(), R.layout.remote_notify_proxy_normal);
n.bigContentView = new RemoteViews(context.getPackageName(),R.layout.remote_notify_proxy_big);
nm.notify(0,n);
}
@Override
public void cancel() {
nm.cancel(0);
}
}
源码中还有个 NotifyHeadUp 类,它的唯一区别就是 在bigContentVie 的基础上还能实现 Notification 的 headsUpContentView ,这个 View 是在 API 20以上 才能使用的,当我们的 App 以全屏的方式展开的时候如果收到了通知,这个 View 就会浮动展示于屏幕顶部。
代理类: NotifyProxy ,持有被代理类对象,在这个示例中,我们根据不同的运行时环境 API 实例化不同的 被代理类对象。
[code]public class NotifyProxy extends Notify{
//代理类持有被代理类的引用
private Notify notify;
public NotifyProxy(Context context) {
super(context);
//根据版本实例化不同的被代理对象
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
notify = new NotifyHeadUp(context);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
notify = new NotifyBig(context);
} else {
notify = new NotifyNormal(context);
}
}
@Override
public void send() {
notify.send();
}
@Override
public void cancel() {
notify.cancel();
}
}
客户端类:也就是我们的Activity,在 Activity 中,我们直接将 Context 传入,代理类会帮我们实例化合适的被代理对象。
[code]new NotifyProxy(NotifyActivity.this).send();
在这个实例中,我们通过代理模式 ,使用一个代理类来针对不同的运行时系统版本,实例化不同的 Notificaition 的子类,而在客户端中 简单地调用代理类的send方法就可以。
静态代理模式总结 代理模式通过代理类对外部提供统一的接口,在代理类中实现对被代理类的附加操作,从而可以在不影响外部调用的情况下实现系统的拓展,我觉得代理模式可能在一个程序项目的开发初期运用不到,而在项目成型而又有了新的变化、升级等,可以考虑用代理模式来实现,这样可以不需要修改原有的代码。
动态代理模式 其实上文所讲述的内容只是代理模式的一部分,代理模式还有更为强大的动态代理模式。以下是这 2 个的区别:
静态代理模式:在我们的代码运行前,代理类的class编译文件就已经存在了
动态代理模式:在 code 阶段并不存在被代理类,而且并不知道要代理哪个对象,利用 Java 的反射机制在运行期动态地生成代理者的对象,代理谁将会在代码执行阶段决定。
动态代理模式的实现 Java 已经为我们提供了一个便捷的动态代理接口 InvocationHandler ,我们重写其调用方法 invoke。以前面 我们的代理律师的例子为例,看看具体的操作是怎么样的
[code]public class DynamicProxy implements InvocationHandler{
private Object object;// 被代理类的类引用
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeDefend();
//调用被代理类对象的方法
Object result = method.invoke(object, args);
afterDefend();
return result;
}
private void beforeDefend(){
System.out.print("我是原告律师,以下是我方辩词");
}
private void afterDefend(){
System.out.print("辩护完毕");
}
}
解释一下这个 invoke 方法,我们通过 invoke 方法来调用具体的被代理方法,也就是真实的方法,如果对反射机制了解的话, method.invoke(object,args) 这句代码应该很好理解,object 是被代理类,method 是被代理类的方法,args 是方法的参数。
我们仅仅是实现了 InvocationHandler 的接口,那么接下来该做什么呢?怎么实现动态生成代理者对象呢?Java 的 java.lang.reflect 包下 还有一个 Proxy 类,它有个静态方法 newProxyInstance(),我们使用这个方法生成。以前面 我们的代理律师的例子为例,
[code]public class Client {
public static void main(String[] args) {
//被代理类
IDefender accuser = new Accuser();
accuser.defend();
DynamicProxy proxy = new DynamicProxy(accuser);
ClassLoader loader = accuser.getClass().getClassLoader();
IDefender proxyIDefender = (IDefender) Proxy.newProxyInstance(loader, new Class[]{IDefender.class}, proxy);
proxyIDefender.defend();
}
}
这个客户端的输出结果,和之前是一样的,可以发现,我们将之前代理类的工作,转换到 InvocationHandler 的 invoke() 方法去执行,不再需要关心到底需要代理谁。
动态代理在 Retrofit 框架中的运用 Retrofit 是 Android 上流行的 Http Client请求库先看以下一段代码
[code]interface GitHubService {
@GET("/repos/{owner}/{repo}/contributors")
List<Contributor> repoContributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
[code]Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
//代理模式的运用
GitHubService service = retrofit.create(GitHubService.class);
[code]Call
本文关键词:设计模式解析,由笔耕文化传播整理发布。
本文编号:236203
本文链接:https://www.wllwen.com/wenshubaike/mishujinen/236203.html