代理模式
一、代理模式介绍
代理模式(Proxy Pattern)也叫委托模式,是结构型设计模式之一。也是我们经常用到和看到的一个模式。
1、代理模式定义
为其他对象提供一种代理以控制对这个对象的访问。
2、代理模式使用场景
当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
二、模板方法模式UML 类图及 简单实现
UML 类图:
角色介绍:
Subject: 抽象主题类。
其主要的职责就是声明真实主题与代理的共同接口方法,该类既可以是一个抽象类也可以是一个接口。RealSubject: 真正主题类。
该类也称为被委托类或者被代理类,该类定义了代理所表示的真实对象,由其执行具体的业务逻辑,而客户类则通过代理类间接的调用真正主题类中定义的方法。ProxySubject:代理类
该类持有一个真实主题类的引用,在qi所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理的作用。Client:客户类
使用代理。
简单实现
下面以找律师诉讼为例说明代理模式。
- 诉讼接口类:
public interface ILawsuit {
/**
* 提交申请
*/
void submit();
/**
* 进行举证
*/
void burden();
/**
* 开始辩护
*/
void defend();
/**
* 诉讼完成
*/
void finish();
}
- 具体诉讼人
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 Lawyer implements ILawsuit{
private ILawsuit mLawsuit; //持有一个具体被代理者的引用
public Lawyer(ILawsuit lawsuit) {
this.mLawsuit = lawsuit;
}
@Override
public void submit() {
mLawsuit.submit();
}
@Override
public void burden() {
mLawsuit.burden();
}
@Override
public void defend() {
mLawsuit.defend();
}
@Override
public void finish() {
mLawsuit.finish();
}
}
- Client
public class Client {
public static void main(String[] args) {
//构造出诉讼人小民
ILawsuit xiaomin = new XiaoMin();
//构造一个代理律师,并将小民传递进去
ILawsuit lawyer = new Lawyer(xiaomin);
//律师提交申请
lawyer.submit();
//律师进行举证
lawyer.burden();
//律师代小民辩护
lawyer.defend();
//完成诉讼
lawyer.finish();
}
}
同样我们也可以代理其他人,只需要实现ILawsuit即可。上面的代理模式也叫静态代理,也就是在代码运行前代理类的class文件就已经存在。同样的,有静态代理就有动态代理,动态代理主要是通过反射机制动态的生成代理者对象。 Java提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法 invoke。
public class DynamicPorxy implements InvocationHandler{
private Object obj; //被代理类的引用
public DynamicPorxy(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;
}
}
这里我们通过invoke方法来调用具体的被代理方法。
修改后的Client类:
public class Client {
public static void main(String[] args) {
//构造出诉讼人小民
ILawsuit xiaomin = new XiaoMin();
//1.静态代理
//构造一个代理律师,并将小民传递进去
//ILawsuit lawyer = new Lawyer(xiaomin);
//--------------------------------------
//2.动态代理
//构造一个动态代理
DynamicPorxy proxy = new DynamicPorxy(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多个被代理类,其实质是对代理者与被代理者解耦。相对而言静态代理则只能为给定接口下的实现类做代理,如果接口不同那么就需要重新定义不同的代理类,较为复杂,但是静态代理更符合面向对象原则。具体使用哪种方式,根据个人喜好。
静态代理和动态代理是从 Code 方面来区分代理模式的,而从适用范围来说,代理可以分为:
远程代理(remote proxy):为某个对象在不同的内存地址空间提供局部代理。使系统可以将 Server 部分隐藏,以便 Client 可以不必考虑 Server 的存在。
虚拟代理(Virtual Proxy):使用一个代理对象表示一个十分耗资源的对象,并在真正需要时才创建。
保护代理(Protection Proxy):使用代理控制对原始对象的访问。
智能引用(Smart Reference):在访问原始对象时执行一些自己的附件操作并对原始对象的引用计数。
这里需要注意的是,静态代理和动态代理都可以应用于上述四种情况,两者各种独立变化。
三、Android 中的 Binder 跨进程通信机制与 AIDL
使用 Binder 机制来跨进程通信主要涉及到四部分:
- Binder Client
- Binder Server
- Binder Driver
- BinderManager
而 Binder Driver 和 BinderManager Android 系统已经帮我们封装好了。对于 Binder Server ,我们可以通过 AIDL(Android Interface Description Language) 来实现。现在以模拟去银行开户、销户、和存取钱的过程来说明。
- 先定义一个接口:
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);
}
接着我们继承 Binder 创建一个 BankBinder 子类并实现上述接口:
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 + "销户成功!";
}
}
这里不考虑其他逻辑,只是简单的打印一下字符串。
最后继承 Service 组件创建一个 BankService 子类来表示服务端:
- BankService
public class BankService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new Binder();
}
}
在 Service 中重写 onBind 方法并返回了一个 IBinder 对象,这里直接返回上面创建了 BankBinder 实例对象。这里可以把 BankService 看作是 Binder Service。接下来实现客户端。
这里用 Activity 来模拟。
- BankActivity
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.hcworld.aidlstudy.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;
}
}
}
跨进程配置 Service
当我们使用 AIDL 跨进程通信时,需要使用 AIDL 来定义我们的接口来生成对应的编译文件。在 Android Studio 中,Alt + Inset,选择 ALDL 就可以了。
新建 IBankAIDL 文件如下:
// IBankAIDL.aidl
package com.hcworld.aidlstudy;
// Declare any non-default types here with import statements
interface IBankAIDL {
/**
* 开户
* @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);
}
定义好了 AIDL 接口之后,可以通过 Make Project 来生成对应的 Java 文件。生成的文件在 build/source/aidl/debug 下。
现在接口已经可以使用了。修改 BankBinder 继承与 AIDL 文件中的 Stub.
public class BankBinder extends IBankAIDL.Stub {
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 + "销户成功!";
}
}
客户端:
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(""),conn,BIND_AUTO_CREATE);
final Intent intent = new Intent();
intent.setAction("com.hcworld.aidlstudy.BankService.BankService");
final Intent eintent = new Intent(createExplicitFromImplicitIntent(this,intent));
bindService(eintent,conn, Service.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 = 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) {
}
break;
}
}
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
四、总结
代理模式应用广泛,但是可能会导致代码的冗余,比如每个具体的被代理对象中都有相似的逻辑代码,这部分可以用装饰模式简化一下。