iOS设计模式解析

策略模式

2019-08-01  本文已影响0人  FY_Chao

What 策略模式

策略模式:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。此模式让算法的变化独立于使用它的客户。如下图:

一般使用接口(协议)的方式来实现策略模式,具体的算法实现交由不同的实体类,每个算法之间可以通过提供的方法进行替换。

例子1

首先我们定义一个 Duck类,Duck中会有 flyquackswimdispaly等行为方法,我们了解到不同类型的鸭子🦆有些行为方法会有不同表现,所以我们需要对这些进行分类,区分出哪些是变化的部分,哪些是不会变化的。针对不会变化的我们可以使用继承来实现,对于可变( flyquack)的就可以采取策略模式,封装算法,并且算法间可以相互替换。
🦆策略模式——GitHub源码
下面是部分源码:

/// 定义协议
@protocol QuackBehavior <NSObject>

- (void)quack;

@end

@protocol FlyBehavior <NSObject>

- (void)fly;

@end
#import "FlyBehavior.h"
#import "QuackBehavior.h"

@interface Duck : NSObject
/// 不变的部分
- (void)swim;
- (void)display;

/// 可变部分(子类在生成构造时设置)
@property (nonatomic, strong) id<FlyBehavior> flyBehavior;
@property (nonatomic, strong) id<QuackBehavior> quackBehavior;

- (void)performFly;
- (void)performQuack;

@end
//  implementation 
@implementation Duck
- (void)performFly {
    [self.flyBehavior fly];
}
- (void)performQuack {
    [self.quackBehavior quack];
}
@end
@interface MallardDuck : Duck

@end

@implementation MallardDuck
// 构造时指定具体的实现
- (instancetype)init {
    if (self = [super init]) {
        self.quackBehavior = [[Quack alloc] init];
        self.flyBehavior = [[FlyWithWings alloc] init];
    }
    return self;
}

- (void)display {
    NSLog(@"I'm a real Mallard Duck");
}

@end

上述代码采用组合的方式,将flyquack的实现封装成类。将fly的实现交由遵守FlyBehavior的类来实现。quack的实现交由遵守QuackBehavior的类来实现。只需要遵守响应的协议我们就可以实现对应的行为,在不同的类中我们的实现不同,当我们需要对行为进行切换的时候,我们只需要将实现类进行替换就好。

    Duck *mDuck = [[MallardDuck alloc] init];
    [mDuck display];
    [mDuck performQuack];
    [mDuck performFly];
    
    mDuck.flyBehavior = [[FlyWithNoWay alloc] init];
    [mDuck performFly];
// 输出
 I'm a real Mallard Duck
 Quack
 i'm flying
 i can't fly

例子2

UITextField策略模式——GitHub源码

我们可以通过 iOS 的 UITextField 的输入验证的功能再来说明下策略模式。

日常开发中我们经常会对UITextField 的输入进行校验,提示用户输入是否合法,如果不使用设计模式,我们最先想到的做法是在 UITextField 的代理房中- (BOOL)textFieldShouldEndEditing:(UITextField *)textField中做判断

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
    if (textField == numericTextField) {
        // 验证输入的text是否为数值
    }else if (textField == alphaTextField ){
        // 验证输入的text是否为字母
    }
}

当我们的输入框的输入格式越来越多 if的判断语句也会越来越多。
ps:一般来说当代码语句中含有很多条件语句,就意味着需要把它们重构成各种策略对象。

参照例1的想法,把变化的抽取出来,即输入验证的内容判断,我们可以判断是否是数字、字母、中文、手机号等。首先我们定义一个协议

@protocol InputValidateProtocol <NSObject>
- (BOOL)validateInput:(UITextField *)input error:(NSError **)error;
@end

协议的具体实现则交由验证判断的具体实现类。


@implementation NumericValidator

- (BOOL)validateInput:(UITextField *)input error:(NSError *__autoreleasing *)error {
        NSError *regError = nil;
        NSRegularExpression *regex = [NSRegularExpression
                                      regularExpressionWithPattern:@"^[0-9]*$" options:NSRegularExpressionAnchorsMatchLines error:&regError];
        NSUInteger numberOfMatches = [regex
                                      numberOfMatchesInString:[input text]
                                      options:NSMatchingAnchored range:NSMakeRange(0, [[input text] length])];
        // if there is not a single match // then return an error and NO
        if (numberOfMatches == 0)
        {
            if (error != nil) {
                NSString *description = NSLocalizedString(@"Input Validation Failed", @"");
                NSString *reason = NSLocalizedString(@"The input can contain only numerical values", @"");
                NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil];
                NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey,NSLocalizedFailureReasonErrorKey, nil];
                NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
                *error = [NSError errorWithDomain:@"InputValidationErrorDomain" code:1001 userInfo:userInfo];
            }
            return NO;
        }
    return YES;
}

@end


@implementation AlphaInputValidator

- (BOOL)validateInput:(UITextField *)input error:(NSError *__autoreleasing *)error {
    NSError *regError = nil;
    NSRegularExpression *regex = [NSRegularExpression
                                  regularExpressionWithPattern:@"^[a-zA-Z]*$" options:NSRegularExpressionAnchorsMatchLines error:&regError];
    NSUInteger numberOfMatches = [regex
                                  numberOfMatchesInString:[input text]
                                  options:NSMatchingAnchored range:NSMakeRange(0, [[input text] length])];
    // If there is not a single match
    // then return an error and NO
    if (numberOfMatches == 0)
    {
        if (error != nil) {
            NSString *description = NSLocalizedString(@"Input Validation Failed", @"");
            NSString *reason = NSLocalizedString(@"The input can contain only letters", @"");
            NSArray *objArray = [NSArray arrayWithObjects:description, reason, nil];
            NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey,NSLocalizedFailureReasonErrorKey, nil];
            NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
            *error = [NSError errorWithDomain:@"InputValidationErrorDomain" code:1002 userInfo:userInfo];
        }
        return NO;
        
    }
    return YES;
}
@end

然后在使用的时候设置相应的判断器。inputValidator属性我们可以通过runtime动态为 UITextField 绑定,或者通过子类自己添加。例子中采用的是子类的方式。


@interface CustomTextField : UITextField

@property (nonatomic, strong) id<InputValidateProtocol> inputValidator;

- (BOOL)vaild;

@end


@implementation CustomTextField

- (BOOL)vaild {
    NSError *error;
    BOOL validationResult = [_inputValidator validateInput:self error:&error];
    if (!validationResult) {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[error localizedDescription]
                                                            message:[error localizedFailureReason] delegate:nil
                                                  cancelButtonTitle:NSLocalizedString(@"OK", @"")
                                                  otherButtonTitles:nil];
        [alertView show];
    }
    return validationResult;
}
@end


- (void)viewDidLoad {
    [super viewDidLoad];
    self.AlaphaTextField.inputValidator = [[AlphaInputValidator alloc] init];
    self.numericTextField.inputValidator = [[NumericValidator alloc] init];
    // Do any additional setup after loading the view.
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    if ([textField isKindOfClass:[CustomTextField class]]) {
        [(CustomTextField *)textField vaild];
    }
}

这样子我们就不用关心textField输入完后是如何检测输入内容是否有效,我们只需指定检测器validator,判断如果是CustomTextField类型就去执行一次检测。无需再添加更多的if条件语句,而且也可以很好的复用定义好的validator

设计原则

上一篇 下一篇

猜你喜欢

热点阅读