在現(xiàn)有工程中實施基于CTMediator的組件化方案

http://casatwy.com/modulization_in_action.html

前述

國內(nèi)業(yè)界大家對組件化的討論從今年年初開始到年尾掂骏,不外乎兩個方案:URL/protocol注冊調(diào)度土浸,runtime調(diào)度褐隆。

我之前批評過URL注冊調(diào)度是錯誤的組件化實施方案,在所有的基于URL注冊調(diào)度的方案中吧恃,存在兩個普遍問題:

命名域滲透

因注冊是不必要的嫡良,而帶來同樣不必要的注冊列表維護(hù)成本

其它各家的基于URL注冊的不同方案在這兩個普遍問題上還有各種各樣的其他問題,例如FRDIntent庫中的FRDIntent對象其本質(zhì)是雞肋對象本谜、原屬于響應(yīng)者的業(yè)務(wù)被滲透到調(diào)用者的業(yè)務(wù)中初家、組件化實施方案的過程中會產(chǎn)生對原有代碼的侵入式修改等問題。

另外乌助,我也發(fā)現(xiàn)還是有人在都沒有理解清楚的前提下就做出了自己的解讀溜在,流毒甚廣。我之前寫過關(guān)于CTMediator比較理論的描述他托,也有Demo掖肋,但惟獨沒有寫實踐方面的描述。我本來以為Demo就足夠了上祈,可現(xiàn)在看來還是要給一篇實踐的文章的培遵。

在更早之前,卓同學(xué)的swift老司機(jī)群里也有人提出因為自己并沒有理解透徹CTMediator方案登刺,所以不敢貿(mào)然直接在項目中應(yīng)用籽腕。所以這篇文章的另一個目的也是希望能夠讓大家明白,基于CTMediator的組件化方案實施其實非常簡單纸俭,而且也是有章法可循的皇耗。這篇文章可能會去討論一些理論的東西,但主要還會是以實踐為主揍很。爭取做到能夠讓大家看完文章之后就可以直接在自己的項目中順利實施組件化郎楼。

最后,我希望這篇文章能夠終結(jié)業(yè)界持續(xù)近一年的關(guān)于組件化方案的無謂討論和錯誤討論窒悔。

準(zhǔn)備工作

我在github上開了一個orgnization呜袁,里面有一個主工程:MainProject,我們要針對這個工程來做組件化简珠。組件化實施完畢之后的主工程就是ModulizedMainProject了阶界。抽出來的獨立Pod、私有Pod源也都會放在這個orgnization中去聋庵。

在一個項目實施組件化方案之前膘融,我們需要做一個準(zhǔn)備工作,建立自己的私有Pod源和快手工具腳本的配置:

先去開一個repo祭玉,這個repo就是我們私有Pod源倉庫

pod repo add [私有Pod源倉庫名字] [私有Pod源的repo地址]

創(chuàng)立一個文件夾氧映,例如Project。把我們的主工程文件夾放到Project下:~/Project/MainProject

在~/Project下clone快速配置私有源的腳本repo:git clone git@github.com:casatwy/ConfigPrivatePod.git

將ConfigPrivatePod的template文件夾下Podfile中source 'https://github.com/ModulizationDemo/PrivatePods.git'改成第一步里面你自己的私有Pod源倉庫的repo地址

將ConfigPrivatePod的template文件夾下upload.sh中PrivatePods改成第二步里面你自己的私有Pod源倉庫的名字

最后你的文件目錄結(jié)構(gòu)應(yīng)該是這樣:

Project

├── ConfigPrivatePod

└── MainProject

到此為止脱货,準(zhǔn)備工作就做好了岛都。

實施組件化方案第一步:創(chuàng)建私有Pod工程和Category工程

MainProject是一個非常簡單的應(yīng)用律姨,一共就三個頁面。首頁push了AViewController臼疫,AViewController里又push了BViewController线召。我們可以理解成這個工程由三個業(yè)務(wù)組成:首頁、A業(yè)務(wù)多矮、B業(yè)務(wù)缓淹。

我們這一次組件化的實施目標(biāo)就是把A業(yè)務(wù)組件化出來,首頁和B業(yè)務(wù)都還放在主工程塔逃。

