1.何为工厂模式
工厂模式可以简单概括为同类型不同型号的产品有各自对应的工厂进行生产。好比如iPhone手机旗下有iPhoneX及iPhone8两种型号的手机,iPhoneX有自己iPhoneX的专属工厂进行生产,而iPhone8有自己iPhone8的专属工厂进行生产。两条生产线没有交集互不影响,后期如果新增或废弃相关类型的产品也不会影响到原有的生产线。
工厂模式也称为虚构造器,它适用于:一个类无法预期生成那个类对象,想让其子类来指定所生成具体对象。
2.何时使用工厂模式
编译时无法确定预期要创建的对象类
类想让子类决定运行时创建什么
类有若干的辅助子类,而你想将返回那个子类这一信息局部化
3.工厂模式静态类结构图
Factory.png
由上图可以知,工厂模式将都具有拍照功能的同一类产品(iPhoneX、iPhone8)抽象为iPhone,而具体对应的生产工厂则抽象为iPhoneFactory。这样做的好处是即使新增或废弃其中一类型产品也不会影响到原有的其他生产线,同时,不同类型的产品的功能也能得到扩展。如:不同类型的iPhone手机的拍照效果具体依赖于其机型的相关硬件
4.案例
我们常见的UITableView列表包含了各种不同的UI展示的Cell。我们在其相关的代理方法实现可能是这样的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { WCQAStyleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"WCQAStyleTableViewCell"]]; [cell configUI:nil]; //不同样式的Cell所展示的UI各不相同,由于是Demo样例,这里并未配置相关数据源 return cell; }else if (indexPath.row == 1) { WCQBStyleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"WCQBStyleTableViewCell"]]; [cell configUI:nil]; return cell; }else if (indexPath.row == 2) { WCQCStyleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"WCQCStyleTableViewCell"]]; [cell configUI:nil]; return cell; } }
随着需求的迭代,我们后期可能会增加更多样式的Cell,此时我们就需要变更这部分的代码。增加与之对应的else if条件判断,代码将变得越来越长。同时,在更加复杂的场景下,我们并不能保证调整原有代码以后,之前功能不受影响。我们将投入更多的时间进行原有代码功能的回归,这是我们不希望发生的。
我们发现上述例子都是通过不同样式的Cell进行不同的UI展示,就好比如我们用不同型号的iPhone进行拍照。我们可以将上述例子调整为工厂模式,看看其能为我们带什么样的效果。
1.将各种展示功能相似的Cell抽象为WCQBaseTableViewCell(继承于UITableViewCell),WCQTableViewCell提供默认展示UI的方法configUI
2.其他多种的Cell继承于WCQBaseTableViewCell,根据其特定的需求对configUI方法进覆写,这里我们以WCQAStyleTableViewCell为例:
WCQAStyleTableViewCell.h
#import "WCQBaseTableViewCell.h" @interface WCQAStyleTableViewCell : WCQBaseTableViewCell @end
WCQAStyleTableViewCell.m
#import "WCQAStyleTableViewCell.h" @implementation WCQAStyleTableViewCell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { //UI } return self; } - (void)awakeFromNib { [super awakeFromNib]; // Initialization code } - (void)configUI:(id)model { self.textLabel.text = NSStringFromClass([self class]); } - (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state } @end
3.对之前UITableView代理方法进行调整
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { WCQBaseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifiers[indexPath.row]]; [cell configUI:nil]; return cell; }
这里可能很多同学会问,说好的工厂模式怎么只有产品工厂没了呢?其实这里[tableView dequeueReusableCellWithIdentifier:self.cellIdentifiers[indexPath.row]]方法即为一个工厂。该方法在调用时会执行各种样式Cell的工厂方法并返回一个具体产品
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { //UI } return self; }
我们能看到在使用了工厂模式之后,最直观的感受是代码量明显减少了,也并未依赖具体的实现类。维护的代码越少其BUG产生的概率也越低。而且,即便是后期有新样式Cell的增加,我们也可以在基本不修改原来客户端的代码来进行样式的增加,我们只需要增加对应Cell的实现类以及对新增样式Cell的注册即可。对应Cell的初始化还是UI展示都封装进了各自的工厂中,互不影响。
Cell注册代码示例:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; for (NSInteger index = 0; index < self.cellIdentifiers.count; index++) { [tableView registerClass:self.cellClasses[index] forCellReuseIdentifier:self.cellIdentifiers[index]]; } tableView.delegate = self; tableView.dataSource = self; [self.view addSubview:tableView]; } #pragma mark - Getter - (NSArray *)cellIdentifiers { if (!_cellIdentifiers) { _cellIdentifiers = @[@"WCQAStyleTableViewCell", @"WCQBStyleTableViewCell", @"WCQCStyleTableViewCell"]; } return _cellIdentifiers; } - (NSArray *)cellClasses { if (!_cellClasses) { _cellClasses = @[[WCQAStyleTableViewCell class], [WCQBStyleTableViewCell class], [WCQCStyleTableViewCell class]];; } return _cellClasses; }
5.总结
工厂模式总体在同一类型差异性较小的子类之间以抽象基类作为其返回类型来适应未来新增产品的动态调整,由于具有同样的接口,我们可以在新增产品类型时尽可能保障原有客户端代码逻辑的稳定性。同时,由于各自类型的产品的初始化方案都已隔离进各自的工厂方法中,避免了牵一发而动其身的尴尬境地。
以上为本人个人理解与分享,如有错误欢迎指出
作者:KeepMoveingOn
链接:https://www.jianshu.com/p/7fbbf5e3ba6b