引言
這是在項目開發(fā)過程中的需求:
用戶Zing_LYF在photo2頻道中發(fā)布了內(nèi)容為“是不是真的”的感言,除了頭部的展示內(nèi)容不同,其他都一樣。
(1)在個人中心,感言頭部顯示該感言所屬頻道的頻道icon和頻道名稱,點擊icon,跳轉(zhuǎn)到頻道詳情
(2)在頻道詳情,感言頭部展示發(fā)布此感言的用戶頭像和用戶名,點擊頭像,跳轉(zhuǎn)該用戶的個人中心


一般的思路:
cell的布局一模一樣,但是在給cell綁定感言模型的時候,同時傳一個模塊參數(shù)表示是個人中心 還是 頻道詳情,然后在cell里面再通過if else 判斷不同的模塊,處理不同的邏輯。
問題
隨著以后模塊的繼續(xù)增加,cell中會有很多if else 的邏輯判斷,顯得邏輯很混亂,代碼很冗余。
思考
我們發(fā)現(xiàn)這兩個模塊的感言數(shù)據(jù)是一樣的,但是部分的展示模式隨著模塊的不同而不同,這幾引出我們今天的設(shè)計模式——策略設(shè)計模式
策略設(shè)計模式
1.概念
策略模式定義了一系列的算法,并將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法獨立于使用它的客戶而獨立變化。
2.組成
- 抽象策略角色: 策略類,通常由一個接口或者抽象類實現(xiàn)。
- 具體策略角色:包裝了相關(guān)的算法和行為。
- 環(huán)境角色:持有一個策略類的引用,最終給客戶端調(diào)用。
3.概念
Context(應(yīng)用場景):
1、需要使用ConcreteStrategy提供的算法。
2、 內(nèi)部維護(hù)一個Strategy的實例。
3、 負(fù)責(zé)動態(tài)設(shè)置運行時Strategy具體的實現(xiàn)算法。
4、負(fù)責(zé)跟Strategy之間的交互和數(shù)據(jù)傳遞。
Strategy(抽象策略類):
1、 定義了一個公共接口,各種不同的算法以不同的方式實現(xiàn)這個接口,Context使用這個接口調(diào)用不同的算法,一般使用接口或抽象類實現(xiàn)。
ConcreteStrategy(具體策略類):
2、 實現(xiàn)了Strategy定義的接口,提供具體的算法實現(xiàn)。
4.UML類圖