因為在實際情況中讯壶,組件化是需要循序漸進(jìn)地實施的。尤其是一些已經(jīng)比較成熟的項目湾盗,業(yè)務(wù)會非常多伏蚊,一時半會兒是不可能完全組件化的。CTMediator方案在實施過程中格粪,對主工程業(yè)務(wù)的影響程度極小躏吊,而且是能夠支持循序漸進(jìn)地改造方式的。這個我會在文章結(jié)尾做總結(jié)的時候提到帐萎。

既然要把A業(yè)務(wù)抽出來作為組件比伏,那么我們需要為此做兩個私有Pod:A業(yè)務(wù)Pod(以后簡稱A Pod)、方便其他人調(diào)用A業(yè)務(wù)的CTMediator category的Pod(以后簡稱A_Category Pod)疆导。這里多解釋一句:A_Category Pod本質(zhì)上只是一個方便方法赁项,它對A Pod不存在任何依賴。

我們先創(chuàng)建A Pod

新建XCode工程澈段,命名為A悠菜,放到Projects下

新建Repo,命名也為A败富,新建好了之后網(wǎng)頁不要關(guān)掉

此時你的文件目錄結(jié)構(gòu)應(yīng)該是這樣:

Project

├── ConfigPrivatePod

├── MainProject

└── A

然后cd到ConfigPrivatePod下悔醋,執(zhí)行./config.sh腳本來配置A這個私有Pod。腳本會問你要一些信息兽叮,Project Name就是A芬骄,要跟你的A工程的目錄名一致。HTTPS Repo充择、SSH Repo網(wǎng)頁上都有德玫,Home Page URL就填你A Repo網(wǎng)頁的URL就好了匪蟀。

這個腳本是我寫來方便配置私有庫的腳本椎麦,pod lib create也可以用,但是它會直接從github上拉一個完整的模版工程下來材彪,只是國內(nèi)訪問github其實會比較慢观挎,會影響效率琴儿。而且這個配置工作其實也不復(fù)雜,我就索性自己寫了個腳本嘁捷。

這個腳本要求私有Pod的文件目錄要跟腳本所在目錄平級造成,也會在XCode工程的代碼目錄下新建一個跟項目同名的目錄。放在這個目錄下的代碼就會隨著Pod的發(fā)版而發(fā)出去雄嚣,這個目錄以外的代碼就不會跟隨Pod的版本發(fā)布而發(fā)布晒屎,這樣子寫用于測試的代碼就比較方便。

然后我們在主工程中缓升,把屬于A業(yè)務(wù)的代碼拎出來鼓鲁,放到新建好的A工程的A文件夾里去,然后拖放到A工程中港谊。原來主工程里面A業(yè)務(wù)的代碼直接刪掉骇吭,此時主工程和A工程編譯不過都是正常的,我們會在第二步中解決主工程的編譯問題歧寺,第三步中解決A工程的編譯問題燥狰。

此時你的主工程應(yīng)該就沒有A業(yè)務(wù)的代碼了,然后你的A工程應(yīng)該是這樣:

A

├── A

| ??├── A

| ??│?? ├── AViewController.h

| ??│?? └── AViewController.m

| ??├── AppDelegate.h

| ??├── AppDelegate.m

| ??├── ViewController.h

| ??├── ViewController.m

| ??└── main.m

└── A.xcodeproj

我們再創(chuàng)建A_Category Pod

同樣的斜筐,我們再創(chuàng)建A_Category龙致,因為它也是個私有Pod,所以也照樣子跑一下config.sh腳本去配置一下就好了顷链。最后你的目錄結(jié)構(gòu)應(yīng)該是這樣的:

Project

├── A

│?? ├── A

│?? │?? ├── A

│?? │?? ├── AppDelegate.h

│?? │?? ├── AppDelegate.m

│?? │?? ├── Assets.xcassets

│?? │?? ├── Info.plist

│?? │?? ├── ViewController.h

│?? │?? ├── ViewController.m

│?? │?? └── main.m

│?? ├── A.podspec

│?? ├── A.xcodeproj

│?? ├── FILE_LICENSE

│?? ├── Podfile

│?? ├── readme.md

│?? └── upload.sh

