[关闭]
@qidiandasheng 2021-01-11T13:59:03.000000Z 字数 13723 阅读 860

设计模式(一):创建型模式之工厂(😁)

架构


❤简单工厂模式

定义

简单工厂模式(Simple Factory Pattern):专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常都具有共同的父类。

简单工厂模式又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。

适用场景

如果我们希望将一些为数不多的类似的对象的创建和他们的创建细节分离开,也不需要知道对象的具体类型,可以使用简单工厂模式。

举个形象点的例子:在前端开发中,常常会使用外观各式各样的按钮:比如有的按钮有圆角,有的按钮有阴影,有的按钮有边框,有的按钮无边框等等。但是因为同一种样式的按钮可以出现在项目的很多地方,所以如果在每个地方都把创建按钮的逻辑写一遍的话显然是会造成代码的重复(而且由于业务的原因有的按钮的创建逻辑能比较复杂,代码量大)。

那么为了避免重复代码的产生,我们可以将这些创建按钮的逻辑都放在一个“工厂”里面,让这个工厂来根据你的需求(传入的参数)来创建对应的按钮并返回给你。这样一来,同样类型的按钮在多个地方使用的时候,就可以只给这个工厂传入其对应的参数并拿到返回的按钮即可。

成员与类图

成员

简单工厂模式的结构比较简单,一共只有三个成员:

模式类图

导出图片Fri Apr 10 2020 14_47_44 GMT+0800 (中国标准时间).png-204.6kB

代码示例

场景概述

举一个店铺售卖不同品牌手机的例子:店铺,即客户端类向手机工厂购进手机售卖。

该场景可以使用简单工厂的角色来设计:

代码实现

抽象产品类Phone:

  1. //================== Phone.h ==================
  2. @interface Phone : NSObject
  3. //package to store
  4. - (void)packaging;
  5. @end

具体产品类:

  1. //================== IPhone ==================
  2. @interface IPhone : Phone
  3. @end
  4. @implementation IPhone
  5. - (void)packaging{
  6. NSLog(@"IPhone has been packaged");
  7. }
  8. @end
  9. //================== MIPhone ==================
  10. @interface MIPhone : Phone
  11. @end
  12. @implementation MIPhone
  13. - (void)packaging{
  14. NSLog(@"MIPhone has been packaged");
  15. }
  16. @end
  17. //================== HWPhone.h ==================
  18. @interface HWPhone : Phone
  19. @end
  20. @implementation HWPhone
  21. - (void)packaging{
  22. NSLog(@"HUAWEI Phone has been packaged");
  23. }
  24. @end

以上是抽象产品类以及它的三个子类:苹果手机类,小米手机类和华为手机类。 下面看一下工厂类 PhoneFactory

  1. //================== PhoneFactory.h ==================
  2. @interface PhoneFactory : NSObject
  3. + (Phone *)createPhoneWithTag:(NSString *)tag;
  4. @end
  5. //================== PhoneFactory.m ==================
  6. #import "IPhone.h"
  7. #import "MIPhone.h"
  8. #import "HWPhone.h"
  9. @implementation PhoneFactory
  10. + (Phone *)createPhoneWithTag:(NSString *)tag{
  11. if ([tag isEqualToString:@"i"]) {
  12. IPhone *iphone = [[IPhone alloc] init];
  13. return iphone;
  14. }else if ([tag isEqualToString:@"MI"]){
  15. MIPhone *miPhone = [[MIPhone alloc] init];
  16. return miPhone;
  17. }else if ([tag isEqualToString:@"HW"]){
  18. HWPhone *hwPhone = [[HWPhone alloc] init];
  19. return hwPhone;
  20. }else{
  21. return nil;
  22. }
  23. }
  24. @end

工厂类向外部(客户端)提供了一个创造手机的接口createPhoneWithTag:,根据传入参数的不同可以返回不同的具体产品类。因此客户端只需要知道它所需要的产品所对应的参数即可获得对应的产品了。

