您的位置:首页 > 其它

代理模式

2016-05-30 22:05 260 查看

一.介绍

对于程序员来说最常接触的代理模式就是代理上网,让别人买饭也是代理,如果你碰到辞职老板不给你发工资,那么要请个律师打官司,这也是代理

 

 

二.代理模式的定义

为其他对象提供一种代理以控制对这个对象的访问。(也就是在对象A中的方法a中调用对象B的方法a)

 

 

三.代理模式的使用场景

当无法或不想直接访问某个对象或访问某个对象存在苦难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性(什么是透明性???),委托对象与代理对象需要实现相同的接口

 

四.简单实现

4.1简单实现1

//Subject.java

</pre><pre name="code" class="java">public abstract class Subject {

/**
* 一个普通的业务方法
* */
public abstract  void visit();
}


//RealSubject.java

public class RealSubject extends Subject {
@Override
public void visit() {
//RealSubject中visit的具体逻辑
System.out.println("Real Subject");
}
}


//ProxySubject.java

public class ProxySubject extends Subject {
private RealSubject mSubject;//持有真实主题的引用

public ProxySubject(RealSubject mSubject) {
this.mSubject = mSubject;
}

@Override
public void visit() {
//通过真实主题引用的对象调用真实主题中的逻辑方法
//感觉有点像装饰者模式
mSubject.visit();
}
}


//相当于client

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//构造一个真实主题对象
RealSubject real = new RealSubject();

//通过真实主题对象构造一个代理对象
ProxySubject proxy = new ProxySubject(real);

//调用代理的相关方法
proxy.visit();
}


Subject:抽象主题类
该类的主要职责是声明真实主题与代理的共同接口方法,该类既可以是一个抽象类也可以是一个接口。
 
RealSubject:真实主题类
该类也称为 被委托类 或者代理类,该类定义了代理所表示的真实对象,由其执行的具体的业务逻辑方法,而客户类则通过代理类间接地调用真实主题类中定义的方法。
 
ProxySubject:代理类
该类也称为 委托类 或  代理类,该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理的作用
 
 
 
 
 
 
 

4.2简单实现2

需求:小明离职了但是被拖欠工资,小明请了律师来作为自己的诉讼代理人
 
 
public class XiaoMin implements   ILawsuit {
@Override
public void submit() {
//老板欠小民工资 小民只好申请仲裁
System.out.println("老板拖欠工资!申请仲裁");
}

@Override
public void burden() {
//小明证据充足,不怕告不赢
System.out.println("这是合同书和过去一年的银行工资流水");
}

@Override
public void defend() {
//铁证如山,辩护也没什么好说的
System.out.println("证据确凿!不用再说了");

}

@Override
public void finish() {
//结果也是肯定的,必赢
System.out.println("诉讼成功!判决老板即日起七天内结算工资");
}
}


public class Lawer implements ILawsuit {
private ILawsuit mLawsuit;//持有一个具体的被代理者的引用

public Lawer(ILawsuit mLawsuit) {
this.mLawsuit = mLawsuit;
}

@Override
public void submit() {
mLawsuit.submit();
}

@Override
public void burden() {
mLawsuit.burden();
}

@Override
public void defend() {
mLawsuit.defend();
}

@Override
public void finish() {
mLawsuit.finish();
}
}


//构造一个小民
ILawsuit xiaomin = new XiaoMin();
//构造一个代理律师并将小民作为构造参数传递进去
ILawsuit lawyer = new Lawer(xiaomin);
//律师提交诉讼申请
lawyer.submit();
//律师代替小民进行辩护
lawyer.defend();
//完成诉讼
lawyer.finish();


 
虽然实现了需求,但是其实我们的代理类完全可以代理多个被代理类,就像上面的例子一样,一个律师可以代理多个人打官司,这是没有任何问题,而具体是到底是代理的那个人,这就要看代理类中所持有的实际对象类型。
可以按下面这么干(这种属于静态代理)。