├── A_Category

│?? ├── A_Category

│?? │?? ├── A_Category

│?? │?? ├── AppDelegate.h

│?? │?? ├── AppDelegate.m

│?? │?? ├── Info.plist

│?? │?? ├── ViewController.h

│?? │?? ├── ViewController.m

│?? │?? └── main.m

│?? ├── A_Category.podspec

│?? ├── A_Category.xcodeproj

│?? ├── FILE_LICENSE

│?? ├── Podfile

│?? ├── readme.md

│?? └── upload.sh

├── ConfigPrivatePod

│?? ├── config.sh

│?? └── templates

└── MainProject

├── FILE_LICENSE

├── MainProject

├── MainProject.xcodeproj

├── MainProject.xcworkspace

├── Podfile

├── Podfile.lock

├── Pods

└── readme.md

然后去A_Category下净当,在Podfile中添加一行pod "CTMediator",在podspec文件的后面添加s.dependency "CTMediator"蕴潦,然后執(zhí)行pod install --verbose像啼。

接下來打開A_Category.xcworkspace,把腳本生成的名為A_Category的空目錄拖放到Xcode對應(yīng)的位置下潭苞,然后在這里新建基于CTMediator的Category:CTMediator+A忽冻。最后你的A_Category工程應(yīng)該是這樣的:

A_Category

├── A_Category

| ??├── A_Category

| ??│?? ├── CTMediator+A.h

| ??│?? └── CTMediator+A.m

| ??├── AppDelegate.h

| ??├── AppDelegate.m

| ??├── ViewController.h

| ??└── ViewController.m

└── A_Category.xcodeproj

到這里為止,A工程和A_Category工程就準(zhǔn)備好了此疹。

實施組件化方案第二步:在主工程中引入A_Category工程僧诚,并讓主工程編譯通過

去主工程的Podfile下添加pod "A_Category", :path => "../A_Category"來本地引用A_Category。

然后編譯一下蝗碎,說找不到AViewController的頭文件湖笨。此時我們把頭文件引用改成#import 。

然后繼續(xù)編譯蹦骑,說找不到AViewController這個類型慈省。看一下這里是使用了AViewController的地方眠菇,于是我們在Development Pods下找到CTMediator+A.h边败,在里面添加一個方法:

- (UIViewController *)A_aViewController;

再去CTMediator+A.m中袱衷,補(bǔ)上這個方法的實現(xiàn),把主工程中調(diào)用的語句作為注釋放進(jìn)去笑窜,將來寫Target-Action要用:

-(UIViewController*)A_aViewController{/*AViewController *viewController = [[AViewController alloc] init];*/return[selfperformTarget:@"A"action:@"viewController"params:nilshouldCacheTarget:NO];}

補(bǔ)充說明一下致燥,performTarget:@"A"中給到的@"A"其實是Target對象的名字。一般來說排截,一個業(yè)務(wù)Pod只需要有一個Target就夠了嫌蚤,但一個Target下可以有很多個Action。Action的名字也是可以隨意命名的断傲,只要到時候Target對象中能夠給到對應(yīng)的Action就可以了搬葬。

關(guān)于Target-Action我們會在第三步中去實現(xiàn),現(xiàn)在不實現(xiàn)Target-Action是不影響主工程編譯的艳悔。

category里面這么寫就已經(jīng)結(jié)束了急凰,后面的實施過程中就不會再改動到它了。

然后我們把主工程調(diào)用AViewController的地方改為基于CTMediator Category的實現(xiàn):

UIViewController*viewController=[[CTMediatorsharedInstance]A_aViewController];[self.navigationControllerpushViewController:viewControlleranimated:YES];

再編譯一下猜年,編譯通過抡锈。

到此為止主工程就改完了,現(xiàn)在跑主工程點擊這個按鈕跳不到A頁面是正常的乔外,因為我們還沒有在A工程中實現(xiàn)Target-Action床三。

而且此時主工程中關(guān)于A業(yè)務(wù)的改動就全部結(jié)束了,后面的組件化實施過程中杨幼,就不會再有針對A業(yè)務(wù)線對主工程的改動了撇簿。

實施組件化方案第三步:添加Target-Action,并讓A工程編譯通過