在本例中,我们声明了店铺类 Store为客户端类:

  1. //================== Store.h ==================
  2. #import "Phone.h"
  3. @interface Store : NSObject
  4. - (void)sellPhone:(Phone *)phone;
  5. @end
  6. //================== Store.m ==================
  7. @implementation Store
  8. - (void)sellPhone:(Phone *)phone{
  9. NSLog(@"Store begins to sell phone:%@",[phone class]);
  10. }
  11. @end

客户端类声明了一个售卖手机的接口sellPhone:。表示它可以售卖作为参数所传入的手机。

最后我们用代码模拟一下这个实际场景:

  1. //================== Using by client ==================
  2. //1. A phone store wants to sell iPhone
  3. Store *phoneStore = [[Store alloc] init];
  4. //2. create phone
  5. Phone *iPhone = [PhoneFactory createPhoneWithTag:@"i"];
  6. //3. package phone to store
  7. [iphone packaging];
  8. //4. store sells phone after receving it
  9. [phoneStore sellPhone:iphone];

上面代码的解读:

1.最开始实例化一个商店,商店打算卖苹果手机
2.商店委托工厂给他制作一台iPhone手机,传入对应的字段i。
3.手机生产好以后打包送到商店
4.商店售卖手机

在这里我们需要注意的是:商店从工厂拿到手机不需要了解手机制作的过程,只需要知道它要工厂做的是手机(只知道Phone类即可),和需要给工厂类传入它所需手机所对应的参数即可(这里的iPhone手机对应的参数就是i)。

代码对应的类图

导出图片Fri Apr 10 2020 15_00_46 GMT+0800 (中国标准时间).png-188.1kB

代码示例二

场景概述

比如我们的一个APP中的一个页面是用TableViewCell来展示的,但是网络接口中提供的数据是A、B、C三种不同类型的数据,A为纯文字,B为文字+图片,C为纯图片。并且这三种类型的数据是按不规律的形式出现的,那么我们在利用model为cell赋值的时候,cell的展现形式要与model类的数据形式相匹配,要注册不同类型的的cell用来展现然后再进行类型判断。

简单工厂模式的原理就是只创建一个model类和cell类,然后以这两个类为父类创建不同的子类,在子类中根据接口数据分配的类型重写model的数据cell的布局等;重写model和cell父类的初始化方法,使用不用的子类的类型来创建父类,总结起来就是一句话:根据不同需求生产不同的产品。

代码实现

model:

  1. //.h文件
  2. #import <Foundation/Foundation.h>
  3. @interface Model : NSObject
  4. +(instancetype)initWithDictionary:(NSDictionary *)dictionary;
  5. @end
  6. //.m文件
  7. #import "Model.h"
  8. #import "First.h"
  9. #import "Second.h"
  10. #import "Third.h"
  11. @implementation Model
  12. //根据字典内提供的数据分别创建出对应的model来获取数据
  13. +(instancetype)initWithDictionary:(NSDictionary *)dictionary{
  14. //先使用父类创建对象;
  15. Model *rootModel = nil;
  16. //根据字典中的key对应的数据初始化不同的子类对象并将其返回给父类
  17. if ([dictionary[@"tag"] isEqualToString:@"1"]) {
  18. rootModel = [[First alloc]init];
  19. }else if ([dictionary[@"tag"] isEqualToString:@"2"]){
  20. rootModel = [[Second alloc]init];
  21. }else if ([dictionary[@"tag"] isEqualToString:@"3"]){
  22. rootModel = [[Third alloc]init];
  23. }
  24. [rootModel setValuesForKeysWithDictionary:dictionary];
  25. return rootModel;
  26. }
  27. -(void)setValue:(id)value forUndefinedKey:(NSString *)key
  28. {
  29. }
  30. @end