Public class Lawyer implements ILawsui{
Private ILawsuit mLawsuit;//持有一个具体被代理者的引用
Public Lawyer(ILawsuit lawsuit){
this.mLawsuit = lawsuit;
}

}

Public class Client{
Public static void main(String[] args){
//构造一个小民。。。
ILawsuit xiaomin = new XiaoMIn();
//构造一个代理律师并将小明作为构造参数传递进去
ILawsuit Lawyer = new Lawyer(xiaomin);
//.....省略
}

}


4.2.1代理模式分为两种:

1.静态代理(如上)
 
2.动态代理
通过反射机制动态地生成代理者的对象,也就是说我们在code阶段根本就不知道代理谁,代理谁是在执行阶段决定
 
Java也给我们提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法invoke

//动态代理类

/**
* Created by Administrator on 2016/5/29 0029.
*/
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;
}
}


//动态代理
//构造一个小民
ILawsuit xiaomin = new XiaoMin();
//构造一个动态代理
DynamicProxy proxy = new DynamicProxy(xiaomin);
//获取被代理类小民的ClassLoader
ClassLoader loader = xiaomin.getClass().getClassLoader();
//动态构造一个代理律师
ILawsuit lawyer = (ILawsuit)Proxy.newProxyInstance(loader,new Class[]{ILawsuit.class},proxy);
//律师提交诉讼申请
lawyer.submit();
//律师进行举证
lawyer.burden();
//律师代替小民进行辩护
lawyer.defend();
//完成诉讼
lawyer.finish();


这样就可以做到一个代理类代理N多个被代理类,其实质是对象代理者与被代理者进行解耦,使两者没有直接的耦合关系。
相对静态代理则只能为给定接口下的实现类做代理,如果接口不同那么就需要重新定义不同代理类(缺点),较为复杂,但是静态代理更符合面向对象原则。
开发时具体使用哪种方式实现代理,就看自己的偏好了。

五.Android中Binder跨进程通信机制与AIDL

 

5.1基本介绍

传统的跨进程通信方式有很多,比如Socket,信号量,管道,内存共享,消息队列等,Android是基于Linux的,为啥要搞一个Binder出来呢???
 
Socket:开销大且效率不高
管道和队列:拷贝次数,更重要的是传统的通信机制安全性低,大部分情况下接收方无法得到发送方进程的可信的PID/UID,难以校验身份。

5.2基本原理(看图说话)

方法1:



 

方法2

 


 

5.3说明AIDL的作用

举个例子
需求:用代码模拟去银行开户,销户,存取钱的过程,首先我们创建一个接口类来定义开户,销户和存取钱的方法。

//BankBinder.java

**
* Created by Administrator on 2016/5/29 0029.
*/
public class BankBinder extends Binder implements IBank {

public String openAccount(String name, String passWord) {

return name+"开户成功!账号为:"+ UUID.randomUUID().toString();
}

@Override
public String saveMoney(int money, String account) {
return "账户:"+account+"存入"+money+"单位:人民币";
}

@Override
public String takeMoney(int money, String account, String password) {
return "账户:"+account+"支取"+money+" 单位 :人民币";
}

@Override
public String closeAccount(String account, String password) {
return account + "销户成功!";
}
}


//BankService.java

/**
* Created by Administrator on 2016/5/29 0029.
*/
public class BankService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new Binder();
}
}


//IBank.java

/**
* Created by Administrator on 2016/5/29 0029.
*/
public interface IBank {
/**
* 开户
* @param name 户主名字
* @param password 密码
* @return  开户信息
* */
String openAccount(String name ,String passWord);

/**
* 存钱
* @param money 金额
* @param account 账户
* @return 存钱信息
* */
String saveMoney(int money ,String account);

/**
* 取钱
* @param money 金额
* @param account 账户
* @param password 密码
* @return 取钱信息
* */
String takeMoney(int money ,String account,String password);

/**
* 销户
* @param account 账户
* @param password 密码
* @return 取钱信息
* */
String closeAccount(String account ,String password);

}


//MainActivity.java