此時我們關(guān)掉所有XCode窗口差购。然后打開兩個工程:A_Category工程和A工程四瘫。

我們在A工程中創(chuàng)建一個文件夾:Targets,然后看到A_Category里面有performTarget:@"A"欲逃,所以我們新建一個對象找蜜,叫做Target_A。

然后又看到對應(yīng)的Action是viewController稳析,于是在Target_A中新建一個方法:Action_viewController洗做。這個Target對象是這樣的:

頭文件:#import @interfaceTarget_A:NSObject-(UIViewController*)Action_viewController:(NSDictionary*)params;@end實現(xiàn)文件:#import "Target_A.h"#import "AViewController.h"@implementationTarget_A-(UIViewController*)Action_viewController:(NSDictionary*)params{AViewController*viewController=[[AViewControlleralloc]init];returnviewController;}@end

這里寫實現(xiàn)文件的時候,對照著之前在A_Category里面的注釋去寫就可以了彰居。

因為Target對象處于A的命名域中诚纸,所以Target對象中可以隨意import A業(yè)務(wù)線中的任何頭文件。

另外補(bǔ)充一點陈惰,Target對象的Action設(shè)計出來也不是僅僅用于返回ViewController實例的畦徘,它可以用來執(zhí)行各種屬于業(yè)務(wù)線本身的任務(wù)。例如上傳文件,轉(zhuǎn)碼等等各種任務(wù)其實都可以作為一個Action來給外部調(diào)用旧烧,Action完成這些任務(wù)的時候,業(yè)務(wù)邏輯是可以寫在Action方法里面的画髓。

換個角度說就是:Action具備調(diào)度業(yè)務(wù)線提供的任何對象和方法來完成自己的任務(wù)的能力掘剪。它的本質(zhì)就是對外業(yè)務(wù)的一層服務(wù)化封裝。

現(xiàn)在我們這個Action要完成的任務(wù)只是實例化一個ViewController并返回出去而已奈虾,根據(jù)上面的描述夺谁,Action可以完成的任務(wù)其實可以更加復(fù)雜。

然后我們再繼續(xù)編譯A工程肉微,發(fā)現(xiàn)找不到BViewController匾鸥。由于我們這次組件化實施的目的僅僅是將A業(yè)務(wù)線抽出來,BViewController是屬于B業(yè)務(wù)線的碉纳,所以我們沒必要把B業(yè)務(wù)也從主工程里面抽出來勿负。但為了能夠讓A工程編譯通過,我們需要提供一個B_Category來使得A工程可以調(diào)度到B劳曹,同時也能夠編譯通過奴愉。

B_Category的創(chuàng)建步驟跟A_Category是一樣的,不外乎就是這幾步:新建Xcode工程铁孵、網(wǎng)頁新建Repo锭硼、跑腳本配置Repo、添加Category代碼蜕劝。

B_Category添加好后檀头,我們同樣在A工程的Podfile中本地指過去,然后跟在主工程的時候一樣岖沛。

所以B_Category是這樣的:

頭文件:#import #import @interfaceCTMediator(B)-(UIViewController*)B_viewControllerWithContentText:(NSString*)contentText;@end實現(xiàn)文件:#import "CTMediator+B.h"@implementationCTMediator(B)-(UIViewController*)B_viewControllerWithContentText:(NSString*)contentText{/*BViewController *viewController = [[BViewController alloc] initWithContentText:@"hello, world!"];*/NSMutableDictionary*params=[[NSMutableDictionaryalloc]init];params[@"contentText"]=contentText;return[selfperformTarget:@"B"action:@"viewController"params:paramsshouldCacheTarget:NO];}@end

然后我們對應(yīng)地在A工程中修改頭文件引用為#import 暑始,并且把調(diào)用的代碼改為:

UIViewController*viewController=[[CTMediatorsharedInstance]B_viewControllerWithContentText:@"hello, world!"];[self.navigationControllerpushViewController:viewControlleranimated:YES];

此時再編譯一下,編譯通過了婴削。注意哦蒋荚,這里A業(yè)務(wù)線跟B業(yè)務(wù)線就已經(jīng)完全解耦了,跟主工程就也已經(jīng)完全解耦了馆蠕。