cell的父类:

  1. //.h文件中
  2. #import <UIKit/UIKit.h>
  3. @class Model;
  4. @interface MyTableViewCell : UITableViewCell
  5. //在父类中声明出一个model类对象,在它的子类中重写set方法,在set方法内部去实现赋值操作
  6. @property(nonatomic,strong) Model *model;
  7. //根据不同类型的model创建不同的cell
  8. +(instancetype)initWithModel:(Model *)model;
  9. @end
  10. //.m文件中
  11. #import "MyTableViewCell.h"
  12. @implementation MyTableViewCell
  13. +(instancetype)initWithModel:(Model *)model{
  14. //根据OC函数获取model类名并将其转化为OC字符串
  15. NSString *modelName = [NSString stringWithUTF8String:object_getClassName(model)];
  16. //使用model的类名拼接一个"cell"来获取到cell类名
  17. NSString *cellName = [modelName stringByAppendingString:@"TableViewCell"];
  18. //根据提供的cellName来给予不同cell所对应的'cell子类'初始化一个cell对象返回给我们的父类对象
  19. //唯一标示符可以使我们所提供的model来给与不同cell所对应的标示符来重用
  20. MyTableViewCell *cell = [[NSClassFromString(cellName) alloc]initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:modelName];
  21. return cell;
  22. }
  23. @end

使用:

  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
  2. //根据indexPath.row获取对应的model
  3. Model *model = [self.dataArray objectAtIndex:indexPath.row];
  4. //根据取出的modle获取对应的类名
  5. NSString *modelName = [NSString stringWithUTF8String:object_getClassName(model)];
  6. //根据不同的唯一标识重用不同的cell
  7. MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:modelName];
  8. if (cell == nil) {
  9. //根据我们每行提供的model创建出对应的cell(根据不同的需求生产不同的产品)
  10. cell = [MyTableViewCell initWithModel:model];
  11. }
  12. //赋值
  13. [cell setModel:model];
  14. return cell;
  15. }

优点

缺点

iOS SDK的应用

❤工厂方法模式

定义

工厂方法模式(Factory Method Pattern)又称为工厂模式,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,即通过不同的工厂子类来创建不同的产品对象。

适用场景

工厂方法模式的适用场景与简单工厂类似,都是创建数据和行为比较类似的对象。但是和简单工厂不同的是:在工厂方法模式中,因为创建对象的责任移交给了抽象工厂的子类,因此客户端需要知道其所需产品所对应的工厂子类,而不是简单工厂中的参数。

成员与类图

成员

工厂方法模式包含四个成员:

  1. 抽象工厂(Abstract Factory):抽象工厂负责声明具体工厂的创建产品的接口。
  2. 具体工厂(Concrete Factory):具体工厂负责创建产品。
  3. 抽象产品(Abstract Product):抽象产品是工厂所创建的所有产品对象的父类,负责声明所有产品实例所共有的公共接口。
  4. 具体产品(Concrete Product):具体产品是工厂所创建的所有产品对象类,它以自己的方式来实现其共同父类声明的接口。

模式类图

导出图片Fri Apr 10 2020 15_12_58 GMT+0800 (中国标准时间).png-241.5kB

从类图中我们可以看到:抽象工厂负责定义具体工厂必须实现的接口,而创建产品对象的任务则交给具体工厂,由特定的子工厂来创建其对应的产品。

这使得工厂方法模式可以允许系统在不修改原有工厂的情况下引进新产品:只需要创建新产品类和其所对应的工厂类即可。

代码示例

场景概述

同样也是模拟上面的简单工厂例子中的场景(手机商店卖手机),但是由于这次是由工厂方法模式来实现的,因此在代码设计上会有变化。

与简单工厂模式不同的是:简单工厂模式里面只有一个工厂,而工厂方法模式里面有一个抽象工厂和继承于它的具体工厂。

因此同样的三个品牌的手机,我们可以通过三个不同的具体工厂:苹果手机工厂(IPhoneFactory),小米手机工厂 (MIPhoneFactory),华为手机工厂(HWPhoneFactory)来生产。而这些具体工厂类都会继承于抽象手机工厂类:PhoneFactory,它来声明生产手机的接口。

代码实现

首先我们声明一个抽象工厂类 PhoneFactory

  1. //================== PhoneFactory.h ==================
  2. #import "Phone.h"
  3. @interface PhoneFactory : NSObject
  4. + (Phone *)createPhone;
  5. @end
  6. //================== PhoneFactory.m ==================
  7. @implementation PhoneFactory
  8. + (Phone *)createPhone{
  9. //implemented by subclass
  10. return nil;
  11. }
  12. @end

