[貝聊科技]iOS 代碼架構(gòu)(一)如何創(chuàng)建一個(gè)易復(fù)用的組件

作者 子豪 貝聊iOS工程師

前言

貝聊的移動(dòng)客戶(hù)端分別有家長(zhǎng)端和老師端诲锹,一家公司里同時(shí)維護(hù)多個(gè)業(yè)務(wù)上有關(guān)聯(lián)性的app這種情況其實(shí)很常見(jiàn)牙捉,例如一些提供 O2O 服務(wù)的公司,經(jīng)常會(huì)分用戶(hù)端和商家端恼五。這些客戶(hù)端雖然各自負(fù)責(zé)著一個(gè)業(yè)務(wù)環(huán)里面的不同部分蓝角,看似不相關(guān)阱穗,但其實(shí)內(nèi)在的設(shè)計(jì)、代碼都有很多共同之處使鹅。

我們編寫(xiě)代碼時(shí)一條最重要的軍規(guī)是 DRY (Don't Repeat Yourself)揪阶,意思就是同樣或者相似的代碼只寫(xiě)一次,通過(guò)代碼復(fù)用的技巧做成公用的組件患朱。項(xiàng)目工期緊張時(shí)鲁僚,其他的一些編碼守則都可以稍微變通一下,但唯獨(dú) DRY 是絕對(duì)要遵守的。這樣做最大的好處是當(dāng)發(fā)生需求變更冰沙、重構(gòu)或者修復(fù) bug 時(shí)侨艾,只要改動(dòng)一處的代碼就可以了。如果采用到處 copy 代碼的方式拓挥,則需要在每一處引用到的地方做修改蒋畜,很容易就會(huì)出現(xiàn)遺漏。并且時(shí)間一長(zhǎng)撞叽,這些復(fù)制的代碼很容易會(huì)漸行漸遠(yuǎn),衍生出許多不同的分支插龄,維護(hù)難度呈指數(shù)級(jí)上升愿棋。稍有經(jīng)驗(yàn)的程序員應(yīng)該都知道到處拷代碼就是挖坑的開(kāi)始,本文以一個(gè)較簡(jiǎn)單的 UI 組件為例均牢,介紹貝聊 iOS 組在設(shè)計(jì)可復(fù)用組件時(shí)的一點(diǎn)小技巧糠雨。

遇到的問(wèn)題

貝聊的家長(zhǎng)版和老師版針對(duì)的受眾不同,設(shè)計(jì)語(yǔ)言徘跪、配色等方面也有點(diǎn)不同甘邀,以最簡(jiǎn)單的自定義提示框?yàn)槔?/p>

家長(zhǎng)版:

老師版:

主要的不同點(diǎn):

  1. 按鈕的圓角半徑 (cornerRadius)
  2. 按鈕的大小、位置 (frame)
  3. 文字的字號(hào) (fontSize)
  4. 文字內(nèi)容到提示框邊界的距離 (contentInsets)
  5. 其實(shí)之前連按鈕的顏色都不一樣垮庐,不過(guò)最近UI改版了

初看起來(lái)不同點(diǎn)很多松邪,但仔細(xì)看其實(shí)只是一些設(shè)計(jì)上的元素有不同。事實(shí)上家長(zhǎng)版和老師版的提示框其實(shí)底層用的都是同一套代碼哨查,這個(gè)彈框組件BLAlertController是我們 iOS 組一個(gè)新入行的小伙寫(xiě)的逗抑,很好地遵守了 DRY 原則,靈活性和代碼質(zhì)量都非常高寒亥。本文就用這個(gè)組件為例來(lái)說(shuō)說(shuō)邮府,怎樣在多個(gè) app 之間優(yōu)雅地復(fù)用代碼。

創(chuàng)建一個(gè)配置類(lèi)

先來(lái)看看初始化方法溉奕,alertController的命名是仿照系統(tǒng)的UIAlertController褂傀,但是因?yàn)?UI 是高度可定制的,所以多加入了很多參數(shù)加勤。

