承接上文iOS设计模式五(访问者,装饰,责任链)
本文为算法封装--获取源码
目录
1 模板模式
2 策略模式
3 命令模式
1 模板模式
模板模式顾名思义,套用模板.应该是最简单常用的模式了吧,就是俩字:复用!
继承也是复用的一种方式,在抽象类或者父类中定义公共的方法,在子类中重写或重载实现
稍微看个继承的代码😂
做一顿饭有很多选择,中餐,西餐.
父类OSZFood.h
#import <Foundation/Foundation.h>
@interface OSZFood : NSObject
//做饭
- (void)make;
//米饭
- (void)makeMeal;
//肉
- (void)makeMeat;
//蔬菜
- (void)makeVegetables;
//预留的空方法,子类去添加 所谓的"钩子"
- (void)makeOther;
@end
OSZFood.m
#import "OSZFood.h"
@implementation OSZFood
- (void)make{
[self makeMeal];
[self makeMeat];
[self makeVegetables];
}
- (void)makeMeal{
NSLog(@"做主食");
}
- (void)makeMeat{
NSLog(@"做肉");
}
- (void)makeVegetables{
NSLog(@"做菜");
}
- (void)makeOther{
}
@end
中餐OSZChineseFood.h
#import "OSZFood.h"
@interface OSZChineseFood : OSZFood
//重写做菜方法,加入中餐特有的食物
- (void)makeVegetables;
- (void)chao;
@end
OSZChineseFood.m
#import "OSZChineseFood.h"
@implementation OSZChineseFood
//重写make方法,加入中餐特有的食物
- (void)makeVegetables{
[self chao];
}
- (void)chao{
NSLog(@"炒个菜");
}
@end
西餐 OSZWesternFood.h
#import "OSZFood.h"
@interface OSZWesternFood : OSZFood
//重写做肉方法,加入西餐特有的牛排
- (void)makeMeat;
- (void)steak;
@end
OSZWesternFood.m
#import "OSZWesternFood.h"
@implementation OSZWesternFood
- (void)makeMeat{
[self steak];
}
- (void)steak{
NSLog(@"煎个牛排");
}
@end
控制器OSZThirteenVC.m
#import "OSZThirteenVC.h"
#import "OSZChineseFood.h"
#import "OSZWesternFood.h"
@interface OSZThirteenVC ()
@end
@implementation OSZThirteenVC
- (void)viewDidLoad {
[super viewDidLoad];
OSZChineseFood *c = [[OSZChineseFood alloc]init];
[c make]; //做主食 //做肉 //炒个菜
OSZWesternFood *w = [[OSZWesternFood alloc]init];
[w make]; //做主食 //煎个牛排 //做菜
}
@end
没什么可看的,就是个继承实现的简单复用,是抽出共同行为放入框架类中的基本手段
2 策略模式
策略模式中一个关键角色是策略类strategy,它为所有支持的或相关的算法声明了一个共同接口,下面三个是具体策略类concreteStrategy,分别根据context的需求实现不同的效果,
调用时context使用strategy声明的方法,根据枚举值选用具体策略类concreteStrategy就可以了
看到这里是不是感觉很简单,其实我们在写app做一个界面的时候就自然的用到了,
context就是视图View,策略类就是控制器Controller,
我们复用同一个View,Controller调用不同的接口,或者干脆换个Controller,
在这个View上显示不同的控件(比如Label,按钮Button,或者自定义的控件),或者显示不同的数据Model
简单的MVC中策略模式例子:
做一个简单的视图,根据控制器不同显示不同的数据
OSZFourteenView.h
#import <UIKit/UIKit.h>
@interface OSZFourteenView : UIView
@property (nonatomic, weak) UILabel *lbl;
@property (nonatomic, weak) UIButton *btn;
@end
OSZFourteenView.m
#import "OSZFourteenView.h"
@implementation OSZFourteenView
- (instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self){
[self setupUI];
}
return self;
}
- (void)setupUI{
self.backgroundColor = [UIColor whiteColor];
CGRect rect = CGRectMake(100,100,150,30);
UILabel *titleLbl = [[UILabel alloc]initWithFrame:rect];
titleLbl.text = @"";
titleLbl.textColor = [UIColor blackColor];
titleLbl.font = [UIFont boldSystemFontOfSize:22];
titleLbl.textAlignment = NSTextAlignmentCenter;
[self addSubview:titleLbl];
self.lbl = titleLbl;
rect = CGRectMake(100,200,200,30);
UIButton *button = [[UIButton alloc]initWithFrame:rect];
[button setTitle:@"" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:22];
[self addSubview:button];
self.btn = button;
}
@end
两个控制器:
OSZFourteenVC.m
#import "OSZFourteenVC.h"
#import "OSZFourteenView.h"
#import "OSZFourteenVC2.h"
@interface OSZFourteenVC ()
@end
@implementation OSZFourteenVC
- (void)viewDidLoad {
[super viewDidLoad];
OSZFourteenView *view14 = [[OSZFourteenView alloc]init];
self.view = view14;
view14.lbl.text = @"第一个控制器";
[view14.btn setTitle:@"跳到第二个控制器" forState:UIControlStateNormal];
[view14.btn addTarget:self action:@selector(turn) forControlEvents:UIControlEventTouchUpInside];
}
- (void)turn{
[self presentViewController:[[OSZFourteenVC2 alloc]init] animated:YES completion:nil];
}
@end
OSZFourteenVC2.m
#import "OSZFourteenVC2.h"
#import "OSZFourteenView.h"
#import "OSZFourteenVC.h"
@interface OSZFourteenVC2 ()
@end
@implementation OSZFourteenVC2
- (void)viewDidLoad {
[super viewDidLoad];
OSZFourteenView *view14 = [[OSZFourteenView alloc]init];
self.view = view14;
view14.lbl.text = @"第二个控制器";
[view14.btn setTitle:@"跳到第一个控制器" forState:UIControlStateNormal];
[view14.btn addTarget:self action:@selector(turn) forControlEvents:UIControlEventTouchUpInside];
}
- (void)turn{
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
策略模式与装饰模式看起来有点像,但是装饰器是在对象外面叠加,而策略模式是在内部更换内容
3 命令模式
把指令封装在命令对象中,命令对象可以被传递并且被不同的客户端复用,客户端可以把它参数化并置入队列或日志中
命令模式消除了作为对象的动作和执行它的接收器之间的绑定.
看完了概念是不是感觉似曾相识?
实际上命令模式是Cocoa Touch框架收录的模式之一
NSInvocation与NSUndoManager和"目标-动作"机制是框架中对这个模式的典型应用
NSInvocation对象封装运行时库以向接收器转发执行消息所需的所有必要信息,是调用方法的一种方式
NSUndoManager作为通用的撤销栈的管理类,能够逆转,撤销操作
"目标-动作"机制就是 目标(Target)与动作(Action),即用performSelector
调用方法的一种方式
一个用NSUndoManager实现的支持撤销功能的画板:
定义线OSZLine.h
#import <Foundation/Foundation.h>
@interface OSZLine : NSObject
@property (nonatomic,assign) CGPoint begin;
@property (nonatomic,assign) CGPoint end;
@property (nonatomic,strong) UIColor *color;
@end
OSZLine.m
#import "OSZLine.h"
@implementation OSZLine
-(instancetype)init{
self = [super init];
if (self) {
[self setColor:[UIColor blackColor]];
}
return self;
}
@end
画板OSZDrawView.h
#import <UIKit/UIKit.h>
#import "OSZLine.h"
@interface OSZDrawView : UIView
@property (nonatomic) OSZLine *currentLine;
@property (nonatomic) NSMutableArray *linesCompleted;
//回退
@property (nonatomic, weak) UIButton *undoBtn;
//前进
@property (nonatomic, weak) UIButton *redoBtn;
@end
OSZDrawView.m
#import "OSZDrawView.h"
@implementation OSZDrawView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setupUI];
}
return self;
}
- (void)setupUI
{
self.backgroundColor = [UIColor whiteColor];
self.linesCompleted = [[NSMutableArray alloc] init];
[self setMultipleTouchEnabled:YES];
[self becomeFirstResponder];
CGFloat origin = 40;
CGFloat W = (kScreenWidth - 4 * origin ) * 0.5;
CGFloat H = 44;
CGFloat X = origin;
CGFloat Y = kScreenHeight - origin - H;
CGRect rect = CGRectMake(X,Y,W,H);
UIButton *undoBtn = [[UIButton alloc]initWithFrame:rect];
[undoBtn setTitle:@"回退" forState:UIControlStateNormal];
[undoBtn setBackgroundColor:[UIColor grayColor]];
[undoBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
undoBtn.titleLabel.font = [UIFont systemFontOfSize:17];
undoBtn.layer.cornerRadius = 5;
undoBtn.layer.masksToBounds = YES;
self.undoBtn = undoBtn;
[self addSubview:undoBtn];
[self.undoBtn addTarget:self action:@selector(undo) forControlEvents:UIControlEventTouchUpInside];
X = 3 * origin + W;
rect = CGRectMake(X,Y,W,H);
UIButton *redoBtn = [[UIButton alloc]initWithFrame:rect];
[redoBtn setTitle:@"前进" forState:UIControlStateNormal];
[redoBtn setBackgroundColor:[UIColor grayColor]];
[redoBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
redoBtn.titleLabel.font = [UIFont systemFontOfSize:17];
redoBtn.layer.cornerRadius = 5;
redoBtn.layer.masksToBounds = YES;
self.undoBtn = redoBtn;
[self addSubview:redoBtn];
[self.undoBtn addTarget:self action:@selector(redo) forControlEvents:UIControlEventTouchUpInside];
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 5.0);
CGContextSetLineCap(context, kCGLineCapRound);
for (OSZLine *line in self.linesCompleted) {
[[line color] set];
CGContextMoveToPoint(context, [line begin].x, [line begin].y);
CGContextAddLineToPoint(context, [line end].x, [line end].y);
CGContextStrokePath(context);
}
}
//回退
- (void)undo
{
if ([self.undoManager canUndo]) {
[self.undoManager undo];
}
}
//前进
- (void)redo
{
if ([self.undoManager canRedo]) {
[self.undoManager redo];
}
}
//NSUndoManager 的实现原理是它作为一个记录器,每次数据变化,我们要用这个记录器记录一个相反的操作,
//当需要undo的时候,它通过执行这个相反的操作就可以实现了。
- (void)addLine:(OSZLine*)line
{
[[self.undoManager prepareWithInvocationTarget:self] removeLine:line];
[self.linesCompleted addObject:line];
[self setNeedsDisplay];
}
- (void)removeLine:(OSZLine*)line
{
if ([self.linesCompleted containsObject:line]) {
[[self.undoManager prepareWithInvocationTarget:self] addLine:line];
[self.linesCompleted removeObject:line];
[self setNeedsDisplay];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *t in touches)
{
CGPoint loc = [t locationInView:self];
self.currentLine.end = loc;
if (self.currentLine)
{
[self addLine:self.currentLine];
}
OSZLine *newLine = [[OSZLine alloc] init];
newLine.begin = loc;
newLine.end = loc;
self.currentLine = newLine;
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//NSUndoManager有一个分组的概念,就是为了解决这类问题的
//将所有的笔画放在一个组中
[self.undoManager beginUndoGrouping];
for (UITouch *t in touches) {
// Create a line for the value
CGPoint loc = [t locationInView:self];
OSZLine *newLine = [[OSZLine alloc] init];
newLine.begin = loc;
newLine.end = loc;
self.currentLine = newLine;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self setNeedsDisplay];
[self.undoManager endUndoGrouping];
}
@end
控制器OSZFifteenVC.m
#import "OSZFifteenVC.h"
#import "OSZDrawView.h"
@interface OSZFifteenVC ()
@property (nonatomic, weak) OSZDrawView *drawView;
@end
@implementation OSZFifteenVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//NSUndoManager应用
OSZDrawView *drawView = [[OSZDrawView alloc]initWithFrame:self.view.frame];
self.drawView = drawView;
[self.view addSubview:drawView];
//NSInvocation应用
[self invocationMethod];
//所谓的"目标-动作"机制就是 目标(Target)与动作(Action)
[self performSelector:@selector(invocationMethod)];
}
//命令模式是Cocoa Touch框架收录的模式之一
//NSInvocation与NSUndoManager和"目标-动作"机制是框架中对这个模式的典型应用
- (void)invocationMethod
{
//NSInvocation;用来包装方法和对应的对象,它可以存储方法的名称,对应的对象,对应的参数,
/*
NSMethodSignature:签名:再创建NSMethodSignature的时候,必须传递一个签名对象,签名对象的作用:用于获取参数的个数和方法的返回值
*/
//创建签名对象的时候不是使用NSMethodSignature这个类创建,而是方法属于谁就用谁来创建
NSMethodSignature*signature = [OSZFifteenVC instanceMethodSignatureForSelector:@selector(sendMessageWithNumber:WithContent:)];
//1、创建NSInvocation对象
NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
//invocation中的方法必须和签名中的方法一致。
invocation.selector = @selector(sendMessageWithNumber:WithContent:);
/*第一个参数:需要给指定方法传递的值
第一个参数需要接收一个指针,也就是传递值的时候需要传递地址*/
//第二个参数:需要给指定方法的第几个参数传值
NSString*number = @"13812345678";
//注意:设置参数的索引时不能从0开始,因为0已经被self占用,1已经被_cmd占用
[invocation setArgument:&number atIndex:2];
NSString*number2 = @"哈哈哈";
[invocation setArgument:&number2 atIndex:3];
//2、调用NSInvocation对象的invoke方法
//只要调用invocation的invoke方法,就代表需要执行NSInvocation对象中制定对象的指定方法,并且传递指定的参数
[invocation invoke];
}
- (void)sendMessageWithNumber:(NSString*)number WithContent:(NSString*)content{
NSLog(@"电话号%@,姓名%@",number,content);
}
@end