承接上文iOS設(shè)計(jì)模式四(組合,迭代器)
本文為行為擴(kuò)展--獲取源碼
目錄
1 訪問者模式
2 裝飾模式
3 責(zé)任鏈模式
1 訪問者模式
訪問者模式是擴(kuò)展組合結(jié)構(gòu)功能的一種方式.
我們可以使用各種不同用途的訪問者,以同樣的方式訪問這個(gè)組合結(jié)構(gòu),然后用訪問者來處理增加的邏輯
當(dāng)然,也可以用類的Extension來增加邏輯,但是一個(gè)訪問者可以給多個(gè)類都增加邏輯,而用Extension則需要很多個(gè)了
一個(gè)給車升級(jí)的例子:
首先要有車OSZCars.h:
#import <Foundation/Foundation.h>
#import "OSZEngine.h"
#import "OSZWheel.h"
#import "OSZVisitor.h"
@interface OSZCars : NSObject
@property (nonatomic, strong) OSZEngine *engine;
@property (nonatomic, strong) NSMutableArray *wheels;
- (void)addWheels:(OSZWheel *)wheel atIndex:(NSUInteger)index;
//該方法是在加入訪問者后添加的
- (void)acceptVisitor:(id<OSZVisitor>)visitor;
@end
值得注意的是- (void)acceptVisitor:(id<OSZVisitor>)visitor
這個(gè)方法是在加入訪問者后添加的
我們先把所有的模型建立完成再建立訪問者來擴(kuò)展
OSZCars.m:
#import "OSZCars.h"
@implementation OSZCars
- (instancetype)init
{
self = [super init];
if (self) {
self.wheels = [NSMutableArray array];
}
return self;
}
-(void)addWheels:(OSZWheel *)wheel atIndex:(NSUInteger)index
{
[self.wheels insertObject:wheel atIndex:index];
}
- (void)acceptVisitor:(id<OSZVisitor>)visitor
{
[self.engine acceptVisitor:visitor];
for (OSZWheel *wheel in self.wheels)
{
[wheel acceptVisitor:visitor];
}
}
@end
車有個(gè)引擎OSZEngine.h:
#import <Foundation/Foundation.h>
#import "OSZVisitor.h"
@interface OSZEngine : NSObject
@property (nonatomic, copy) NSString *name;
//接收訪問者修改
- (void)acceptVisitor:(id<OSZVisitor>)visitor;
@end
OSZEngine.m:
#import "OSZEngine.h"
@implementation OSZEngine
- (void)acceptVisitor:(id<OSZVisitor>)visitor
{
[visitor visitEngine:self];
}
@end
車還有四個(gè)輪子OSZWheel.h:
#import <Foundation/Foundation.h>
#import "OSZVisitor.h"
@interface OSZWheel : NSObject
@property (nonatomic, assign) CGFloat size;
@property (nonatomic, copy) NSString *name;
//接收訪問者修改
- (void)acceptVisitor:(id<OSZVisitor>)visitor;
@end
OSZWheel.m:
#import "OSZWheel.h"
@implementation OSZWheel
- (void)acceptVisitor:(id<OSZVisitor>) visitor
{
[visitor visitWheel:self];
}
@end
訪問者協(xié)議OSZVisitor.h:
#import <Foundation/Foundation.h>
@class OSZEngine,OSZWheel;
@protocol OSZVisitor <NSObject>
- (void)visitEngine:(OSZEngine *)engine;
- (void)visitWheel:(OSZWheel *)wheel;
@end
接下來在我們要擴(kuò)展功能的時(shí)候,創(chuàng)建一個(gè)訪問者,實(shí)體類修理工
OSZRepairman.h:
#import <Foundation/Foundation.h>
#import "OSZVisitor.h"
@interface OSZRepairman : NSObject<OSZVisitor>
@property (nonatomic, copy) NSString *name;
-(void)visitEngine:(OSZEngine *)engine;
-(void)visitWheel:(OSZWheel *)wheel;
@end
OSZRepairman.m:
#import "OSZRepairman.h"
#import "OSZEngine.h"
#import "OSZWheel.h"
@implementation OSZRepairman
-(void)visitEngine:(OSZEngine *)engine
{
NSLog(@"修理工正在對(duì)%@升級(jí)",engine.name);
}
-(void)visitWheel:(OSZWheel *)wheel
{
NSLog(@"修理工正在對(duì)%@升級(jí)",wheel.name);
}
@end
接下來我們?cè)诳刂破髦袆?chuàng)建車的實(shí)例,訪問者對(duì)車的功能擴(kuò)展
OSZTenVC.m
#import "OSZTenVC.h"
#import "OSZCars.h"
#import "OSZEngine.h"
#import "OSZWheel.h"
#import "OSZRepairman.h"
@interface OSZTenVC ()
@end
@implementation OSZTenVC
- (void)viewDidLoad {
[super viewDidLoad];
OSZEngine *engine1 = [[OSZEngine alloc]init];
engine1.name = @"引擎1號(hào)";
OSZWheel *wheel1 = [[OSZWheel alloc]init];
wheel1.name = @"輪胎1";
OSZWheel *wheel2 = [[OSZWheel alloc]init];
wheel2.name = @"輪胎2";
OSZWheel *wheel3 = [[OSZWheel alloc]init];
wheel3.name = @"輪胎3";
OSZWheel *wheel4 = [[OSZWheel alloc]init];
wheel4.name = @"輪胎4";
OSZCars *car = [[OSZCars alloc]init];
car.engine = engine1;
[car addWheels:wheel1 atIndex:0];
[car addWheels:wheel2 atIndex:1];
[car addWheels:wheel3 atIndex:2];
[car addWheels:wheel4 atIndex:3];
NSLog(@"%@",car.engine.name);
//引擎1號(hào)
[car.wheels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
OSZWheel *wheel = obj;
NSLog(@"%@",wheel.name);
}];
//輪胎1
//輪胎2
//輪胎3
//輪胎4
//訪問者
OSZRepairman *man = [[OSZRepairman alloc]init];
[car acceptVisitor:man];
//修理工正在對(duì)引擎1號(hào)升級(jí)
//修理工正在對(duì)輪胎1升級(jí)
//修理工正在對(duì)輪胎2升級(jí)
//修理工正在對(duì)輪胎3升級(jí)
//修理工正在對(duì)輪胎4升級(jí)
}
@end
可以看出,一旦對(duì)組合結(jié)構(gòu)實(shí)現(xiàn)了訪問者模式,通常就不需要再修改各組合結(jié)構(gòu)的接口,
其實(shí)也是相當(dāng)于把各個(gè)組合結(jié)構(gòu)要擴(kuò)展的邏輯集中在了訪問者中,在沒有復(fù)用的情況下,代碼的總量是一定的
擴(kuò)展:iOS設(shè)計(jì)模式---訪問者模式
2 裝飾模式
我們向?qū)ο筇砑訓(xùn)|西,在用的時(shí)候可以裝飾上,不用的時(shí)候不破壞其原有的邏輯,
添加的東西都可以隨時(shí)添加或者刪除
有兩種方式可以實(shí)現(xiàn)這個(gè)模式:子類和分類
處理圖片的例子:獲取源碼
該協(xié)議定義了一種圖片數(shù)據(jù)結(jié)構(gòu)OSZPhotoProtocol.h
#import <Foundation/Foundation.h>
@protocol OSZPhotoProtocol <NSObject>
//漂亮等級(jí)
@property (nonatomic, assign) int beauty;
//寬高
@property (nonatomic, assign) int width;
@property (nonatomic, assign) int height;
//展示屬性
- (void)show;
@end
裝飾器根類OSZPhoto.h
#import <Foundation/Foundation.h>
#import "OSZPhotoProtocol.h"
@interface OSZPhoto : NSObject<OSZPhotoProtocol>
//保存圖片
@property (nonatomic, strong) id<OSZPhotoProtocol> photo;
//處理圖片的方法
- (id)initWithPhoto:(id<OSZPhotoProtocol>)photos;
@end
OSZPhoto.m
#import "OSZPhoto.h"
@implementation OSZPhoto
@synthesize beauty,width,height;
- (id)initWithPhoto:(id<OSZPhotoProtocol>)photos
{
if (self = [super init])
{
self.photo = photos;
}
return self;
}
- (void)show
{
NSLog(@"漂亮等級(jí)%d,寬%d,高%d",self.photo.beauty,self.photo.width,self.photo.height);
}
@end
2.1先通過子類來實(shí)現(xiàn):
OSZPhotoMeiYan.h是空的
#import "OSZPhoto.h"
@interface OSZPhotoMeiYan : OSZPhoto
@end
OSZPhotoMeiYan.m
#import "OSZPhotoMeiYan.h"
@implementation OSZPhotoMeiYan
- (id)initWithPhoto:(id<OSZPhotoProtocol>)photos
{
// 另一種實(shí)現(xiàn)方式,把自己當(dāng)做圖片處理,而不是持有一個(gè)圖片加工
// self = photos;
// self.beauty += 100;
// return self;
if (self = [super initWithPhoto:photos])
{
self.photo = photos;
}
return self;
}
- (void)show
{
self.photo.beauty += 100;
NSLog(@"漂亮等級(jí)%d,寬%d,高%d",self.photo.beauty,self.photo.width,self.photo.height);
}
@end
OSZPhotoEdit.h也是空的
#import "OSZPhoto.h"
@interface OSZPhotoEdit : OSZPhoto
@end
OSZPhotoEdit.m
#import "OSZPhotoEdit.h"
@implementation OSZPhotoEdit
- (id)initWithPhoto:(id<OSZPhotoProtocol>)photos
{
// self = photos;
// self.width += 100;
// self.height += 100;
// return self;
if (self = [super initWithPhoto:photos])
{
self.photo = photos;
}
return self;
}
- (void)show
{
self.photo.width += 100;
self.photo.height += 100;
NSLog(@"漂亮等級(jí)%d,寬%d,高%d",self.photo.beauty,self.photo.width,self.photo.height);
}
@end
2.2 通過分類來實(shí)現(xiàn)
OSZPhoto+MeiYan.h
#import "OSZPhoto.h"
@interface OSZPhoto (MeiYan)
- (instancetype)showWithMeiYan;
@end
OSZPhoto+MeiYan.m
#import "OSZPhoto+MeiYan.h"
@implementation OSZPhoto (MeiYan)
- (instancetype)showWithMeiYan
{
self.beauty += 100;
NSLog(@"漂亮等級(jí)%d,寬%d,高%d",self.beauty,self.width,self.height);
return self;
}
@end
OSZPhoto+Edit.h
#import "OSZPhoto.h"
@interface OSZPhoto (Edit)
- (instancetype)showWithEdit;
@end
OSZPhoto+Edit.m
#import "OSZPhoto+Edit.h"
@implementation OSZPhoto (Edit)
- (instancetype)showWithEdit
{
self.width += 100;
self.height += 100;
NSLog(@"漂亮等級(jí)%d,寬%d,高%d",self.beauty,self.width,self.height);
return self;
}
@end
控制器OSZElevenVC.m
#import "OSZElevenVC.h"
#import "OSZPhoto.h"
#import "OSZPhotoMeiYan.h"
#import "OSZPhotoEdit.h"
#import "OSZPhoto+MeiYan.h"
#import "OSZPhoto+Edit.h"
@interface OSZElevenVC ()
@end
@implementation OSZElevenVC
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
//正常使用
OSZPhoto *p1 = [[OSZPhoto alloc]init];
p1.beauty = 100;
p1.width = 100;
p1.height = 100;
p1.photo = p1;
[p1 show]; //漂亮等級(jí)100,寬100,高100
//1.通過真正的子類實(shí)現(xiàn)裝飾模式
//裝飾1 美顏
OSZPhotoMeiYan *p2 = [[OSZPhotoMeiYan alloc]initWithPhoto:p1];
[p2 show]; //漂亮等級(jí)200,寬100,高100
//裝飾2 修改大小
OSZPhotoEdit *p3 = [[OSZPhotoEdit alloc]initWithPhoto:p2.photo];
[p3 show]; //漂亮等級(jí)200,寬200,高200
//2.通過分類實(shí)現(xiàn)裝飾模式
//裝飾1 美顏
OSZPhoto *p4 = [p1 showWithMeiYan]; //漂亮等級(jí)300,寬200,高200
//裝飾2 修改大小
OSZPhoto *p5 = [p4 showWithEdit]; //漂亮等級(jí)300,寬300,高300
}
@end
可以看出子類與分類的使用區(qū)別:
在子類中,圖片是一個(gè)抽象類型,被子類持有加工,比較結(jié)構(gòu)化,是原始風(fēng)格
在分類中,圖片是本身直接被加工,比較簡(jiǎn)單輕便,適用于少量裝飾
當(dāng)然,二者都可以實(shí)現(xiàn)另一種方式,這個(gè)例子僅僅是一個(gè)簡(jiǎn)單的演示,可能并不能看出來優(yōu)點(diǎn)與好處,也可能并不貼切,僅僅是我個(gè)人的理解吧
具體如何在項(xiàng)目中使用,可能已經(jīng)在不知不覺中就使用了,比如分類,延展,等等方式
擴(kuò)展:iOS 設(shè)計(jì)模式之裝飾模式(Decorator)
一個(gè)很有意思的報(bào)錯(cuò):
錯(cuò)誤信息如下:
error:Cannot assign to 'self' outside of a method in the init family
原因:
只能在init方法中給self賦值碉渡,Xcode判斷是否為init方法規(guī)則:方法返回id,并且名字以init+大寫字母開頭+其他為準(zhǔn)則
3 責(zé)任鏈模式
這種模式允許我們對(duì)整體結(jié)構(gòu)功能的升級(jí)或者擴(kuò)展,而不必修改已有單元的功能,只要添加鏈就行了
看起來跟裝飾模式有點(diǎn)像,但實(shí)現(xiàn)不同的目的,實(shí)現(xiàn)方式也不同
主要方式為:
當(dāng)前對(duì)象持有同父類下另一對(duì)象,另一對(duì)象可以繼續(xù)持有其他對(duì)象,每個(gè)對(duì)象都重載相同的方法,當(dāng)前對(duì)象處理不了,就返回給父類,父類再傳給另一子類對(duì)象處理
模仿盾牌防御攻擊的例子:獲取源碼
首先創(chuàng)建一個(gè)攻擊父類OSZAttack方便使用,再創(chuàng)建幾個(gè)子攻擊類
火攻擊:OSZFireAttack 冰攻擊:OSZIceAttack 電攻擊:OSZLightingAttack
這幾個(gè)類都是空的,僅僅使用個(gè)類名,就不做繁瑣的邏輯了
再創(chuàng)建一個(gè)防御父類
OSZAttackHandler.h
#import <Foundation/Foundation.h>
#import "OSZAttack.h"
@interface OSZAttackHandler : NSObject
//攻擊處理者
@property (nonatomic, strong) OSZAttackHandler *nextAttackHandler;
//傳遞攻擊
- (void)transmitAttack:(OSZAttack *)attack;
@end
OSZAttackHandler.m
#import "OSZAttackHandler.h"
@implementation OSZAttackHandler
- (void)transmitAttack:(OSZAttack *)attack
{
[self.nextAttackHandler transmitAttack:attack];
}
@end
火攻擊防御者OSZFireHandler.h
#import "OSZAttackHandler.h"
@interface OSZFireHandler : OSZAttackHandler
//在子類的頭文件中再次聲明重寫的方法不是必需的,但是這樣做更加清楚
@end
OSZFireHandler.m
#import "OSZFireHandler.h"
#import "OSZFireAttack.h"
@implementation OSZFireHandler
- (void)transmitAttack:(OSZAttack *)attack
{
//如果是火攻擊就攔截下來
if ([attack isKindOfClass:[OSZFireAttack class]])
{
NSLog(@"我攔截住了火攻擊");
}
//如果不是就返回給父類,再去給別的人處理
else
{
NSLog(@"我擋不住這個(gè)攻擊");
[super transmitAttack:attack];
}
}
@end
為了簡(jiǎn)化代碼,冰防御者OSZIceHandler.h同樣沒有重寫聲明,什么也沒有
OSZIceHandler.m
#import "OSZIceHandler.h"
#import "OSZIceAttack.h"
@implementation OSZIceHandler
- (void)transmitAttack:(OSZAttack *)attack
{
//如果是冰攻擊就攔截下來
if ([attack isKindOfClass:[OSZIceAttack class]])
{
NSLog(@"我攔截住了冰攻擊");
}
//如果不是就返回給父類,再去給別的人處理
else
{
NSLog(@"我擋不住這個(gè)攻擊");
[super transmitAttack:attack];
}
}
@end
最后一個(gè)子類,作為化身,不做防御,處理最后的結(jié)果
OSZAvatar.h中什么也沒有
OSZAvatar.m
#import "OSZAvatar.h"
@implementation OSZAvatar
- (void)transmitAttack:(OSZAttack *)attack
{
NSLog(@"我被打到了????");
}
@end
控制器OSZTwelveVC
#import "OSZTwelveVC.h"
#import "OSZFireAttack.h"
#import "OSZIceAttack.h"
#import "OSZLightingAttack.h"
#import "OSZFireHandler.h"
#import "OSZIceHandler.h"
#import "OSZAvatar.h"
@interface OSZTwelveVC ()
@end
@implementation OSZTwelveVC
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建一個(gè)人物,分配責(zé)任鏈
OSZAvatar *handler1 = [[OSZAvatar alloc]init];
//加火防御
OSZFireHandler *handler2 = [[OSZFireHandler alloc]init];
handler2.nextAttackHandler = handler1;
//加冰防御
OSZIceHandler *handler3 = [[OSZIceHandler alloc]init];
handler3.nextAttackHandler = handler2;
//開始攻擊測(cè)試,選擇最后一個(gè)責(zé)任鏈人物 先走冰防御,再走火防御
//先用火
OSZFireAttack *fire = [[OSZFireAttack alloc]init];
[handler3 transmitAttack:fire];
//我擋不住這個(gè)攻擊
//我攔截住了火攻擊
//再用冰
OSZIceAttack *ice = [[OSZIceAttack alloc]init];
[handler3 transmitAttack:ice];
//我攔截住了冰攻擊
//再用電
OSZLightingAttack *lighting = [[OSZLightingAttack alloc]init];
[handler3 transmitAttack:lighting];
//我擋不住這個(gè)攻擊
//我擋不住這個(gè)攻擊
//我被打到了????
}
@end
實(shí)際上我們完全可以把所有的邏輯都塞在一個(gè)類中達(dá)到目的,但如果邏輯很龐大的話,就會(huì)很亂,
而這三種模式,都是在擴(kuò)展類邏輯的同時(shí),進(jìn)行最少的修改甚至不修改