- 常見的 Objective-C 構造器
31度的高溫,啥也別說,來杯奇異果茶
LYFruitTea *fruitTea = [[LYFruitTea alloc] initWith:LYFruitVariety_Kiwi];
喝起來覺得太甜了,好吧,加個甜度控制
LYFruitTea *fruitTea = [[LYFruitTea alloc] initWith:LYFruitVariety_Kiwi andSweetness:30];
30%的甜度剛好,可是還是很熱呀,能不能加冰呢
LYFruitTea *fruitTea = [[LYFruitTea alloc] initWith:LYFruitVariety_Kiwi sweetness:30 andIced:YES];
LYFruitTea 的內(nèi)部實現(xiàn),一般都是其它構造器層層調(diào)用至最后最長參數(shù)設置的那個構造器(重疊調(diào)用構造器):
- (instancetype)initWith:(LYFruitVariety)fruitVariety {
return [self initWith:fruitVariety andSweetness:10];
}
- (instancetype)initWith:(LYFruitVariety)fruitVariety andSweetness:(NSInteger)sweetness {
return [self initWith:fruitVariety sweetness:sweetness andIced:NO];
}
- (instancetype)initWith:(LYFruitVariety)fruitVariety sweetness:(NSInteger)sweetness andIced:(BOOL)iced {
if (self = [super init]) {
_fruitVariety = fruitVariety;
_sweetness = sweetness;
_iced = iced;
}
return self;
}
擴展性不好,有了新的需求,用戶要選擇是否加牛奶,是否加珍珠,那是否就不斷的擴展/新建構造方法呢?
當然也有另外一種方式:
LYFruitTea *fruitTea = [[LYFruitTea alloc] init];
fruitTea.fruitVariety = LYFruitVariety_Kiwi;
fruitTea.sweetness = 30;
fruitTea.iced = YES;
這樣寫可以滿足要求了吧,擴展性不錯捏,?(?????????)?,可是這樣寫很分散,容易丟失屬性設置(特別是后續(xù)增加的必設屬性),使用者也可能不知道新增了屬性,所以兩種屬性都不能夠很靈活的處理需求變更
- Builder Pattern
記得曾看過一句話:遇到多個構造器參數(shù)時需要考慮用構建器,它將復雜對象的構造與它的表示分開
Builder Pattern 在 JavaScript 中的使用(這里的 Method Chaining 下一篇)
LYFruitTea *fruitTea = new LYFruitTea.builder()
.setFruitVariety(LYFruitVariety_Kiwi)
.setSweetness(30)
.setIced(YES)
.build();
新需求需要設置新屬性時,在 build() 調(diào)用前添加 setXXX() 即可,同時若有必設屬性,在 build() 里進行檢查并給予??
對應到 Objective-C 中常規(guī)實現(xiàn)是
LYFruitTea *fruitTea = [[[[[[LYFruitTeaBuilder alloc] init] setFruitVariety:LYFruitVariety_Kiwi] setSweetness:30] setIced:YES] build];
這是在玩貪吃蛇嗎?有種被支配的恐懼
嗯,是的還有另外的實現(xiàn)方式:
LYFruitTeaBuilder *fruitTeaBuilder = [[LYFruitTeaBuilder alloc] init];
fruitTeaBuilder.fruitVariety = fruitVariety;
fruitTeaBuilder.sweetness = sweetness;
fruitTeaBuilder.iced = iced;
LYFruitTea *fruitTea = [fruitTeaBuilder build];
到這里是不是發(fā)現(xiàn)跟之前常用的構造器的問題一樣,是不是看不到 Builder Pattern 在 Objective-C 使用的真正意義,那是因為目前只是僅僅將這個模式直接拷貝至 Objective-C 來實現(xiàn),并未適應 Objective-C
- Builder Pattern 在 Objective-C 的使用
Builder Pattern 適應 Objective-C 的寫法其實并不特別,就是使用 block (Method Chaining 也是使用 block 實現(xiàn)),并在 block 里進行屬性設置等等配置工作
typedef void(^LYFruitTeaBuilderBlock)(LYFruitTeaBuilder *builder);
LYFruitTea *fruitTea = [LYFruitTea ruitVarietyWith:LYFruitVariety_Kiwi
builder:^(LYFruitTeaBuilder *builder) {
builder.sweetness = 30;
builder.iced = YES;
}];
看起來是不是舒服多了,若是有其他需求變更,集中在塊中進行配置,也不會由于疏忽而忘記了build的調(diào)用,這里只需要關注想要的配置,使用者很方便調(diào)用
其中ruitVarietyWith:builder:的方法實現(xiàn)
+ (LYFruitTea *)ruitVarietyWith:(LYFruitVariety)fruitVariety builder:(LYFruitTeaBuilderBlock)block {
NSParameterAssert(block);
LYFruitTeaBuilder *builder = [[LYFruitTeaBuilder alloc] initWith:fruitVariety];
block(builder);
return [builder build];
}
LYFruitTeaBuilder 的 build 方法實現(xiàn):
- (LYFruitTea *)build {
/*進行一些條件的檢查*/
LYFruitTea *fruitTea = [[LYFruitTea alloc] initWithBuilder:self];
return fruitTea;
}
LYFruitTea 的 initWithBuilder: 方法實現(xiàn):
- (instancetype)initWithBuilder:(LYFruitTeaBuilder *)builder {
if (self = [super init]) {
_fruitVariety = builder.fruitVariety;
_sweetness = builder.sweetness;
_iced = builder.iced;
}
return self;
}
當然也可以將這一操作集中在 LYFruitTeaBuilder 一起完成:
- (LYFruitTea *)build {
/*進行一些條件的檢查*/
LYFruitTea *fruitTea = [[LYFruitTea alloc] init];
fruitTea.fruitVariety = self.fruitVariety;
fruitTea.sweetness = self.sweetness;
fruitTea.iced = self.iced;
return fruitTea;
}
這兩種進行最后配置時的操作,看個人習慣,怎么方便怎么來,個人更傾向于后面一種,避免了兩個地方的互相調(diào)用,引起的查閱不便,可讀性更強
作為客戶端開發(fā),常常會有需求展示的變更,以便于產(chǎn)品快速進行驗證,這個時候使用 Builder Pattern 最大的好處,就是配置進行統(tǒng)一配置,看起來整潔,需求變更,開發(fā)人員可以快速響應,無需大片改動代碼,非常便于后期人員的理解維護
哎,感覺今天蔫蔫的,先醬紫吧,腦袋一片漿糊呢