iOS 開發(fā)中的 Self-Manager 模式

Self-Manager源于我們團(tuán)隊內(nèi)部的黑話,“誒?你剛?cè)サ膭?chuàng)業(yè)公司有幾個 iOS 開發(fā)啊?” “就我一個” “靠,你這是 Self-Manager 啊”
最近,這個思路被我們當(dāng)做了一種設(shè)計模式,即賦予一個 Widget 更大的權(quán)利,讓其自己負(fù)責(zé)自己的事件。舉個簡單的栗子,這種負(fù)責(zé)展示頭像的視圖:

sina.png

它的職責(zé)包括:
1.通過傳入的 URL,加載并展示頭像圖片
2.顯示一些附屬信息,比如大V的標(biāo)志
3.將用戶點擊頭像的事件傳遞給外層的 View Controller 跳轉(zhuǎn)到用戶信息頁面

于是乎這個 Widget 的 API 可以長這個樣子:

@interface FDAvatarView : UIView
// 假設(shè) VIPInfo 是某個 Entity
- (void)configureWithAvatarURL:(NSURL *)URL VIPInfo:(id)info tapped:(void (^)(void))block;
@end

使用這個控件的人只需要調(diào)用這個 configure 方法就可以配置入?yún)⒑褪录幚怼5S之而來的就是一些蛋疼的問題:

1.configure 的調(diào)用者是 superview,上面的例子中也就是一個 UITableViewCell,但 Cell 這層并不知道自己的 ViewController 是誰,于是乎還得向上一級傳遞這個點擊事件,直到能獲取到 NavigationController,然后 Push 一個用戶信息的頁面。

2.這個 Avatar View 在 App 的各個地方都可能粗線,而且行為一致,那就意味著事件處理的 block,要散落在各個頁面中,同時也帶來了很多“只是為向上一層級轉(zhuǎn)發(fā)事件”的 “Middle Man”

為解決這個問題,就需要給這個 View 放權(quán),讓其自己 Handle 自己的事件,也就是 Self-Managed
,為了不破壞 View 的純潔性,比較好的實踐是在 Category 中實現(xiàn):

@interface FDAvatarView (FDAvatarViewSelfManager)
- (void)selfManagedConfigureWithAvatarURL:(NSURL *)URL VIPInfo:(id)info uid:(NSString *)uid;
@end

實現(xiàn)時最好要調(diào)用 View 主類提供的 API:

@implementation FDAvatarView (FDAvatarViewSelfManager)
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'PingFang SC'; color: #4cbf57}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #ffffff}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #4cbf57}span.s1 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s2 {font-variant-ligatures: no-common-ligatures}span.s3 {font-variant-ligatures: no-common-ligatures; color: #c2349b}span.s4 {font-variant-ligatures: no-common-ligatures; color: #00afca}span.s5 {font-variant-ligatures: no-common-ligatures; color: #ffffff}span.s6 {font: 14.0px 'PingFang SC'; font-variant-ligatures: no-common-ligatures}span.s7 {font-variant-ligatures: no-common-ligatures; color: #4cbf57}span.s8 {font: 14.0px 'PingFang SC'; font-variant-ligatures: no-common-ligatures; color: #4cbf57}

// 為后一個頁面的創(chuàng)建增加了個 UID 參數(shù)
- (void)selfManagedConfigureWithAvatarURL:(NSURL *)URL VIPInfo:(id)info UID:(NSString *)UID {
    [self configureWithAvatarURL:URL VIPInfo:info tapped:^{
    // 假設(shè) App 結(jié)構(gòu)是 Root -> TabBar -> Navigation -> ViewController
    UITabBarController *tabBarControler = (id)UIApplication.sharedApplication.delegate.window.rootViewController;
    UINavigationController *navigationController = tabBarControler.selectedViewController;
                                               // 創(chuàng)建用戶信息 View Controller
    FDUserProfileViewController *profileViewController = [FDUserProfileViewController viewControllerWithUID:UID];
    [navigationController pushViewController:profileViewController animated:YES];
    }];
}
@end

這里用到了類似 AOP 的思路,添加了對 App 層級的耦合,如果覺得這樣的耦合方式不妥的話,也可以封裝個全局方法去取到當(dāng)前頂層的 Navigation Controller。
這樣,F(xiàn)DAvatarView 的調(diào)用者只需要配置入?yún)?,其余的它自己全能搞定了,即?App 內(nèi)很多處出現(xiàn)頭像,邏輯代碼也只有一份。

接下來再來個例子:

Zambia.png

這個點贊的按鈕功能上有幾個職責(zé):

1.顯示已有的點贊數(shù)
2.點擊按鈕后執(zhí)行一個小動畫,點贊數(shù) +1,同時發(fā)送網(wǎng)絡(luò)請求。
3.若已經(jīng)點贊,點擊執(zhí)行反向操作
4.若網(wǎng)絡(luò)請求發(fā)送失敗,則回退成點擊前的狀態(tài)

這個控件的 API 可以設(shè)計成這樣:

@interface FDLikeButton : UIButton
- (void)configureLikeStatus:(BOOL)likeOrNot count:(NSInteger)count animated:(BOOL)animated;
@end

因為繼承自 UIButton,所以外部可以直接設(shè)置其 action,就不增加 tappedHandler 的參數(shù)了。外部在點擊事件中需要調(diào)用這個配置方法,播放點贊動畫,緊接著發(fā)送一個網(wǎng)絡(luò)請求,若網(wǎng)絡(luò)請求失敗,可以再次調(diào)用這個 API 的無動畫版本回滾狀態(tài)。但像上一個例子一樣,網(wǎng)絡(luò)請求和事件處理邏輯相同,但代碼卻分部在各個頁面中,于是給這個 View 增加 Self-Managed 模式的 Category:

@interface FDLikeButton (FDLikeButtonSelfManager)
- (void)selfManagedConfigureWithLikeStatus:(BOOL)likeOrNot count:(NSInteger)count;
@end

偽代碼的實現(xiàn)如下:

@implementation FDLikeButton (FDLikeButtonSelfManager)
- (void)selfManagedConfigureWithLikeStatus:(BOOL)likeOrNot count:(NSInteger)count {
    [self configureLikeStatus:likeOrNot count:count animated:NO];
    [self addTarget:self action:@selector(likeButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)likeButtonTapped:(id)sender {
    // +1 or -1 with animation
    // Network request ^(NSError *error) {
    //     if (error) {
    //         rollback
    //     }
    // }
}
@end

從設(shè)計上,Self-Manager 模式并沒有破壞原有的 MVC 結(jié)構(gòu),上面兩個例子中的 View 依然可以不耦合具體業(yè)務(wù)邏輯的單拿出來用。使用 Category 的方式把應(yīng)該寫在 ViewController 中的代碼移動到 View 的文件中,讓功能更加的內(nèi)聚。

程序的復(fù)雜度并不會因哪種酷炫的設(shè)計模式所減少,能做到的只是對復(fù)雜度的切分和控制,即:
1.讓一大坨惡心的代碼變成幾小坨不那么惡心的代碼。
2.讓惡心的代碼只在一個地方惡心。

Self-Manager 模式我們實踐的時候?qū)懫饋砗荛_心,拋磚引玉一下,希望也能解決你的苦惱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容