代理模式(Proxy Pattern)

2019-12-03  本文已影响0人  iOS_学渣

代理模式:为另外一个对象提供一个替身或占位符,以控制该对象的访问。

代理模式是结构型模式之一。代理模式应该算是一个应用比较常见的设计模式了,是必须掌握的一种设计模式。

代理模式
为什么需要代理模式?

通过代理模式来简化逻辑的一个很通用的方法。我们不需要知道彼此的业务逻辑。只要处理你要干什么,你告知给我(代理)然后我去找人(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;
}

优点

只需要关注业务逻辑本身,保证业务逻辑的重用性
在一定程度上解耦

缺点

可能导致速度变慢
实现代理模式需要额外的工作,有些代理模式的实现非常的复杂

上一篇下一篇

猜你喜欢

热点阅读