實施組件化方案最后一步:收尾工作期升、組件發(fā)版

此時還有一個收尾工作是我們給B業(yè)務(wù)線創(chuàng)建了Category,但沒有創(chuàng)建Target-Action互躬。所以我們要去主工程創(chuàng)建一個B業(yè)務(wù)線的Target-Action播赁。創(chuàng)建的時候其實完全不需要動到B業(yè)務(wù)線的代碼,只需要新增Target_B對象即可:

Target_B頭文件:#import @interfaceTarget_B:NSObject-(UIViewController*)Action_viewController:(NSDictionary*)params;@endTarget_B實現(xiàn)文件:#import "Target_B.h"#import "BViewController.h"@implementationTarget_B-(UIViewController*)Action_viewController:(NSDictionary*)params{NSString*contentText=params[@"contentText"];BViewController*viewController=[[BViewControlleralloc]initWithContentText:contentText];returnviewController;}@end

這個Target對象在主工程內(nèi)不存在任何侵入性吼渡,將來如果B要獨立成一個組件的話容为,把這個Target對象帶上就可以了。

收尾工作就到此結(jié)束,我們創(chuàng)建了三個私有Pod:A坎背、A_Category替劈、B_Category。

接下來我們要做的事情就是給這三個私有Pod發(fā)版得滤,發(fā)版之前去podspec里面確認(rèn)一下版本號和dependency陨献。

Category的dependency是不需要填寫對應(yīng)的業(yè)務(wù)線的,它應(yīng)該是只依賴一個CTMediator就可以了懂更。其它業(yè)務(wù)線的dependency也是不需要依賴業(yè)務(wù)線的眨业,只需要依賴業(yè)務(wù)線的Category。例如A業(yè)務(wù)線只需要依賴B_Category沮协,而不需要依賴B業(yè)務(wù)線或主工程龄捡。

發(fā)版過程就是幾行命令:

git add .

git commit -m "版本號"

git tag 版本號

git push origin master --tags

./upload.sh

命令行cd進(jìn)入到對應(yīng)的項目中,然后執(zhí)行以上命令就可以了慷暂。

要注意的是聘殖,這里的版本號要和podspec文件中的s.version給到的版本號一致。upload.sh是配置私有Pod的腳本生成的行瑞,如果你這邊沒有upload.sh這個文件就斤,說明這個私有Pod你還沒用腳本配置過。

最后蘑辑,所有的Pod發(fā)完版之后洋机,我們再把Podfile里原來的本地引用改回正常引用,也就是把:path...那一段從Podfile里面去掉就好了洋魂,改動之后記得commit并push绷旗。

組件化實施就這么三步,到此結(jié)束副砍。

總結(jié)

hard code

這個組件化方案的hard code僅存在于Target對象和Category方法中衔肢,影響面極小,并不會泄漏到主工程的業(yè)務(wù)代碼中豁翎,也不會泄漏到業(yè)務(wù)線的業(yè)務(wù)代碼中角骤。

而且在實際組件化的實施中,也是依據(jù)category去做業(yè)務(wù)線的組件化的心剥。所以先寫category里的target名字邦尊,action名字,param參數(shù)优烧,到后面在業(yè)務(wù)線組件中創(chuàng)建Target的時候蝉揍,照著category里面已經(jīng)寫好的內(nèi)容直接copy到Target對象中就肯定不會出錯(僅Target對象,并不會牽扯到業(yè)務(wù)線本身原有的對象)畦娄。

如果要消除這一層hard code又沾,那么勢必就要引入一個第三方pod弊仪,然后target對象所在的業(yè)務(wù)線和category都要依賴這個pod。為了消除這種影響面極小的hard code杖刷,而且只要按照章法來就不會出錯励饵。為此引入一個新的依賴,其實是不劃算的滑燃。

命名域問題

在這個實踐中役听,響應(yīng)者的命名域并沒有泄漏到除了響應(yīng)者以外的任何地方,這就帶來一個好處不瓶,遷移非常方便禾嫉。