+ (instancetype)alertControllerWithTitle:(NSString *)title 
                                         message:(NSString *)message
                               buttonTextColor:(UIColor *)textColor
                        buttonBackgroundColor:(UIColor *)buttonBackgroundColor
                                  cornerRadius:(CGFloat)cornerRadius
                   ....  // 篇幅原因仙辟,點(diǎn)擊回調(diào)和其他配置項(xiàng)都省略,全部列出來(lái)的話超過(guò)二十項(xiàng)

這里遇到的第一個(gè)問(wèn)題就是參數(shù)列表過(guò)長(zhǎng)胸竞,Objective-C 沒(méi)有默認(rèn)參數(shù)也沒(méi)有方法重載欺嗤,如果每次初始化都要填寫(xiě)這一大堆參數(shù),這樣的組件也未免太難用了卫枝。

其實(shí) iOS SDK 的代碼里面就有很多優(yōu)秀的設(shè)計(jì)模式的應(yīng)用范例煎饼,遇到問(wèn)題的時(shí)候參考一下,會(huì)有很多收獲校赤。這里遇到的問(wèn)題主要是代碼架構(gòu)的問(wèn)題吆玖,發(fā)散一下筒溃,發(fā)現(xiàn) Foundation 框架的 NSURLSession也是有很多可配置的屬性的。蘋(píng)果的工程師把這些可選參數(shù)專(zhuān)門(mén)構(gòu)造成了一個(gè)NSURLSessionConfiguration來(lái)管理這些可配置屬性沾乘。創(chuàng)建一個(gè)NSURLSession時(shí)怜奖,需要傳入一個(gè)NSURLSessionConfiguration來(lái)指定一些參數(shù),而NSURLSessionConfiguration的大部分屬性都是有默認(rèn)值的翅阵,例如timeoutIntervalForRequest歪玲。通過(guò)NSURLSessionConfiguration.defaultSessionConfiguration方法可以創(chuàng)建一個(gè)默認(rèn)的 configuration,此時(shí)timeoutIntervalForRequest的默認(rèn)值是60掷匠,這個(gè)值能適用于大部分情況滥崩。如果有特殊的需求也可以自行調(diào)整。

我們?cè)?9%的情況下其實(shí)都只是想用默認(rèn)樣式的彈框讹语,這時(shí)創(chuàng)建一個(gè)可定制的钙皮,帶默認(rèn)值的配置類(lèi)就是很好的解決方法。

依葫蘆畫(huà)瓢顽决,我們也創(chuàng)建一個(gè)BLAlertConfiguration短条,定義大致如下:

@interface BLAlertConfiguration : NSObject <NSCopying> // 配置類(lèi)實(shí)現(xiàn)了深拷貝

@property (nonatomic) UIColor *buttonTextColor;
@property (nonatomic) UIColor *buttonBackgroundColor;
@property (nonatomic) CGFloat cornerRadius;

// 默認(rèn)的配置項(xiàng)
@property (class, nonatomic) BLAlertConfiguration *defaultConfiguration;

... //其他可配置項(xiàng)由于篇幅原因不一一列舉了

@end

@interface BLAlertController : UIViewController

- (instancetype)initWithTitle:(NSString *)title
                      message:(NSString *)message
                configuration:(BLAlertConfiguration *)configuration;
      
 - (instancetype)initWithTitle:(NSString *)title
                       message:(NSString *)message;
                       
@end

BLAlertController 有兩個(gè)初始化方法,initWithTitle:message: 是個(gè) convenience initializer才菠,內(nèi)部調(diào)用了 initWithTitle:message:configuration:并把BLAlertConfiguration.defaultConfiguration傳進(jìn)去了茸时。所以一般的使用就很簡(jiǎn)單了,直接調(diào)用initWithTitle:message:就好赋访。

在不同的項(xiàng)目中設(shè)置不同的默認(rèn)值

上面解決了參數(shù)列表過(guò)長(zhǎng)的問(wèn)題屹蚊,但是還是沒(méi)有說(shuō)明在兩個(gè)項(xiàng)目中怎么設(shè)置不同的默認(rèn) UI 風(fēng)格。答案其實(shí)呼之欲出进每,聰明的讀者應(yīng)該已經(jīng)想到了汹粤。

BLAlertConfiguration.defaultConfiguration 這個(gè)屬性是 Objective-C 新加的 class property 語(yǔ)法,用來(lái)打通 Swift 的類(lèi)屬性田晚。我們可以通過(guò)靜態(tài)變量和 getter setter嘱兼,把 defaultConfiguration 變成一個(gè)可讀可寫(xiě)的類(lèi)屬性。