5.使用
1.抽象策略類:SenseHeaderDisplayStrategy(感言頭部展示策略)
@interface SenseHeaderDisplayStrategy : NSObject<SenseHeaderDisplayStrategy>
@end
2.抽象策略類定義的一系列接口:
/**
頭部視圖策略接口的定義
*/
@protocol SenseHeaderDisplayStrategy <NSObject>
@optional
/**
獲取頭像URL
@param sense sense
@return 頭像URL
*/
- (NSString *)getIconUrlWithSense:(ZTMSense *)sense;
/**
獲取標(biāo)題文本
@param sense sense
@return 標(biāo)題文本
*/
- (NSString *)getTitleTextWithSense:(ZTMSense *)sense;
/**
點擊頭像邏輯跳轉(zhuǎn)
@param sense sense
@param delegate 代理
*/
- (void)triggerIconClickWithSense:(ZTMSense *)sense delegate:(id<SenseCellDelegate>)delegate;
@end
3.具體策略類:SenseHeaderDisplay4Channel(頭部展示頻道信息的策略類)
實現(xiàn)具體的接口:
#pragma mark - SenseHeaderDisplayStrategy
- (NSString *)getIconUrlWithSense:(ZTMSense *)sense{
//頻道icon
return [ZingFileManager getPortrait:sense.channel.icon];
}
- (NSString *)getTitleTextWithSense:(ZTMSense *)sense {
//頻道名稱
return sense.channel.name;
}
- (void)triggerIconClickWithSense:(ZTMSense *)sense delegate:(id<SenseCellDelegate>)delegate {
//跳轉(zhuǎn)頻道詳情
[self onNavigationToChannelWithSense:sense];
}
- (void)onNavigationToChannelWithSense:(ZTMSense *)sense {
[[ZingManager getCurrentViewController] gotoChannelWithId:sense.channelId];
}
4.具體策略類:SenseHeaderDisplay4Person(頭部展示個人信息的策略類)
實現(xiàn)具體的接口:
#pragma mark - SenseHeaderDisplayStrategy
- (NSString *)getIconUrlWithSense:(ZTMSense *)sense {
//用戶頭像
return [ZingFileManager getPortrait:sense.user.avatar];
}
- (NSString *)getTitleTextWithSense:(ZTMSense *)sense {
//用戶名
return sense.user.userName;
}
- (void)triggerIconClickWithSense:(ZTMSense *)sense delegate:(id<SenseCellDelegate>)delegate {
//跳轉(zhuǎn)用戶個人中心
[self onNavigationToPersonWithUser:sense.user];
}
- (void)onNavigationToPersonWithUser:(ZTMUserDescription *)user {
[[ZingManager getCurrentViewController] gotoProfileWithUserId:user.id_p];
}
5.在對應(yīng)的模塊實例對應(yīng)的策略類,讓cell持有對應(yīng)策略類,調(diào)用對應(yīng)具體策略類的實現(xiàn)
(1)頻道詳情的頁面,調(diào)用展示用戶信息的策略類
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ZTMSenseLayout *layout;
if (indexPath.section == 0) {
layout = self.draftLayouts[indexPath.row];
}else{
layout = self.senseLayouts[indexPath.row];
}
SenseCell *cell = [SenseCell cellWithTableView:tableView delegate:self type:layout.sense.content.mediaType headerDisplayStrategy:[SenseHeaderDisplay4Personal defaultDisplayStrategy]];
[layout setIndexPath:indexPath];
if (layout.tableView != tableView) layout.tableView = tableView;
[cell layoutSubviewsWithLayout:layout];
return cell;
}
(2)個人中心的頁面,調(diào)用展示頻道信息的策略類
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ZTMSenseLayout *layout;
if (indexPath.section == 0) {
layout = self.draftLayouts[indexPath.row];
} else {
layout = self.senseLayouts[indexPath.row];
}
SenseCell *cell = [SenseCell cellWithTableView:tableView delegate:self type:layout.sense.content.mediaType headerDisplayStrategy:[SenseHeaderDisplay4Channel defaultDisplayStrategy]];
[layout setIndexPath:indexPath];
if (layout.tableView != tableView) layout.tableView = tableView;
[cell layoutSubviewsWithLayout:layout];
return cell;
}
6.在cell布局的時候調(diào)用策略類
/** 頭部顯示策略 */
@property (nonatomic, strong) SenseHeaderDisplayStrategy *headerDisplayStrategy;
/**
布局
@param layout 布局
*/
- (void)layoutSubviewsWithLayout:(ZTMSenseLayout *)layout {
[super layoutSubviewsWithLayout:layout];
//設(shè)置頭像,調(diào)用策略類
[self.avatar_imageView sd_setImageWithURL:[ZingFileManager getPortraitURLWithString:[self.headerDisplayStrategy getIconUrlWithSense:layout.sense]] placeholderImage:[UIImage avatarPlaceholder] options:SDWebImageLowPriority];
//設(shè)置名字,調(diào)用策略類
_userName_label.text = [_headerDisplayStrategy getTitleTextWithSense:layout.sense];
}
[self.avatar_imageView addTapGestureBlock:^{
//點擊頭像,調(diào)用策略類
[weak_self.headerDisplayStrategy triggerIconClickWithSense:weak_self.sense delegate:weak_self.delegate];
}];
6.優(yōu)缺點
優(yōu)點:
1、 策略模式提供了管理相關(guān)的算法族的辦法。策略類的等級結(jié)構(gòu)定義了一個算法或行為族。恰當(dāng)使用繼承可以把公共的代碼轉(zhuǎn)移到父類里面,從而避免重復(fù)的代碼。
2、 策略模式提供了可以替換繼承關(guān)系的辦法。繼承可以處理多種算法或行為。如果不是用策略模式,那么使用算法或行為的環(huán)境類就可能會有一些子類,每一個子類提供一個不同的算法或行為。但是,這樣一來算法或行為的使用者就和算法或行為本身混在一起。決定使用哪一種算法或采取哪一種行為的邏輯就和算法或行為的邏輯混合在一起,從而不可能再獨立演化。繼承使得動態(tài)改變算法或行為變得不可能。
3、 使用策略模式可以避免使用多重條件轉(zhuǎn)移語句。多重轉(zhuǎn)移語句不易維護(hù),它把采取哪一種算法或采取哪一種行為的邏輯與算法或行為的邏輯混合在一起,統(tǒng)統(tǒng)列在一個多重轉(zhuǎn)移語句里面,比使用繼承的辦法還要原始和落后。
缺點:
1、客戶端必須知道所有的策略類,并自行決定使用哪一個策略類。這就意味著客戶端必須理解這些算法的區(qū)別,以便適時選擇恰當(dāng)?shù)乃惴?。換言之,策略模式只適用于客戶端知道所有的算法或行為的情況。
2、 策略模式造成很多的策略類,每個具體策略類都會產(chǎn)生一個新類。有時候可以通過把依賴于環(huán)境的狀態(tài)保存到客戶端里面,而將策略類設(shè)計成可共享的,這樣策略類實例可以被不同客戶端使用。