抽象工厂类给具体工厂提供了生产手机的接口,因此不同的具体工厂可以按照自己的方式来生产手机。

下面看一下具体工厂:

  1. //================== 苹果手机工厂IPhoneFactory ==================
  2. @interface IPhoneFactory : PhoneFactory
  3. @end
  4. #import "IPhone.h"
  5. @implementation IPhoneFactory
  6. + (Phone *)createPhone{
  7. IPhone *iphone = [[IPhone alloc] init];
  8. NSLog(@"iPhone has been created");
  9. return iphone;
  10. }
  11. @end
  12. //================== 小米手机工厂 MIPhoneFactory ==================
  13. @interface MPhoneFactory : PhoneFactory
  14. @end
  15. #import "MiPhone.h"
  16. @implementation MPhoneFactory
  17. + (Phone *)createPhone{
  18. MiPhone *miPhone = [[MiPhone alloc] init];
  19. NSLog(@"MIPhone has been created");
  20. return miPhone;
  21. }
  22. @end
  23. //================== 华为手机工厂 HWPhoneFactory ==================
  24. @interface HWPhoneFactory : PhoneFactory
  25. @end
  26. #import "HWPhone.h"
  27. @implementation HWPhoneFactory
  28. + (Phone *)createPhone{
  29. HWPhone *hwPhone = [[HWPhone alloc] init];
  30. NSLog(@"HWPhone has been created");
  31. return hwPhone;
  32. }
  33. @end

以上就是声明的抽象工厂类和具体工厂类。因为生产手机的责任分配给了各个具体工厂类,因此客户端只需要委托所需手机所对应的工厂就可以获得其生产的手机了。

下面我们用代码模拟一下该场景:

  1. //================== Using by client ==================
  2. //A phone store
  3. Store *phoneStore = [[Store alloc] init];
  4. //phoneStore wants to sell iphone
  5. Phone *iphone = [IPhoneFactory createPhone];
  6. [iphone packaging];
  7. [phoneStore sellPhone:iphone];
  8. //phoneStore wants to sell MIPhone
  9. Phone *miPhone = [MPhoneFactory createPhone];
  10. [miPhone packaging];
  11. [phoneStore sellPhone:miPhone];
  12. //phoneStore wants to sell HWPhone
  13. Phone *hwPhone = [HWPhoneFactory createPhone];
  14. [hwPhone packaging];
  15. [phoneStore sellPhone:hwPhone];

由上面的代码可以看出:客户端phoneStore只需委托iPhone,MIPhone,HWPhone对应的工厂即可获得对应的手机了。

而且以后如果增加其他牌子的手机,例如魅族手机,就可以声明一个魅族手机类和魅族手机的工厂类并实现createPhone这个方法即可,而不需要改动原有已经声明好的各个手机类和具体工厂类。

下面我们看一下该例子对应的 UML类图,可以更直观地看一下各个成员之间的关系:

代码对应的类图

导出图片Fri Apr 10 2020 15_21_54 GMT+0800 (中国标准时间).png-271kB

优点

缺点

iOS SDK中的应用

抽象工厂模式

定义

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

适用场景

有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。比如系统中有多于一个的产品族,而每次只使用其中某一产品族,属于同一个产品族的产品将在一起使用。

在这里说一下产品族和产品等级结构的概念:

用一张图来帮助理解:

导出图片Fri Apr 10 2020 15_25_43 GMT+0800 (中国标准时间).png-99.6kB

在上图中:

下面再举一个例子帮助大家理解:

我们将小米,华为,苹果公司比作抽象工厂方法里的工厂:这三个工厂都有自己生产的手机,平板和电脑。
那么小米手机,小米平板,小米电脑就属于小米这个工厂的产品族;同样适用于华为工厂和苹果工厂。
而小米手机,华为手机,苹果手机则属于同一产品等级结构:手机的产品等级结构;平板和电脑也是如此。

