美文网首页
代理模式(Proxy Pattern)

代理模式(Proxy Pattern)

作者: iOS_学渣 | 来源:发表于2019-12-03 15:14 被阅读0次

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

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

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

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

    优点

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

    缺点

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

    相关文章

      网友评论

          本文标题:代理模式(Proxy Pattern)

          本文链接:https://www.haomeiwen.com/subject/ilkcgctx.html