S16. 代理模式

2022-03-27  本文已影响0人  拂去尘世尘

代理模式

代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

在某些情况下,客户端代码不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。由代理对象向客户端提供引用原对象的接口,客户端通过调用代理对象访问原对象。

意义

首先要明白代理对象存在的必要性,如果不存在代理对象会有什么问题?

代理模式在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

代理模式主要是为客户端提供真实对象的使用入口,至于实现方式有多种,不必拘泥于某种特定的实现方法,达到代理模式的目的即可。

源码实现

编程环境

  1. 编译环境: Linux环境
  2. 语言: C++语言
  3. 编译命令: ./build.sh

工程结构

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(&param.fd);
        init_flag = 1;
    }

    LOGD("Create listen_thread!\n");
    thread t(listen_thread, &param);
    t.join();
}

此接口创建了一个线程用于时刻监听代理对象的请求,通过回调函数作出响应。

两个接口配合使用,此处实现方式采用的fifo方式。为了文章排版,不过多贴代码了。可在后台输入标题获取源码。


测试效果

[图片上传失败...(image-fb3354-1648382614818)]

总结

上一篇下一篇

猜你喜欢

热点阅读