结合这个例子对上面的图做一个修改可以更形象地理解抽象工厂方法的设计:

导出图片Fri Apr 10 2020 15_27_53 GMT+0800 (中国标准时间).png-147.4kB

成员与类图

成员

抽象工厂模式的成员和工厂方法模式的成员是一样的,只不过抽象工厂方法里的工厂是面向产品族的。
  1. 抽象工厂(Abstract Factory):抽象工厂负责声明具体工厂的创建产品族内的所有产品的接口。
  2. 具体工厂(Concrete Factory):具体工厂负责创建产品族内的产品。
  3. 抽象产品(Abstract Product):抽象产品是工厂所创建的所有产品对象的父类,负责声明所有产品实例所共有的公共接口。
  4. 具体产品(Concrete Product):具体产品是工厂所创建的所有产品对象类,它以自己的方式来实现其共同父类声明的接口。

模式类图

导出图片Fri Apr 10 2020 15_29_41 GMT+0800 (中国标准时间).png-261.6kB

代码示例

场景概述

由于抽象工厂方法里的工厂是面向产品族的,所以为了贴合抽象工厂方法的特点,我们将上面的场景做一下调整:在上面两个例子中,商店只卖手机。在这个例子中我们让商店也卖电脑:分别是苹果电脑,小米电脑,华为电脑。

场景分析

如果我们还是套用上面介绍过的工厂方法模式来实现该场景的话,则需要创建三个电脑产品对应的工厂:苹果电脑工厂,小米电脑工厂,华为电脑工厂。这就导致类的个数直线上升,以后如果还增加其他的产品,还需要添加其对应的工厂类,这显然是不够优雅的。

仔细看一下这六个产品的特点,我们可以把这它们划分在三个产品族里面:

  1. 苹果产品族:苹果手机,苹果电脑
  2. 小米产品族:小米手机,小米电脑
  3. 华为产品族:华为手机,华为电脑

而抽象方法恰恰是面向产品族设计的,因此该场景适合使用的是抽象工厂方法。下面结合代码来看一下该如何设计。

代码实现

首先引入电脑的基类和各个品牌的电脑类:

电脑基类:

  1. //================== Computer.h ==================
  2. @interface Computer : NSObject
  3. //package to store
  4. - (void)packaging;
  5. @end
  6. //================== Computer.m ==================
  7. @implementation Computer
  8. - (void)packaging{
  9. //implemented by subclass
  10. }
  11. @end

电脑品牌具体类:

  1. //================== 苹果电脑类 MacBookComputer ==================
  2. @interface MacBookComputer : Computer
  3. @end
  4. @implementation MacBookComputer
  5. - (void)packaging{
  6. NSLog(@"MacBookComputer has been packaged");
  7. }
  8. @end
  9. //================== 小米电脑类 MIComputer ==================
  10. @interface MIComputer : Computer
  11. @end
  12. @implementation MIComputer
  13. - (void)packaging{
  14. NSLog(@"MIComputer has been packaged");
  15. }
  16. @end
  17. //================== 华为电脑类 MateBookComputer ==================
  18. @interface MateBookComputer : Computer
  19. @end
  20. @implementation MateBookComputer
  21. - (void)packaging{
  22. NSLog(@"MateBookComputer has been packaged");
  23. }
  24. @end

引入电脑相关产品类以后,我们需要重新设计工厂类。因为抽象工厂方法模式的工厂是面向产品族的,所以抽象工厂方法模式里的工厂所创建的是同一产品族的产品。下面我们看一下抽象工厂方法模式的工厂该如何设计:

首先创建所有工厂都需要集成的抽象工厂,它声明了生产同一产品族的所有产品的接口:

  1. //================== Factory.h ==================
  2. #import "Phone.h"
  3. #import "Computer.h"
  4. @interface Factory : NSObject
  5. + (Phone *)createPhone;
  6. + (Computer *)createComputer;
  7. @end
  8. //================== Factory.m ==================
  9. @implementation Factory
  10. + (Phone *)createPhone{
  11. //implemented by subclass
  12. return nil;
  13. }
  14. + (Computer *)createComputer{
  15. //implemented by subclass
  16. return nil;
  17. }
  18. @end