public class MainActivity extends Activity implements View.OnClickListener {
//    private BankBinder mBankBinder; //没有使用AIDL
private IBankAIDL mBankBinder; //使用AIDL
private TextView tvMsg;//显示处理结果的文本控件

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//绑定Service
bindService(new Intent("com.example.administrator.proxytext0529.BankService"),conn,BIND_AUTO_CREATE);

//初始化文本控件
tvMsg = (TextView) findViewById(R.id.tv_msg);

init(R.id.aidl_bank_open_btn);
init(R.id.aidl_bank_save_btn);
init(R.id.aidl_bank_take_btn);
init(R.id.aidl_bank_close_btn);
}

private void init(int ID) {
Button b = (Button) findViewById(ID);
b.setOnClickListener(this);
}

private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//没有使用AIDL
//            mBankBinder = (BankBinder) service;
//使用AIDL
mBankBinder = (BankBinder) IBankAIDL.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}

@Override
public void onClick(View v) {

switch (v.getId()){
case R.id.aidl_bank_open_btn:
try {
tvMsg.setText(mBankBinder.openAccount("aige","123443"));
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.aidl_bank_save_btn:
try {
tvMsg.setText(mBankBinder.saveMoney(6666,"asdasd12321"));
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.aidl_bank_take_btn:
try {
tvMsg.setText(mBankBinder.takeMoney(250,"asd123","123443"));
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.aidl_bank_close_btn:
try {
tvMsg.setText(mBankBinder.closeAccount("asdasd12123","123123"));
} catch (RemoteException e) {
d
}
break;

}
}
}


这时候BankService.java在主进程中,如果BankService在其他进程中怎么破???
那就要用到AIDL,否则跨进程使用会报类型转换异常
这么改造一下就运行在另外一个进程中了

<service
android:name=".BankService"
android:process="cn.dx.myprocess">
<intent-filter>
<action android:name="com.example.administrator.proxytext0529.BankService"/>
</intent-filter>
</service>

结局方法就是使用AIDL,在MainActivity.java中已经写了

5.4简单实践

需求背景:
Notifycation简单,但是不同版本系统,需要做适配
1.正常视图,高度64dp的长条状通知视图
2.在API16中引入的Style方式展示的MediaStyle,InboxStyle,BigTextStyle和BigPiture四种Nocification风格样式
3.也是在API16中引入的可以将通知视图显示为256dp高度大视图的bigContentView
4.最后一类则是在L中加入的headsUpContentView(浮动于屏幕顶部类似于Toast,就像收到红包一样)
 
注意:Android4.0也就是API16以上的系统中,通知列表值允许位于最顶端的一个BigContentVIew展示,而其他的Notification均会以普通ContentView的形式展开,所以我们只需要增加,不用删除
最后的目的就是在不同的系统版本采用不同的通知,代理我们选择(感觉有点像策略模式和装饰者模式)

//Notify.java

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)
.setContent(PendingIntent.getActivity(context,NotifyActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT);
}

/**
* 发送一条通知
* */
public abstract void send();

/**
* 取消一条通知
* */
public abstract void cancel();

}


//NotifyNormal.java
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);
}
}

//NotifyBig.java
public class NotifyBig extends Notify {

public NotifyBig(Context context) {
super(context);
}

@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);

}
}

//NotifyHeadsUP.java
public class NotifyHeadsUp extends Notify {

public NotifyHeadsUp(Context context) {
super(context);
}

@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);
n.headsUpContentView = new RemoteViews(context.getPackageName(),
R.layout.remote_notify_proxy_normal);
nm.notify(0, n);
}

@Override
public void cancel() {
nm.cancel(0);
}
}


//NotifyProxy.java(代理类)
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 NotifyHeadsUp(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();
}
}

(还有一个小三的代理例子待添加???)

目的达到了,但是每次都重新写了上一个的(contentview,bigview之类的),可以用装饰模式简化一下
 
缺点:就是容易增加类(大部分设计模式都会这样)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: