结构型设计模式之代理模式:
一、含义
代理模式也叫做委托模式,其定义如下:
为其他对象提供一种代理以控制对这个对象的访问。
二、代码说明
1.主要有两个角色
1)具体主题角色
也叫做委托角色、被代理角色。它是业务逻辑的具体执行者。
2)代理主题角色
也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
一个代理类可以代理多个被委托者或被代理者。
2.在用C实现过程中也是参考这种思想,以游戏代理场景举例,具体实现如下:
1)代理模式使用场景:
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : ProxyPatternUsage.c 5 * Description : 代理模式的使用 6 7 book@book-desktop:/work/projects/test/DesignPatterns/ProxyPattern$ gcc -o ProxyPatternUsage GamePlayer.c ProxyPattern.c ProxyPatternUsage.c 8 book@book-desktop:/work/projects/test/DesignPatterns/ProxyPattern$ ./ProxyPatternUsage 9 -----------------开始打游戏----------------10 登录名为ZhangSan的用户:张三,登录成功!11 张三 在打怪!12 张三又升了一级!13 14 * Created : 2017.07.21.15 * Author : Yu Weifeng16 * Function List : 17 * Last Modified : 18 * History : 19 ******************************************************************************/20 #include"stdio.h"21 #include"malloc.h"22 #include"stdlib.h"23 #include"string.h"24 #include"ProxyPattern.h"25 26 27 28 29 /*****************************************************************************30 -Fuction : main31 -Description : 32 -Input : 33 -Output : 34 -Return : 35 * Modify Date Version Author Modification36 * -----------------------------------------------37 * 2017/07/19 V1.0.0 Yu Weifeng Created38 ******************************************************************************/39 int main(int argc,char **argv)40 {41 g_tGamePlayer.SetName("张三");//设置玩家42 g_tGamePlayerProxy.SetGamePlayer(g_tGamePlayer.tIGamePlayer);//设置代理者(表示代理谁)43 44 printf("-----------------开始打游戏----------------\r\n");45 g_tGamePlayerProxy.tIGamePlayer.Login("ZhangSan","Password");46 g_tGamePlayerProxy.tIGamePlayer.KillBoss();47 g_tGamePlayerProxy.tIGamePlayer.Upgrade();48 49 return 0;50 }
2)被调用者:
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : ProxyPattern.c 5 * Description : 代理模式 6 本文件是代理角色 7 以游戏代理举例 8 9 * Created : 2017.07.21.10 * Author : Yu Weifeng11 * Function List : 12 * Last Modified : 13 * History : 14 ******************************************************************************/15 #include"stdio.h"16 #include"malloc.h"17 #include"stdlib.h"18 #include"string.h"19 #include"ProxyPattern.h"20 21 static void SetGamePlayer(T_IGamePlayer i_tGamePlayer);22 static void ProxyLogin(char *i_strUserName,char *i_strPassword);23 static void ProxyKillBoss();24 static void ProxyUpgrade();25 26 static T_IGamePlayer g_tIGamePlayer;27 28 const T_GamePlayerProxy g_tGamePlayerProxy={29 .SetGamePlayer =SetGamePlayer,30 .tIGamePlayer ={ProxyLogin,ProxyKillBoss,ProxyUpgrade},31 };32 /*****************************************************************************33 -Fuction : SetGamePlayer34 -Description : 公有函数35 -Input : 36 -Output : 37 -Return : 38 * Modify Date Version Author Modification39 * -----------------------------------------------40 * 2017/07/21 V1.0.0 Yu Weifeng Created41 ******************************************************************************/42 static void SetGamePlayer(T_IGamePlayer i_tGamePlayer)43 { //具体T_GamePlayer为只读,所以不能传址,使用传值形式,即拷贝了副本44 memcpy(&g_tIGamePlayer,&i_tGamePlayer,sizeof(T_IGamePlayer));//这里就可以取址了45 }46 47 /*****************************************************************************48 -Fuction : ProxyLogin49 -Description : 公有函数50 -Input : 51 -Output : 52 -Return : 53 * Modify Date Version Author Modification54 * -----------------------------------------------55 * 2017/07/21 V1.0.0 Yu Weifeng Created56 ******************************************************************************/57 static void ProxyLogin(char *i_strUserName,char *i_strPassword)58 {59 g_tIGamePlayer.Login(i_strUserName,i_strPassword);60 }61 62 /*****************************************************************************63 -Fuction : ProxyKillBoss64 -Description : 公有函数65 -Input : 66 -Output : 67 -Return : 68 * Modify Date Version Author Modification69 * -----------------------------------------------70 * 2017/07/21 V1.0.0 Yu Weifeng Created71 ******************************************************************************/72 static void ProxyKillBoss()73 {74 g_tIGamePlayer.KillBoss();75 }76 77 /*****************************************************************************78 -Fuction : ProxyUpgrade79 -Description : 公有函数80 -Input : 81 -Output : 82 -Return : 83 * Modify Date Version Author Modification84 * -----------------------------------------------85 * 2017/07/21 V1.0.0 Yu Weifeng Created86 ******************************************************************************/87 static void ProxyUpgrade()88 {89 g_tIGamePlayer.Upgrade();90 }
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : ProxyPattern.h 5 * Description : 代理模式 6 7 * Created : 2017.07.21. 8 * Author : Yu Weifeng 9 * Function List : 10 * Last Modified : 11 * History : 12 ******************************************************************************/13 #ifndef PROXY_PATTERN_H14 #define PROXY_PATTERN_H15 16 17 typedef struct IGamePlayer18 {19 void (*Login)(char *i_strUserName,char *i_strPassword);20 void (*KillBoss)();21 void (*Upgrade)();22 }T_IGamePlayer;23 24 typedef struct GamePlayer25 {26 T_IGamePlayer tIGamePlayer;//实现同一个接口27 void (*SetName)(char *i_strName);//私有变量放内部不放外部的原因是28 //放外部,则要修改接口,29 }T_GamePlayer;30 31 typedef struct GamePlayerProxy32 {33 T_IGamePlayer tIGamePlayer;//实现同一个接口34 void (*SetGamePlayer)(T_IGamePlayer i_tGamePlayer);//私有变量放内部不放外部的原因是35 //放外部,则要修改接口,导致接口内方法参数增加以及变得复杂36 }T_GamePlayerProxy;37 38 39 const T_GamePlayerProxy g_tGamePlayerProxy;//私有变量放内部,单例模式40 //如果考虑扩展,可以再以T_GamePlayerProxy来构造,形成有上限的多例模式(单例模式的扩展,因为实例都是已经构造好的,固定的)41 //没有提供new方法,外部不能想new几个就new几个,只能使用固定的那一个(或几个)实例(已经构造好的),因此是单例模式(或是其扩展)42 const T_GamePlayer g_tGamePlayer;//私有变量放内部,单例模式43 44 #endif
1 /***************************************************************************** 2 * Copyright (C) 2017-2018 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : GamePlayer.c 5 * Description : 代理模式 6 本文件是游戏角色 7 8 * Created : 2017.07.21. 9 * Author : Yu Weifeng10 * Function List : 11 * Last Modified : 12 * History : 13 ******************************************************************************/14 #include"stdio.h"15 #include"malloc.h"16 #include"stdlib.h"17 #include"string.h"18 #include"ProxyPattern.h"19 20 static void SetGamePlayerName(char *i_strName);21 static void GamePlayerLogin(char *i_strUserName,char *i_strPassword);22 static void GamePlayerKillBoss();23 static void GamePlayerUpgrade();24 static char *g_strGamePlayerName=NULL;25 26 27 const T_GamePlayer g_tGamePlayer={28 .SetName =SetGamePlayerName,29 .tIGamePlayer ={GamePlayerLogin,GamePlayerKillBoss,GamePlayerUpgrade},30 };31 /*****************************************************************************32 -Fuction : SetGamePlayerName33 -Description : 公有函数34 -Input : 35 -Output : 36 -Return : 37 * Modify Date Version Author Modification38 * -----------------------------------------------39 * 2017/07/21 V1.0.0 Yu Weifeng Created40 ******************************************************************************/41 static void SetGamePlayerName(char *i_strName)42 {43 g_strGamePlayerName=i_strName;44 }45 46 /*****************************************************************************47 -Fuction : GamePlayerLogin48 -Description : 公有函数49 -Input : 50 -Output : 51 -Return : 52 * Modify Date Version Author Modification53 * -----------------------------------------------54 * 2017/07/21 V1.0.0 Yu Weifeng Created55 ******************************************************************************/56 static void GamePlayerLogin(char *i_strUserName,char *i_strPassword)57 {58 printf("登录名为%s的用户:%s,登录成功!\r\n",i_strUserName,g_strGamePlayerName);59 }60 61 /*****************************************************************************62 -Fuction : GamePlayerKillBoss63 -Description : 公有函数64 -Input : 65 -Output : 66 -Return : 67 * Modify Date Version Author Modification68 * -----------------------------------------------69 * 2017/07/21 V1.0.0 Yu Weifeng Created70 ******************************************************************************/71 static void GamePlayerKillBoss()72 {73 printf("%s 在打怪!\r\n",g_strGamePlayerName);74 }75 76 /*****************************************************************************77 -Fuction : GamePlayerUpgrade78 -Description : 公有函数79 -Input : 80 -Output : 81 -Return : 82 * Modify Date Version Author Modification83 * -----------------------------------------------84 * 2017/07/21 V1.0.0 Yu Weifeng Created85 ******************************************************************************/86 static void GamePlayerUpgrade()87 {88 printf("%s又升了一级!\r\n",g_strGamePlayerName);89 }
3)执行结果:
book@book-desktop:/work/projects/test/DesignPatterns/ProxyPattern$ gcc -o ProxyPatternUsage
GamePlayer.c ProxyPattern.c ProxyPatternUsage.c
book@book-desktop:/work/projects/test/DesignPatterns/ProxyPattern$ ./ProxyPatternUsage
-----------------开始打游戏----------------
登录名为ZhangSan的用户:张三,登录成功!
张三 在打怪!
张三又升了一级!
4)详细代码:
https://github.com/fengweiyu/DesignThinking/tree/master/DesignPatterns/StructuralDesignPatterns/ProxyPattern
三、使用场景
1.不想参与中间过程的是是非非(具体由代理者处理),同时减轻负担
四、优点
1.职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事物,附带的结果就是编程简洁清晰。
2.高扩展性
具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都是在接口之下,所以代理类完全就可以在不做任何修改的情况下使用
3.智能化
这在上诉的讲解中还没体现出来,主要在动态代理上得以体现
五、代理模式的扩展
1.普通代理和强制代理
1)普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问
即普通代理的要求就是客户端只能访问代理角色,而不能访问真实角色
普通代理的约束:真实的主题角色放在代理类内部创建对象,同时通过约定来静止new一个真实的角色,这样,调用这只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层模块没有任何影响,只要实现了接口所对应的方法。
2)强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的。
即必须通过真实角色查找到代理角色,也就是说真实角色管理代理角色(不允许直接访问真实角色),高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
注意:代理角色也可以再次被代理
2.代理是有个性的
代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。
3.虚拟代理
在需要的时候才初始化主题对象,可以避免被代理对象较多而引起的初始化缓慢的问题,其缺点是需要在每个方法中判断主题对象是否被创建,这就是虚拟代理。
4.动态代理
动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象,相对来说,自己写代理类的方式就是静态代理。
动态代理事先(定义的时候)不知道代理谁,而C没有所有对象的父类都是object的概念以及反射的机制,所以很难实现。java的实现可参考其他书目
六、与其他模式的区别
1、代理模式与策略模式的区别:
差别就是策略模式的封装角色和被封装的策略类不用实现同一个接口,如果是同一个接口那就称为了代理模式。
2、代理模式与装饰模式
装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,
不同点则是代理模式着重对代理过程的控制,它不对被代理类的功能做任何处理,保证原滋原味的调用。
而装饰模式则是对类的功能进行加强或减弱,它着重类的功能变化,它不做准入条件判断和准入参数过滤。
3、代理模式与其他包装模式的区别:
自己不处理让其他人处理,这种类型的模式定义一个名字,叫做包装模式。包装模式包括:装饰模式、适配器模式、门面模式、代理模式、桥梁模式。
5个包装模式都是通过委托的方式对一个对象或一系列对象施行包装,有了包装,设计的系统才更加灵活、稳定,并且极具扩展性。从实现的角度来看,它们都是代理的一种具体表现形式,它们在使用场景上的区别如下:
1)代理模式主要用在不希望展示一个对象内部细节的场景中,此外,代理模式还可以用在一个对象的访问需要限制的场景中。
2)装饰模式是一种特殊的代理模式,它倡导的是在不改变接口的前提下为对象增强功能,或者动态添加额外职责。就扩展性而言,它比子类更灵活。
3)适配器模式的主要意图是接口转换,把一个对象的接口转换成系统希望的另外一个接口。这里的系统指的不仅仅是一个应用,也可能是某个环境,比如通过接口转换可以屏蔽外界接口,以免外界接口深入系统内部,从而提高系统的稳定性和可靠性
4)桥梁模式是在抽象层产生耦合,解决的是自行扩展的问题,它可以使两个有耦合关系的对象互不影响地扩展。
5)门面模式是一个粗粒度的封装,它提供一个方便访问子系统的接口,不具有任何的业务逻辑,仅仅是一个访问复杂系统的快速通道,没有它,子系统照样运行。