接着,根据不同的产品族,我们创建不同的具体工厂:

  1. //================== 苹果产品族工厂 AppleFactory ==================
  2. @interface AppleFactory : Factory
  3. @end
  4. #import "IPhone.h"
  5. #import "MacBookComputer.h"
  6. @implementation AppleFactory
  7. + (Phone *)createPhone{
  8. IPhone *iPhone = [[IPhone alloc] init];
  9. NSLog(@"iPhone has been created");
  10. return iPhone;
  11. }
  12. + (Computer *)createComputer{
  13. MacBookComputer *macbook = [[MacBookComputer alloc] init];
  14. NSLog(@"Macbook has been created");
  15. return macbook;
  16. }
  17. @end
  18. //================== 小米产品族工厂 MIFactory ==================
  19. @interface MIFactory : Factory
  20. @end
  21. #import "MIPhone.h"
  22. #import "MIComputer.h"
  23. @implementation MIFactory
  24. + (Phone *)createPhone{
  25. MIPhone *miPhone = [[MIPhone alloc] init];
  26. NSLog(@"MIPhone has been created");
  27. return miPhone;
  28. }
  29. + (Computer *)createComputer{
  30. MIComputer *miComputer = [[MIComputer alloc] init];
  31. NSLog(@"MIComputer has been created");
  32. return miComputer;
  33. }
  34. @end
  35. //================== 华为产品族工厂 HWFactory ==================
  36. @interface HWFactory : Factory
  37. @end
  38. #import "HWPhone.h"
  39. #import "MateBookComputer.h"
  40. @implementation HWFactory
  41. + (Phone *)createPhone{
  42. HWPhone *hwPhone = [[HWPhone alloc] init];
  43. NSLog(@"HWPhone has been created");
  44. return hwPhone;
  45. }
  46. + (Computer *)createComputer{
  47. MateBookComputer *hwComputer = [[MateBookComputer alloc] init];
  48. NSLog(@"HWComputer has been created");
  49. return hwComputer;
  50. }
  51. @end

以上就是工厂类的设计。这样设计好之后,客户端如果需要哪一产品族的某个产品的话,只需要找到对应产品族工厂后,调用生产该产品的接口即可。假如需要苹果电脑,只需要委托苹果工厂来制造苹果电脑即可;如果需要小米手机,只需要委托小米工厂制造小米手机即可。

下面用代码来模拟一下这个场景:

  1. //================== Using by client ==================
  2. Store *store = [[Store alloc] init];
  3. //Store wants to sell MacBook
  4. Computer *macBook = [AppleFactory createComputer];
  5. [macBook packaging];
  6. [store sellComputer:macBook];
  7. //Store wants to sell MIPhone
  8. Phone *miPhone = [MIFactory createPhone];
  9. [miPhone packaging];
  10. [store sellPhone:miPhone];
  11. //Store wants to sell MateBook
  12. Computer *mateBook = [HWFactory createComputer];
  13. [mateBook packaging];
  14. [store sellComputer:mateBook];

上面的代码就是模拟了商店售卖苹果电脑,小米手机,华为电脑的场景。而今后如果该商店引入了新品牌的产品,比如联想手机,联想电脑,那么我们只需要新增联想手机类,联想电脑类,联想工厂类即可。

代码对应的类图

导出图片Fri Apr 10 2020 15_38_45 GMT+0800 (中国标准时间).png-269.6kB

由于三个工厂的产品总数过多,因此在这里只体现了苹果工厂和小米工厂的产品。

优点

缺点

工厂总结

工厂的作用

工厂的复杂度

大体上看,简单工厂模式,工厂方法模式和抽象工厂模式的复杂程度是逐渐升高的。

在实际开发过程中,我们需要根据业务场景的复杂程度的不同来采用最适合的工厂模式。

参考

面向对象设计的设计模式(一):创建型模式(附 Demo & UML类图)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注