❝ 代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。 ❞
在某些情况下,客户端代码不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。由代理对象向客户端提供引用原对象的接口,客户端通过调用代理对象访问原对象。
首先要明白代理对象存在的必要性,如果不存在代理对象会有什么问题?
代理模式在Android中被用到很多。其中根据其目的和实现方式,主要可分为以下几种:
「远程代理(Remote Proxy)」 客户端代码与目标对象不在同一进程、地址空间或主机,客户端无法直接调用目标对象接口。此时可以通过调用代理对象来模拟调用目标对象接口。其中,代理对象与目标对象的通讯可能通过Binder、socket或其他通信方式,无论通过何种方式客户端都无需关心。
「虚拟代理(Virtual Proxy)」 对于一些占用系统资源较多或者加载时间较长的对象,可以先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象。
「保护代理(Protect Proxy)」 给不同的用户提供不同的对象访问权限。
「缓冲代理(Cache Proxy)」 为某一个目标操作的结果提供临时存储空间,以使更多用户可以共享这些结果。
「智能引用代理(Smart Reference Proxy)」 当一个对象被引用时提供一些额外的操作,比如将对象被调用的次数记录下来等。
在嵌入的分层思想中,某些靠近底层的进程享有硬件资源控制,更高层的用户进程则没有此权限。现存在两个进程,假设进程A拥有led资源控制权,进程B不可直接操控Led。如何实现进程B控制Led。
上述场景通过进程间通信搭建A、B进程的通讯,然后通过进程A响应进程B的命令完成对应的操作即可实现。
但是作为追求完美的程序猿,这种方式是最完美的吗?
上述的实现方式,虽然能够满足需求,但是也会存在以下问题:
代理模式能够轻松的缓解上述的问题,方案如下:
在开发工作中,相比于一个人维护,多个开发者交叉对接更不稳定。另外A可以通过增加或删除部分代理对象接口,控制B进程得到使用范围。
享元模式.png
代理模式主要是为客户端提供真实对象的使用入口,至于实现方式有多种,不必拘泥于某种特定的实现方法,达到代理模式的目的即可。
「编程环境」
「工程结构」
Proxy/
├── build
│ └── build.sh
├── Client
│ ├── CMakeLists.txt
│ └── main_client.cc
├── CMakeLists.txt
├── Ipc
│ ├── CMakeLists.txt
│ ├── msg_manager.cc
│ └── msg_manager.h
├── Out
└── Server
├── Api
│ ├── common_type.h
│ ├── led_manager_proxy.cc
│ └── led_manager_proxy.h
├── CMakeLists.txt
├── Led
│ ├── led_manager.cc
│ └── led_manager.h
└── main_server.cc
此处将Ipc、Server/Api、Server/Led分别编译成动态库,其中Client只需链接Api的库就能控制Led。这么做可以避免代码上的耦合。
「代理接口」
class CLedManagerProxy
{
public:
CLedManagerProxy();
~CLedManagerProxy();
void ShowHorseLight(int index);
void ShowBreathLight(int index);
void OpenLight(int index);
void Stop(int index);
};
客户进程可通过上述接口实现对应Led的功能操作。由服务进程维护者提供头文件与库,被客户进程使用。
「客户进程」
// main_client.cc
int main(int argc, char *argv[])
{
char input = 0;
CLedManagerProxy theLedManagerProxy;
print_info();
do {
MAIN_LOG("Input case: ");
input = fgetc(stdin);
getchar();
switch (input)
{
case 'a':
theLedManagerProxy.OpenLight(LED1);
break;
case 'b':
theLedManagerProxy.ShowHorseLight(LED1);
break;
case 'c':
theLedManagerProxy.ShowBreathLight(LED1);
break;
case 'd':
theLedManagerProxy.Stop(LED1);
break;
case 'h':
print_info();
break;
default:
MAIN_LOG("No this case (%c).\n", input);
break;
}
} while(input != 'q');
return 0;
}
上述为客户进程,通过调用代理模式的接口完成对应Led功能的操作。
「服务进程」
int main(int argc, char *argv[])
{
CLedManager::GetInstance()->Init();
return 0;
}
服务进程在实现自身业务外,需实现监听命令的线程,用于相应代理模式的申请。
「真实对象」
class CLedManager
{
public:
CLedManager();
~CLedManager();
static CLedManager* GetInstance();
void Init();
static void SendMsg(int type, void* msg);
void ProcessMsg(ELedMsgType type, void* msg);
void ShowHorseLight(int index);
void ShowBreathLight(int index);
void OpenLight(int index);
void Stop(int index);
};
上述为真实的操作Led的对象,直接控制硬件资源。
「Ipc通信」 由于代理对象在客户进程使用,真实对象在服务进程使用。需要通过进程间通信来实现两者的映射关系。
// 发生请求
void SendMsgEvent(void *pEvent, int size)
{
if (!pEvent) {
LOGE("pEvent is NULL!\n");
return;
}
msg_send(pEvent, size);
}
上述接口用于代理对象发送命令,其实方式可采用管道、消息队列、共享内存或socket等其中一种即可。
// 监听请求
void SetListener(PTypeCallBack pCb)
{
SListenParam param;
if (!pCb) {
LOGE("pCb is NULL!\n");
return;
}
param.cb = pCb;
if (!init_flag) {
init(¶m.fd);
init_flag = 1;
}
LOGD("Create listen_thread!\n");
thread t(listen_thread, ¶m);
t.join();
}
此接口创建了一个线程用于时刻监听代理对象的请求,通过回调函数作出响应。
两个接口配合使用,此处实现方式采用的fifo方式。为了文章排版,不过多贴代码了。可在"开源519"公众号后台输入标题获取源码。
测试.png
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。