背景:最近在進行list頁重構,list頁的cell會有不一樣的展示形式,即同一個UITableView上多種cell並存。爲了響應這種需求,咱們考慮經過cell容器化的方式解決該問題。最理想的解決方法就是經過工廠模式來定製這些cell,當服務端告知咱們某一個indexPath的cell的style時,咱們就用相應類型的cell去填充。spa
工廠模式介紹設計
工廠模式能夠分爲簡單工廠模式, 工廠方法模式, 抽象工廠模式,這三種模式在設計程度上由簡單到複雜。下面挨個解釋下各自的特色。orm
簡單工廠模式對象
簡單工廠模式是由工廠類直接生產相應的產品(經過類方法),而後在方法中經過switch-case 語句(或者if-else 語句)來決定勝場什麼產品。該模式算是工廠模式的一個特例,由於當用戶須要新增一種產品時,須要在直接修改工廠方法的switch-case 語句(或if-else 語句),經過添加分支條件來適應更多種狀況,由此看出,它違背了開放-封閉原則。索引
@interface TRIPHotelCellFactory : NSObject接口 /**開發 * cell工廠方法字符串 *產品 * @param cellClassName 待新建的cell類名it * @param cellModel cell數據模型 * @param indexPath index索引 * * @return 目標cell */ + (TRIPHotelBasicCell *)creatCellWithClassName:(NSString *)cellClassName cellModel:(TRIPHotelCellModel *)cellModel indexPath:(NSIndexPath *)indexPath; @end |
@implementation TRIPHotelCellFactory
+ (TRIPHotelBasicCell *)creatCellWithClassName:(NSString *)cellClassName cellModel:(TRIPHotelCellModel *)cellModel indexPath:(NSIndexPath *)indexPath{ TRIPHotelBasicCell *cell = nil;
if ([cellClassName isEqualToString:@"TRIPHotelStandardCell"]) { cell = [[TRIPHotelStandardCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"TRIPHotelStandardCell"]; } else if ([cellClassName isEqualToString:@"TRIPHotelForSaleCell"]){ cell = [[TRIPHotelForSaleCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"TRIPHotelForSaleCell"]; }
return [cell initData:cellModel indexPath:indexPath]; } @end |
工廠方法模式
工廠方法模式不直接生產產品,而是定義好如何生產產品的接口,而後由其子類決定具體生產何種產品,即工廠方法使一個類的實例化延遲到其子類。基類工廠只負責定義接口便可,具體的實現由其子類完成,當須要新增一種產品類型時,只須要再定義一個子類工廠,由該子工廠類去生產相應的產品。很好的知足了開發-封閉的原則。
@interface TRIPHotelCellFactory : NSObject /** * cell工廠方法 * * @param cellModel 待新建的cell類名 * @param indexPath cell數據模型 * * @return 目標cell */ + (TRIPHotelBasicCell *)creatCellWithModel:(TRIPHotelCellModel *)cellModel indexPath:(NSIndexPath *)indexPath; @end |
@implementation TRIPHotelCellFactory + (TRIPHotelBasicCell *)creatCellWithModel:(TRIPHotelCellModel *)cellModel indexPath:(NSIndexPath *)indexPath{ return [[TRIPHotelBasicCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"TRIPHotelBasicCell"]; } @end |
@interface TRIPHotelForSaleCellFactory : TRIPHotelCellFactory
@end |
@implementation TRIPHotelForSaleCellFactory
+ (TRIPHotelBasicCell *)creatCellWithModel:(TRIPHotelCellModel *)cellModel indexPath:(NSIndexPath *)indexPath{ TRIPHotelForSaleCell *cell = [[TRIPHotelForSaleCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"TRIPHotelForSaleCell"]; return [cell initData:cellModel indexPath:indexPath]; }
@end |
實際上基類工廠方法中返回什麼都無所謂,最終咱們是其子類去生產相應的產品。
抽象工廠模式
該工廠模式就更復雜一些了,該工廠類不只能夠生產產品A,還能夠生產產品B,只不過基類工程定義好生產相應產品的方法,尤爲子類去實現具體的產品勝場過程。這個模式用的不是不少,這裏就不舉例了。
最佳實踐
從上面的對比能夠發現,簡單工廠模式和工廠方法模式各有好處,一個實現簡單,一個知足開放-封閉原則,支持封閉擴展,若是把這二者的好處融合在一塊兒?
反射機制的引入是這種設想變成可能。
在oc中,反射形如:
NSString *cellClassName = @"TRIPHotelStandardCell";
Class classForCell = NSClassFromString(cellClassName);
能夠直接經過類名便可得到相應的類並實現初始化。
修改簡單工廠模式以下:
@implementation TRIPHotelCellFactory
+ (TRIPHotelBasicCell *)creatCellWithClassName:(NSString *)cellClassName cellModel:(TRIPHotelCellModel *)cellModel indexPath:(NSIndexPath *)indexPath{ TRIPHotelBasicCell *cell = nil;
// 經過反射來定義cell,當遇到cell拓展時,能夠直接用字符串反射,無需修改該工廠方法 Class classForCell = NSClassFromString(cellClassName);
// 初始化目標cell cell = [[classForCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellClassName];
return [cell initData:cellModel indexPath:indexPath]; }
@end |
這樣一來,就能夠在外部直接傳入待生產cell的類名,便可得到相應的對象。當新增cell類型時,只須要在外部傳入新定製的cell類名。該作法使簡單工廠模式知足了封閉-開放原則,並且實現簡單。
其實現形如:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TRIPHotelCellModel *dataModel = (TRIPHotelCellModel *)self.modelArray[indexPath.row]; TRIPHotelBasicCell *cell = nil; switch (dataModel.cellStyle) { case HotelCellStyleStandard: { cell = [tableView dequeueReusableCellWithIdentifier:@"TRIPHotelStandardCell"]; if (nil == cell) { cell = [TRIPHotelCellFactory creatCellWithClassName:@"TRIPHotelStandardCell" cellModel:dataModel indexPath:indexPath]; } } break; case HotelCellStyleForSale: { cell = [tableView dequeueReusableCellWithIdentifier:@"TRIPHotelForSaleCell"]; if (nil == cell) { cell = [TRIPHotelCellFactory creatCellWithClassName:@"TRIPHotelForSaleCell" cellModel:dataModel indexPath:indexPath]; } } break; default: break; } return cell; } |