@implementation BLAlertConfiguration

static BLAlertConfiguration *defaultConfiguration;

+ (void)setDefaultAlertConfiguration:(BLAlertConfiguration *)configuration {
    if (defaultConfiguration) { //只允許設(shè)置一次贤徒,有值的時(shí)候返回
        return;
    }
    defaultConfiguration = [configuration copy]; // 通過(guò)拷貝對(duì)象芹壕,避免配置項(xiàng)后面被修改
}

+ (instancetype)defaultConfiguration {
    NSAssert(defaultConfiguration, @"未設(shè)置 defaultConfiguration,應(yīng)先調(diào)用 +[BLAlertConfiguration setDefaultAlertConfiguration:] 來(lái)進(jìn)行初始化");
    return defaultConfiguration;
}

@end

這樣只要在程序啟動(dòng)的時(shí)候接奈,例如在 AppDelegate 的application:didFinishLaunchingWithOptions:回調(diào)中設(shè)置一下 BLAlertConfiguration.defaultConfiguration 就可以了踢涌,在不同的項(xiàng)目中設(shè)置不同的默認(rèn)值,就能達(dá)成不同的設(shè)計(jì)風(fēng)格序宦。

BLAlertConfiguration *configuration = [BLAlertConfiguration new];
configuration.buttonTextColor = [UIColor blackColor];
configuration.buttonBackgroundColor = [UIColor yellowColor];
configuration.cornerRadius = 4.0;

BLAlertConfiguration.defaultAlertConfiguration = configuration;

結(jié)語(yǔ)

本文作為系統(tǒng)的首篇和引子睁壁,內(nèi)容相對(duì)簡(jiǎn)單,但是很好地體現(xiàn)了 DRY 的精神。如果你很少接觸這類(lèi)問(wèn)題潘明,這會(huì)是一個(gè)很好的開(kāi)始行剂。學(xué)會(huì)發(fā)現(xiàn)代碼中的壞味道,思考改進(jìn)的方法钳降,保持項(xiàng)目整潔是提升架構(gòu)設(shè)計(jì)能力的必經(jīng)之路厚宰。這種代碼復(fù)用的手法目前已經(jīng)貫穿了我們整個(gè)公共代碼庫(kù),并且有很多變體遂填,后面會(huì)陸續(xù)地介紹貝聊項(xiàng)目中其他關(guān)于代碼復(fù)用方面的心得铲觉,敬請(qǐng)期待。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吓坚,一起剝皮案震驚了整個(gè)濱河市备燃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凌唬,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漏麦,死亡現(xiàn)場(chǎng)離奇詭異客税,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)撕贞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)更耻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人捏膨,你說(shuō)我怎么就攤上這事秧均。” “怎么了号涯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵目胡,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我链快,道長(zhǎng)誉己,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任域蜗,我火速辦了婚禮巨双,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霉祸。我一直安慰自己筑累,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布丝蹭。 她就那樣靜靜地躺著慢宗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上婆廊,一...
    開(kāi)封第一講書(shū)人閱讀 48,954評(píng)論 1 283
  • 那天迅细,我揣著相機(jī)與錄音,去河邊找鬼淘邻。 笑死茵典,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宾舅。 我是一名探鬼主播统阿,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼筹我!你這毒婦竟也來(lái)了扶平?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蔬蕊,失蹤者是張志新(化名)和其女友劉穎结澄,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體岸夯,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡麻献,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了猜扮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勉吻。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旅赢,靈堂內(nèi)的尸體忽然破棺而出齿桃,到底是詐尸還是另有隱情,我是刑警寧澤煮盼,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布短纵,位于F島的核電站,受9級(jí)特大地震影響僵控,放射性物質(zhì)發(fā)生泄漏踩娘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一喉祭、第九天 我趴在偏房一處隱蔽的房頂上張望养渴。 院中可真熱鬧,春花似錦泛烙、人聲如沸理卑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)藐唠。三九已至帆疟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宇立,已是汗流浹背踪宠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妈嘹,地道東北人柳琢。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像润脸,于是被迫代替她去往敵國(guó)和親柬脸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容