比如我們的響應(yīng)者是一個上傳組件灾杰。這個上傳組件如果要替換的話蚊丐,只需要在它外面包一個Target-Action,就可以直接拿來用了艳吠。而且包Target-Action的過程中麦备,不會產(chǎn)生任何侵入性的影響。

例如原來是你自己基于AFNetworking寫的上傳組件昭娩,現(xiàn)在用了七牛SDK上傳凛篙,那么整個過程你只需要提供一個Target-Action封裝一下七牛的上傳操作即可。不需要改動七牛SDK的代碼栏渺,也不需要改動調(diào)用方的代碼呛梆。倘若是基于URL注冊的調(diào)度,做這個事情就很蛋疼磕诊。

服務(wù)管理問題

由于Target對象處于響應(yīng)者的命名域中填物,Target對象就可以對外提供除了頁面實例以外的各種Action。

而且霎终,由于其本質(zhì)就是針對響應(yīng)者對外業(yè)務(wù)邏輯的Action化封裝(其實就是服務(wù)化封裝)滞磺,這就能夠使得一個響應(yīng)者對外提供了哪些Action(服務(wù)),Action(服務(wù))的實現(xiàn)邏輯是什么得到了非常好的管理莱褒,能夠大大降低將來工程的維護(hù)成本击困。然后Category解決了服務(wù)應(yīng)該怎么調(diào)用的問題。

但在基于URL注冊機(jī)制和Protocol共享機(jī)制的組件化方案中广凸,由于服務(wù)散落在響應(yīng)者各處阅茶,服務(wù)管理就顯得十分困難。如果還是執(zhí)念于這樣的方案谅海,大家只要拿上面提到的三個問題目派,對照著URL注冊機(jī)制和Protocol共享機(jī)制的組件化方案比對一下,就能明白了胁赢。

另外企蹭,如果這種方案把所有的服務(wù)歸攏到一個對象中來達(dá)到方便管理的目的的話白筹,其本質(zhì)就已經(jīng)變成了Target-Action模式,Protocol共享機(jī)制其實就已經(jīng)沒有存在意義了谅摄。

高內(nèi)聚

基于protocol共享機(jī)制的組件化方案導(dǎo)致響應(yīng)者業(yè)務(wù)邏輯泄漏到了調(diào)用者業(yè)務(wù)邏輯中徒河,并沒有做到高內(nèi)聚。

如果這部分業(yè)務(wù)在其他地方也要使用送漠,那么代碼就要重新寫一遍顽照。雖然它可以提供一個業(yè)務(wù)高內(nèi)聚的對象來符合這個protocol,但事實上這就又變成了Target-Action模式闽寡,protocol的存在意義就也沒有了代兵。

侵入性問題

正如你所見,CTMediator組件化方案的實施非常安全爷狈。因為它并不存在任何侵入性的代碼修改植影。

對于響應(yīng)者來說,什么代碼都不用改涎永,只需要包一層Target-Action即可思币。例如本例中的B業(yè)務(wù)線作為A業(yè)務(wù)的響應(yīng)者時,不需要修改B業(yè)務(wù)的任何代碼羡微。

對于調(diào)用者來說谷饿,只需要把調(diào)用方式換成CTMediator調(diào)用即可,其改動也不涉及原有的業(yè)務(wù)邏輯妈倔,所以是十分安全的博投。

另外一個非侵入性的特征體現(xiàn)在,基于CTMediator的組件化方案是可以循序漸進(jìn)地實施的盯蝴。這個方案的實施并不要求所有業(yè)務(wù)線都要被獨立出來成為組件毅哗,實施過程也并不會修改未組件化的業(yè)務(wù)的代碼。

在獨立A業(yè)務(wù)線的過程中如果涉及其它業(yè)務(wù)線(B業(yè)務(wù)線)的調(diào)用结洼,就只需要給到Target對象即可黎做,Target對象本身并不會對未組件化的業(yè)務(wù)線(B業(yè)務(wù)線)產(chǎn)生任何的修改。而且將來如果對應(yīng)業(yè)務(wù)線需要被獨立出去的時候松忍,也僅需要把Target對象一起復(fù)制過去就可以了蒸殿。

