(-) 提出问题
朋友们在开发中有没有遇到过这种情况:想封装一些算法,不想让算法直接暴露出来?或者项目里面的一些if-else过于复杂,每个else里都有很多的算法代码,这些算法可能是一些相似的函数或者方法?
如果能把每个算法封装成一个对象,那么就能消除根据类型决定使用什么算法的一些if-else语句,可以考虑使用策略设计模式。
(二 )策略设计模式 简介
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
应用场景:
1、 一个类在执行中使用多个if-else来决定行为。
2、需要算法的各种变体。
3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
(三 )使用策略模式重构代码
下面从一个例子中看下策略模式的写法:
项目里有一些展示运动数据的label,要展示不同的形式,对应不同的算法。效果如下:
第1步 创建策略类
创建策略根类,添加处理正文和单位两个字符串的方法,并提供可以获取处理完的字符串的属性。
@interface AttributeLabelStrategy : NSObject
//属性字符串
@property(nonatomic,copy)NSMutableAttributedString* attributeStr;
/**
* 策略的输入
*
* @param
*
* @return 如果为YES,表示测试通过,如果为NO,表示测试不通过
*/
-(BOOL)strategyText:(NSString *)text unitText:(NSString *)unitText;
@end
对这个方法默认空实现,返回NO
-(BOOL)strategyText:(NSString *)text unitText:(NSString *)unitText{
return NO;
}
第2步 创建具体的策略类
这里创建了两个具体的策略类,一个是正文在前单位在后,一个是单位在前正文在后。两个类分别继承于策略根类,并重写strategyText: unitText:方法。
正文在前:
#import "AttributeTextInFront.h"
#import <UIKit/UIKit.h>
@implementation AttributeTextInFront
-(BOOL)strategyText:(NSString *)text unitText:(NSString *)unitText{
if (!text.length && !unitText.length) {
self.attributeStr = nil;
return NO;
}
NSString *str=[NSString stringWithFormat:@"%@%@",text,unitText];
NSInteger length=unitText.length;
NSMutableAttributedString *AttributedStr=[[NSMutableAttributedString alloc] initWithString:str];
// 前面字符
[AttributedStr addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:20]
range:NSMakeRange(0, str.length-length)];
[AttributedStr addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:10]
range:NSMakeRange(str.length-length, length)];
self.attributeStr = AttributedStr;
return YES;
}
@end
正文在后:
#import "AttributeTextInAfter.h"
#import <UIKit/UIKit.h>
@implementation AttributeTextInAfter
-(BOOL)strategyText:(NSString *)text unitText:(NSString *)unitText{
if (!text.length && !unitText.length) {
self.attributeStr = nil;
return NO;
}
NSString *str=[NSString stringWithFormat:@"%@%@",unitText,text];
NSInteger length=text.length;
NSMutableAttributedString *AttributedStr=[[NSMutableAttributedString alloc] initWithString:str];
// 前面字符
[AttributedStr addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:10]
range:NSMakeRange(0, str.length-length)];
[AttributedStr addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:20]
range:NSMakeRange(str.length-length, length)];
self.attributeStr = AttributedStr;
return YES;
}
@end
第3步 修改自定义的label类
修改label类,使控件关联策略类。添加策略根类属性,用来可以关联所有此根类的子策略类。添加设置属性字符串的方法。
#import <UIKit/UIKit.h>
@class AttributeLabelStrategy;
@interface AttributeLabel : UILabel
/**
* 抽象的策略
*/
@property(nonatomic,strong)AttributeLabelStrategy *strategy;
/**
* 设置显示字符串
*
* @param text 正文字符串
* @param unitText 单位字符串
*
* @return 是否合法,合法,读取AttributeLabelStrategy当中的attributeStr
*/
-(BOOL)setText:(NSString *)text unitText:(NSString *)unitText;
@end
在实现的时候设置属性字符串的方法就直接调用策略类里的设置方法就可以了
-(BOOL)setText:(NSString *)text unitText:(NSString *)unitText{
return [self.strategy strategyText:text unitText:unitText];
}
这样这个策略模式就写完了。
第4步 使用
在使用的时候把label的策略属性负值成自己需要的具体策略类就可以了。然后调用设置字符串的方法,如果返回成功则获取策略类里的属性字符串就可以了。
AttributeLabel *frontLabel = [[AttributeLabel alloc]initWithFrame:CGRectMake(100, 50, 100, 30)];
frontLabel.strategy = [AttributeTextInFront new];
[self.view addSubview:frontLabel];
if ([frontLabel setText:@"10.3" unitText:@"公里"]) {
frontLabel.attributedText = frontLabel.strategy.attributeStr;
}
AttributeLabel *afterLabel = [[AttributeLabel alloc]initWithFrame:CGRectMake(100, 150, 100, 30)];
afterLabel.strategy = [AttributeTextInAfter new];
[self.view addSubview:afterLabel];
if ([afterLabel setText:@"10'23\"" unitText:@"配速"]) {
afterLabel.attributedText = afterLabel.strategy.attributeStr;
}
(四) 策略模式的优缺点
优点:
1、 提供了管理相关的算法族的办法。可以封装一些算法,不想让算法直接暴露出来。
2、可以避免使用多重条件转移语句,消除根据类型决定使用什么算法的一些if-else的语句。
缺点:
1、使用之前必须知道所有的策略,使用中不能动态改变,在实例话的时候就设定好需要使用的策略类了。
所以大家酌情使用
(五)鸣谢
这篇文章是看过 YouXianMing老师 的教程后,得到的启发并重构自己项目代码后写的总结。希望对大家有点帮助。在此感谢YouXianMing老师。