自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

8次嘗試,帶你走進iOS 精益編程

移動開發(fā) iOS
Copy-Paste是大部分程序員最容易犯的毛病,為此引入了大量的重復代碼。

[[153315]]

開場

今天, 我們將從一個小功能開始, 先去不假思索的實現(xiàn)它

Product Repository: Filtering Operation

Code start

有一個產(chǎn)品庫, 我們要對它做過濾操作.

***個需求并不復雜.

需求1:在倉庫中查找所有顏色為紅色的產(chǎn)品

First Attempt: Hard Code

我們先用最簡單的方式去實現(xiàn)它, 硬編碼

  1. - (NSArray *)findAllRedProducts:(NSArray *)products 
  2. NSMutableArray *list = [@[] mutableCopy]; 
  3. for (Product *product in products) { 
  4. if (product.color == RED) { 
  5. [list addObject:product]; 
  6. return list; 

 

如果這個世界是永恒靜止的,這樣的實現(xiàn)無可厚非,但世界往往并非如此。

緊接著,第二個需求來了

需求2:在倉庫中查找所有顏色為綠色的產(chǎn)品

Second Attempt: Parameterizing

Copy-Paste是大部分程序員最容易犯的毛病,為此引入了大量的重復代碼。

  1. - (NSArray *)findAllGreenProducts:(NSArray *)products 
  2. NSMutableArray *list = [@[] mutableCopy]; 
  3. for (Product *product in products) { 
  4. if (product.color == GREEN) { 
  5. [list addObject:product]; 
  6. return list; 

為了消滅硬編碼,得到可重用的代碼,可以引入簡單的參數(shù)化設(shè)計。

  1. - (NSArray *)findProducts:(NSArray *)products byColor:(ProductColor)color 
  2. NSMutableArray *list = [@[] mutableCopy]; 
  3. for (Product *product in products) { 
  4. if (product.color == color) { 
  5. [list addObject:product]; 
  6. return list; 

終于可以放心了, 這個時候我們的產(chǎn)品經(jīng)理怎么可能讓你舒服呢,需求3又來了

需求3:查找所有重量小于10的所有產(chǎn)品

Third Attempt: Parameterizing with Every Attribute You Can Think Of

大部分程序員依然會使用Copy-Paste解決這個問題,拒絕Copy-Paste的陋習,***實效的一個反饋就是讓這個快捷鍵失效,從而在每次嘗試Copy-Paste時提醒自己做更好的設(shè)計

  1. - (NSArray *)findProducts:(NSArray *)products byWeith:(float)weight 
  2. NSMutableArray *list = [@[] mutableCopy]; 
  3. for (Product *product in products) { 
  4. if (product.weight < weight) { 
  5. [list addObject:product]; 
  6. return list; 

為了消除兩者重復的代碼,通過簡單的參數(shù)化往往不能***解決這類問題,相反地會引入過度的復雜度和偶發(fā)成本。

  1. - (NSArray *)findProducts:(NSArray *)products byColor:(ProductColor)color byWeith:(float)weight type:(int)type 
  2. NSMutableArray *list = [@[] mutableCopy]; 
  3. for (Product *product in products) { 
  4. if ((type == 1) && product.color == color) { 
  5. [list addObject:product]; 
  6. continue
  7. else if ((type == 2) && (product.weight < weight)) 
  8. [list addObject:product]; 
  9. continue
  10. return list; 

日常工作中,這樣的實現(xiàn)手法非常普遍,函數(shù)的參數(shù)列表隨著需求增加不斷增加,函數(shù)邏輯承擔的職責越來越多,邏輯也變得越來越難以控制。

通過參數(shù)配置應(yīng)對變化的設(shè)計往往都是失敗的設(shè)計

易于導致復雜的邏輯控制,引發(fā)額外的偶發(fā)復雜度

Forth Attempt: Abstracting over Criteria

為此需要抽象,使其遍歷的算法與查找的標準能夠獨立地變化,互不影響。

  1. @interface ProductSpec : NSObject 
  2. - (BOOL)satisfy:(Product *)product; 
  3. @end 

此刻filter的算法邏輯得到封閉,當然函數(shù)名需要重命名,使其算法實現(xiàn)更加具有普遍性。

  1. - (NSArray *)findProducts:(NSArray *)products bySpec:(ProductSpec *)spec 
  2. NSMutableArray *list = [@[] mutableCopy]; 
  3. for (Product *product in products) { 
  4. if ([spec satisfy:product]) { 
  5. [list addObject:product]; 
  6. return list; 

通過可復用的類來封裝各種變化,讓變化的因素控制在最小的范圍內(nèi)。

  1. @interface ColorSpec() 
  2. @property (nonatomic, assign) ProductColor color; 
  3. @end 
  4. @implementation ColorSpec 
  5. + (instancetype)specWithColor:(ProductColor)color 
  6. ColorSpec *spec = [[ColorSpec alloc] init]; 
  7. spec.color = color; 
  8. return spec; 
  9. - (BOOL)satisfy:(Product *)product 
  10. return product.color == RED; 
  11. @end 
  12. @interface BelowWeightSpec() 
  13. @property (nonatomic, assign) float limit; 
  14. @end 
  15. @implementation BelowWeightSpec 
  16. + (instancetype)specWithBelowWeight:(float)limit 
  17. BelowWeightSpec *spec = [[BelowWeightSpec alloc] init]; 
  18. spec.limit = limit; 
  19. return spec; 
  20. - (BOOL)satisfy:(Product *)product 
  21. return (product.weight < _limit); 
  22. @end 

用戶的接口也變得簡單多了,而且富有表現(xiàn)力。

  1. [self findProducts:_products bySpec:[ColorSpec specWithColor:RED]]; 

這是經(jīng)典的OO設(shè)計,如果熟悉設(shè)計模式的讀者對此已經(jīng)習以為常了。設(shè)計模式是好東西,但往往被濫用。為此不能依葫蘆畫瓢,死板照抄,而是為了得到更簡單的設(shè)計而引入設(shè)計模式的,這個過程是很自然的。

與大師們交流,問究此處為何引入設(shè)計模式,得到的答案:直覺。忘記所有設(shè)計模式吧,管它是不是模式,如果設(shè)計是簡單的,這就是模式。

另外還有一個明顯的壞味道,ColorSpec和BelowWeightSpec都需要繼承ProductSpec,都需要定義一個構(gòu)造函數(shù)和一個私有的字段,并重寫satisfy方法,這些都充斥著重復的結(jié)構(gòu)。

是不是覺得目前的寫法已經(jīng)夠用了? 莫急, 讓我們來看看下個需求

需求4:查找所有顏色為紅色,并且重量小于10的所有產(chǎn)品

 

  1. Firth Attempt: Composite Criteria 
  2.  
  3. 按照既有的代碼結(jié)構(gòu),往往易于設(shè)計出類似ColorAndBelowWeightSpec的實現(xiàn)。 
  4. @interface ColorAndBelowWeigthSpec() 
  5. @property (nonatomic, assign) ProductColor color; 
  6. @property (nonatomic, assign) float limit; 
  7. @end 
  8. @implementation ColorAndBelowWeigthSpec 
  9. + (instancetype)specWithColor:(ProductColor)color beloWeigth:(float)limit 
  10. ColorAndBelowWeigthSpec *spec = [[ColorAndBelowWeigthSpec alloc] init]; 
  11. spec.color = color; 
  12. spec.limit = limit; 
  13. return spec; 
  14. - (BOOL)satisfy:(Product *)product 
  15. return product.color == _color || (product.weight < _limit); 
  16. @end 

 

存在兩個明顯的壞味道:

包含and的命名往往是違背單一職責的信號燈

ColorAndBelowWeightSpec的實現(xiàn)與ColorSpec,BelowWeightSpec之間存在明顯的重復

此刻,需要尋找更本質(zhì)的抽象來表達設(shè)計,and/or/not語義可以***解決這類問題。

Composite Spec: AndSpec, OrSpec, NotSpec

Atomic Spec:ColorSpec, BeblowWeightSpec

  1. @interface AndSpec() 
  2. @property (nonatomic, strong) NSArray *specs; 
  3. @end 
  4. @implementation AndSpec 
  5. + (instancetype)spec:(ProductSpec *)spec, ... NS_REQUIRES_NIL_TERMINATION 
  6. va_list args; 
  7. va_start( args, spec ); 
  8. NSMutableArray *mArray = [@[spec] mutableCopy]; 
  9. for ( ;; ) 
  10. id tempSpec = va_arg( args, id ); 
  11. if (tempSpec == nil) 
  12. break
  13. [mArray addObject:tempSpec]; 
  14. va_end( args ); 
  15. AndSpec *andSpec = [[AndSpec alloc] init]; 
  16. andSpec.specs = [mArray copy]; 
  17. return andSpec; 
  18. - (BOOL)satisfy:(Product *)product 
  19. for (ProductSpec *spec in _specs) { 
  20. if (![spec satisfy:product]) { 
  21. return NO; 
  22. return YES; 
  23. @end 
  24. @interface OrSpec () 
  25. @property (nonatomic, strong) NSArray *specs; 
  26. @end 
  27. @implementation OrSpec 
  28. + (instancetype)spec:(ProductSpec *)spec, ... NS_REQUIRES_NIL_TERMINATION 
  29. va_list args; 
  30. va_start( args, spec ); 
  31. NSMutableArray *mArray = [@[spec] mutableCopy]; 
  32. for ( ;; ) 
  33. id tempSpec = va_arg( args, id ); 
  34. if (tempSpec == nil) 
  35. break
  36. [mArray addObject:tempSpec]; 
  37. va_end( args ); 
  38. OrSpec *orSpec = [[OrSpec alloc] init]; 
  39. orSpec.specs = [mArray copy]; 
  40. return orSpec; 
  41. - (BOOL)satisfy:(Product *)product 
  42. for (ProductSpec *spec in _specs) { 
  43. if ([spec satisfy:product]) { 
  44. return YES; 
  45. return NO; 
  46. @end 
  47. @interface NotSpec () 
  48. @property (nonatomic, strong) ProductSpec *spec; 
  49. @end 
  50. @implementation NotSpec 
  51. + (instancetype)spec:(ProductSpec *)spec 
  52. NotSpec *notSpec = [[NotSpec alloc] init]; 
  53. notSpec.spec = spec; 
  54. return notSpec; 
  55. - (BOOL)satisfy:(Product *)product 
  56. if (![_spec satisfy:product]) { 
  57. return YES; 
  58. return NO; 
  59. @end 

可以通過AndSpec組合ColorSpec, BelowWeightSpec來實現(xiàn)需求,簡單漂亮,并且富有表達力。

  1. [self findProducts:_products bySpec:[AndSpec spec:[ColorSpec specWithColor:RED], [BelowWeightSpec specWithBelowWeight:10], nil]]; 

但這樣的設(shè)計存在兩個嚴重的壞問道:

AndSpec與OrSpec存在明顯的代碼重復,OO設(shè)計的***個直覺就是通過抽取基類來消除重復。

  1. @interface CombinableSpec () 
  2. @property (nonatomic, strong) NSArray *specs; 
  3. @end 
  4. @implementation CombinableSpec 
  5. + (instancetype)spec:(CombinableSpec *)spec, ... NS_REQUIRES_NIL_TERMINATION 
  6. va_list args; 
  7. va_start( args, spec ); 
  8. NSMutableArray *mArray = [@[spec] mutableCopy]; 
  9. for ( ;; ) 
  10. id tempSpec = va_arg( args, id ); 
  11. if (tempSpec == nil) 
  12. break
  13. [mArray addObject:tempSpec]; 
  14. va_end( args ); 
  15. CombinableSpec *combinableSpec = [[CombinableSpec alloc] init]; 
  16. combinableSpec.specs = [mArray copy]; 
  17. return combinableSpec; 
  18. - (BOOL)satisfy:(Product *)product 
  19. for (ProductSpec *spec in _specs) { 
  20. if ([spec satisfy:product] == _shortcut) { 
  21. return _shortcut; 
  22. return !_shortcut; 
  23. @end 
  24. @implementation AndSpec 
  25. - (instancetype)init 
  26. self = [super init]; 
  27. if (self) { 
  28. self.shortcut = NO; 
  29. return self; 
  30. @end 
  31. @implementation OrSpec 
  32. - (instancetype)init 
  33. self = [super init]; 
  34. if (self) { 
  35. self.shortcut = YES; 
  36. return self; 
  37. @end 

大堆的初始化方法讓人眼花繚亂

  1. [self findProducts:_products bySpec:[NotSpec spec:[AndSpec spec:[ColorSpec specWithColor:RED], [BelowWeightSpec specWithBelowWeight:10], nil]]]; 
  2.  
  3. Sixth Attempt: Using DSL 

 

可以引入DSL改善程序的可讀性,讓代碼更具表達力。

我們先添加一些DSL:

  1. static ProductSpec *COLOR(ProductColor color) 
  2. return [ColorSpec specWithColor:RED]; 
  3. static ProductSpec *BELOWWEIGHT(float limit) 
  4. return [BelowWeightSpec specWithBelowWeight:limit]; 
  5. static ProductSpec *AND(ProductSpec *spec1, ProductSpec *spec2) 
  6. return [AndSpec spec:spec1, spec2, nil]; 
  7. static ProductSpec *OR(ProductSpec *spec1, ProductSpec *spec2) 
  8. return [OrSpec spec:spec1, spec2, nil]; 
  9. static ProductSpec *NOT(ProductSpec *spec) 
  10. return [NotSpec spec:spec]; 

這樣我們的代碼表現(xiàn)起來就是這樣的

  1. [self findProducts:_products bySpec:NOT(AND(COLOR(RED), BELOWWEIGHT(10)))]; 
  2.  
  3. Seventh Attempt: Using a Lambda Expression 

 

可以使用Block改善設(shè)計,增強表達力。

  1. - (NSArray *)findProducts:(NSArray *)products byBlock:(BOOL (^)())block 
  2. NSMutableArray *list = [@[] mutableCopy]; 
  3. for (Product *product in products) { 
  4. if (block(product)) { 
  5. [list addObject:product]; 
  6. return list; 

代碼現(xiàn)在開起來是這個樣子

  1. [self findProducts:_products byBlock:^BOOL(id p) {return [p color] == RED;}]; 

構(gòu)造DSL,復用這些Block

  1. ProductSpecBlock color(ProductColor color) 
  2. return ^BOOL(id p) {return [p color] == color;}; 
  3. ProductSpecBlock weightBelow(float limit) 
  4. return ^BOOL(id p) {return [p weight] < limit;}; 
  5. - (void)test7_2 
  6. [self findProducts:_products byBlock:color(RED)]; 
  7.  
  8. Eighth attempt: Using NSPredicate 

 

還可以使用標準庫
 

  1. [self.products filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"weight > 10"]]; 

結(jié)束

今天的編碼就到此為止了, 這篇文章本是Horance所寫, 筆者將用OC實現(xiàn)了一遍.如果咱們不是iOS Developer的話, 還是有其他attempt的, 如泛型.

責任編輯:chenqingxiang 來源: 曉月的專欄
相關(guān)推薦

2017-05-16 15:27:32

精益敏捷代碼

2016-07-18 16:09:40

精益生產(chǎn)Testin質(zhì)量控制

2010-03-16 17:30:14

Java多線程編程

2016-02-24 16:09:24

并行科技軟件交付

2009-12-09 13:41:50

PHP Zend框架

2013-08-14 13:35:32

設(shè)計

2011-10-08 14:38:21

精益掃描儀

2017-09-21 10:34:38

留存分析數(shù)據(jù)分析留存

2020-07-03 09:41:20

華為∑co時間醫(yī)療

2011-10-06 17:10:24

精益掃描儀

2010-09-14 10:15:24

2011-03-16 14:20:30

2011-07-27 17:05:59

精益掃描儀

2015-07-22 10:25:09

NEC

2012-06-04 15:26:29

精益掃描儀

2020-08-11 09:47:30

JS閉包代碼

2014-03-27 17:20:02

金蝶

2011-09-14 17:21:28

精益掃描儀

2015-11-03 11:08:01

IT高性能運維

2018-08-27 11:46:37

易觀之星
點贊
收藏

51CTO技術(shù)棧公眾號