但在基于URL注冊和protocol共享的組件化方案中,都必須要在未組件化的業(yè)務(wù)線中寫入注冊代碼和protocol聲明鸣峭,并分配對應(yīng)的URL和protocol到具體的業(yè)務(wù)對象上宏所。這些其實都是不必要的,無端多出了額外維護(hù)成本摊溶。

注冊問題

CTMediator沒有任何注冊邏輯的代碼爬骤,避免了注冊文件的維護(hù)和管理。Category給到的方法很明確地告知了調(diào)用者應(yīng)該如何調(diào)用莫换。

例如B_Category給到的- (UIViewController *)B_viewControllerWithContentText:(NSString *)contentText;方法霞玄。這能夠讓工程師一眼就能夠明白使用方式骤铃,而不必抓瞎拿著URL再去翻文檔。

這可以很大程度提高工作效率坷剧,同時降低維護(hù)成本惰爬。

實施組件化方案的時機(jī)

MVP階段過后,越早實施越好惫企。

這里說的MVP不是一種設(shè)計模式撕瞧,而是最小價值產(chǎn)品的意思,它是產(chǎn)品演進(jìn)的第一個階段狞尔。

一般來說天使輪就是用于MVP驗證的丛版,在這個階段產(chǎn)品閉環(huán)尚未確定,因此產(chǎn)品本身的邏輯就會各種變化偏序。但是過了天使輪之后页畦,產(chǎn)品閉環(huán)已經(jīng)確定,此時就應(yīng)當(dāng)實施組件化禽车,以應(yīng)對A輪之后的產(chǎn)品拓張寇漫。

有的人說我現(xiàn)在項目很小刊殉,人也很少殉摔,所以沒必要實施組件化。確實记焊,把一個小項目組件化之后逸月,跟之前相比并沒有多大程度的改善,因為本來小項目就不復(fù)雜遍膜,改成組件化之后碗硬,也不會更簡單。

但這其實是一種很短視的認(rèn)知瓢颅。

組件化對于一個小項目而言恩尾,真正發(fā)揮優(yōu)勢的地方是在未來的半年甚至一年之后。

因為趁著人少項目小挽懦,實施組件化的成本就也很小翰意,三四天就可以實施完畢。于是等將來一年之后業(yè)務(wù)拓張到更大規(guī)模時信柿,就不會束手束腳了冀偶。

但如果等到項目大了,人手多了再去實施組件化渔嚷,那時候?qū)嵤┙M件化的復(fù)雜度肯定比現(xiàn)在規(guī)模還很小的時候的復(fù)雜度要大得多进鸠,三四天肯定搞不定,而且實施過程還會非常艱辛形病。到那時你就后悔為什么當(dāng)初沒有早早實施組件化了客年。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霞幅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子量瓜,更是在濱河造成了極大的恐慌蝗岖,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榔至,死亡現(xiàn)場離奇詭異抵赢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)唧取,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門铅鲤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人枫弟,你說我怎么就攤上這事邢享。” “怎么了淡诗?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵骇塘,是天一觀的道長。 經(jīng)常有香客問我韩容,道長款违,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任群凶,我火速辦了婚禮插爹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘请梢。我一直安慰自己赠尾,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布毅弧。 她就那樣靜靜地躺著气嫁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪够坐。 梳的紋絲不亂的頭發(fā)上寸宵,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音咆霜,去河邊找鬼邓馒。 笑死,一個胖子當(dāng)著我的面吹牛蛾坯,可吹牛的內(nèi)容都是我干的光酣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼脉课,長吁一口氣:“原來是場噩夢啊……” “哼救军!你這毒婦竟也來了财异?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤唱遭,失蹤者是張志新(化名)和其女友劉穎戳寸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拷泽,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡疫鹊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡骗村,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枣耀,到底是詐尸還是另有隱情,我是刑警寧澤庭再,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布捞奕,位于F島的核電站,受9級特大地震影響拄轻,放射性物質(zhì)發(fā)生泄漏颅围。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一哺眯、第九天 我趴在偏房一處隱蔽的房頂上張望谷浅。 院中可真熱鬧扒俯,春花似錦奶卓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至掌猛,卻和暖如春盏浙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荔茬。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工废膘, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慕蔚。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓丐黄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親孔飒。 傳聞我的和親對象是個殘疾皇子灌闺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

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