代理模式:为另外一个对象提供一个替身或占位符,以控制该对象的访问。
代理模式是结构型模式之一。代理模式应该算是一个应用比较常见的设计模式了,是必须掌握的一种设计模式。
为什么需要代理模式?
通过代理模式来简化逻辑的一个很通用的方法。我们不需要知道彼此的业务逻辑。只要处理你要干什么,你告知给我(代理)然后我去找人(RealSubject)帮你实现就好。
比如说我们要展示服务端的数据。我们告知我们的代理类,代理去请求服务端的数据。代理拿到数据后通知我们展示数据的对象。我们不需要关心服务端的具体内部逻辑。
代理分类
1.静态代理
1).普通的代理:类图展示的代理类型,也是最容易理解的代理类型。
2).远程代理 :调用代理类,会被代理转发到远程执行,并且结果会通过返回给代理,再由代理告知给我们调用请求的对象。(上面说的请求数据就是远程代理)
3).虚拟代理:直到我们真正需要一个对象的时候才会创建它。在对象创建前和创建中的过程中时由虚拟对象来扮演真实对象替身。对象创建后,再由代理委托给真实对象。
2.动态代理:在运行时动态的创建代理类,实现多个接口,并将方法转发给指定的类。
动态代理的运用,保护代理。
举个栗子
简单的代理模式没啥好说的,这里提一个保护代理的栗子,保护代理能懂,简单的代理也就懂了。
相亲网站上每个人的信息都在上面公开,你可以根据你的喜好去找一个你喜欢的人。你也可以把你的信息贴出来,让别人来选择。
既然如此,那用户信息都能修改,如果有人改了你兴趣爱好,填上一些恶趣味的爱好? 貌似有些可怕。。。
如果说不让用户修改,那别人怎么对你进行评价?这也是一个问题
所以貌似应该是这样,对自己的信息可以修改但不能修改评价,对别人对信息不能修改,但可以修改评价。
怎么样保证?
我们对所有对数据流进行拦截。要访问(读取/写入)数据必须经过我们的保护代理,保护代理保证写入数据的安全性。
如果不能理解保护代理类图的,现在应该懂了。
用户信息
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol UserInfo <NSObject>
@property (nonatomic ,strong)NSString * name;
@property (nonatomic ,assign)NSInteger age;
//0 为男 , 1 为女
@property (nonatomic ,assign)BOOL sex;
@property (nonatomic ,copy)NSString * hobit;
@property (nonatomic ,strong)NSString * publicExperice;
//评分
@property (nonatomic ,assign)NSInteger grade;
@end
NS_ASSUME_NONNULL_END
用户的具体实现(getter/setter)方法
#import "LocalUser.h"
@implementation LocalUser
@synthesize name = _name;
@synthesize age = _age;
@synthesize sex = _sex;
@synthesize hobit = _hobit;
@synthesize publicExperice = _publicExperice;
@synthesize grade = _grade;
-(void)setName:(NSString *)name {
_name = name;
}
-(NSString *)name {
return _name;
}
-(void)setAge:(NSInteger)age {
_age = age;
}
-(NSInteger)age {
return _age;
}
-(void)setSex:(BOOL)sex {
_sex = sex;
}
-(BOOL)sex {
return _sex;
}
-(void)setHobit:(NSString *)hobit {
_hobit = hobit;
}
-(NSString *)hobit {
return _hobit;
}
-(void)setPublicExperice:(NSString *)publicExperice {
_publicExperice = publicExperice;
}
-(NSString *)publicExperice {
return _publicExperice;
}
-(void)setGrade:(NSInteger)grade {
_grade = grade;
}
-(NSInteger)grade {
return _grade;
}
@end
动态代理需要遵循的协议
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol Invoker <NSObject>
-(id)invokeWithSelector:(SEL)sel args:(NSArray *)args ;
@end
NS_ASSUME_NONNULL_END
声明:iOS的动态代理,这里是手动实现的,逻辑并不完善。
java中有自己的动态代理,对这套代码无需看懂。
简单叙述下,就是通过消息转发将方法调用转发给其他的对象(realSubject)来处理。
#import "Proxy.h"
#import <objc/message.h>
static id<Invoker> proxyHander;
static id localProtol ;
@implementation Proxy
+(id)proxyInstance:(Protocol *)protocol handler:(id<Invoker>)handler {
localProtol = protocol;
proxyHander = handler ;
class_addProtocol(self, protocol);
return [[Proxy alloc] init];
}
+(id<Invoker>)getProxyHandler {
return proxyHander;
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [[proxyHander class] instanceMethodSignatureForSelector:@selector(invokeWithSelector:args:)];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation {
NSMethodSignature *signature = [[localProtol class] instanceMethodSignatureForSelector:anInvocation.selector];
NSInteger count = signature.numberOfArguments;
//说明 所有参数必须atIndex: >=2 因为前面默认两个参数
SEL selector = anInvocation.selector;
NSMutableArray * args = [NSMutableArray array];
for (int i = 0; i < count - 2; i ++) {
const char * type = [signature getArgumentTypeAtIndex:2];
NSString * car = [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
if ([car isEqualToString:@":"]) {
SEL selector ;
[anInvocation getArgument:&selector atIndex:i + 2];
[args addObject:NSStringFromSelector(selector)];
}else if([car isEqualToString:@"@"]){
NSObject * obj ;
[anInvocation getArgument:&obj atIndex:i + 2];
[args addObject:obj];
}else if([car isEqualToString:@"i"]){
int num;
[anInvocation getArgument:&num atIndex:i + 2];
[args addObject:@(num)];
}else if([car isEqualToString:@"B"]){
int num;
[anInvocation getArgument:&num atIndex:i + 2];
[args addObject:@(num)];
}else if([car isEqualToString:@"f"]){
float num;
[anInvocation getArgument:&num atIndex:i + 2];
[args addObject:@(num)];
}
}
[anInvocation setArgument:&selector atIndex:2];
if (args) {
[anInvocation setArgument:&args atIndex:3];
}
[anInvocation setTarget:proxyHander];
[anInvocation setSelector:@selector(invokeWithSelector:args:)];
[anInvocation invokeWithTarget:proxyHander];
}
@end
处理自己的数据的代理
iOS可以用继承,也可以使用拓展,这里用的扩展
#import "Proxy+OwnnerProxy.h"
#import "LocalUser.h"
#import "NewOwnerInfoHandler.h"
@implementation Proxy (OwnnerProxy)
-(id<UserInfo>)getOwnerWithObject:(id<UserInfo>)object {
NewOwnerInfoHandler * hander = [[NewOwnerInfoHandler alloc] initWithUser:object];
return [Proxy proxyInstance:object handler:hander];
}
处理别人数据的代理
#import "Proxy+OtherProxy.h"
#import "NewOtherInfoHandler.h"
@implementation Proxy (OtherProxy)
-(id)getOtherWithObject:(id)object {
NewOtherInfoHandler * hander = [[NewOtherInfoHandler alloc] initWithUser:object];
return [Proxy proxyInstance:object handler:hander];
}
@end
处理自己信息的RealSubject
#import "NewOwnerInfoHandler.h"
#import <objc/message.h>
@interface NewOwnerInfoHandler()
@property (nonatomic ,strong)id<UserInfo> localUser;
@end
@implementation NewOwnerInfoHandler
- (instancetype)initWithUser:(id<UserInfo>)localUser {
if (self = [super init]) {
_localUser = localUser;
}return self;
}
-(id)invokeWithSelector:(SEL)sel args:(NSArray *)args{
NSString * selectorName = NSStringFromSelector(sel) ;
NSMethodSignature *signature = [[_localUser class] instanceMethodSignatureForSelector:sel];
if([selectorName isEqualToString:@"setGrade:"]) {
@throw [NSException exceptionWithName:@"error opration" reason:@"you can`t change your grade" userInfo:nil];
}else if ([selectorName hasPrefix:@"set"]) {//set方法
for (NSObject *obj in args) {
if ([obj isKindOfClass:[NSNumber class]]) {
const char * type = [signature getArgumentTypeAtIndex:2];
NSString * car = [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
if([car isEqualToString:@"B"]) {
objc_msgSend(_localUser, sel,[[args firstObject] intValue]);
}else if([car isEqualToString:@"f"]) {
objc_msgSend(_localUser, sel,[[args firstObject] intValue]);
}else if([car isEqualToString:@"f"]) {
objc_msgSend(_localUser, sel,[[args firstObject] intValue]);
}else {
objc_msgSend(_localUser, sel,[[args firstObject] intValue]);
}
}else {
objc_msgSend(_localUser, sel,[args firstObject]);
}
}
}else {//普通方法和get方法
return objc_msgSend(_localUser, sel);
}
return nil;
}
@end
处理别人信息的realSubject
#import "NewOtherInfoHandler.h"
#import <objc/message.h>
@interface NewOtherInfoHandler()
@property (nonatomic ,strong)id<UserInfo> localUser;
@end
@implementation NewOtherInfoHandler
- (instancetype)initWithUser:(id<UserInfo>)localUser {
if (self = [super init]) {
_localUser = localUser;
}return self;
}
-(id)invokeWithSelector:(SEL)sel args:(NSArray *)args{
NSString * selectorName = NSStringFromSelector(sel) ;
if([selectorName isEqualToString:@"setGrade:"]) {
objc_msgSend(_localUser, sel,[[args firstObject] integerValue]);
}else if ([selectorName hasPrefix:@"set"]) {//set方法
@throw [NSException exceptionWithName:@"error opration" reason:@"you have no right to modify some one`s infomation" userInfo:nil];
}else {//普通方法和get方法
return objc_msgSend(_localUser, sel);
}
return nil;
}
@end
动态代理调用
这里数据直接造一个假设是从数据库中拿出来的。
#import <Foundation/Foundation.h>
#import "Proxy+OwnnerProxy.h"
#import "LocalUser.h"
#import "Proxy+OtherProxy.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
//制造的数据
Proxy * proxy = [[Proxy alloc] init];
LocalUser * local = [[LocalUser alloc] init];
local.name = @"汪撕葱";
local.age = 28;
local.sex = 0;
local.hobit = @"谈朋友";
local.publicExperice = @"谈过100个女朋友";
local.grade = 9;
//开始动态代理
// LocalUser * user = [proxy getOwnerWithObject:local];
// user.grade = 10;
LocalUser * user = [proxy getOtherWithObject:local];
user.grade = 10;
user.name = @"华克";
}
return 0;
}
优点
只需要关注业务逻辑本身,保证业务逻辑的重用性
在一定程度上解耦
缺点
可能导致速度变慢
实现代理模式需要额外的工作,有些代理模式的实现非常的复杂