工厂设计模式
文章目录
- 工厂设计模式
- 设计模式概念
- 设计模式七大准则
- 开闭原则
- 单⼀职责原则
- 里氏替换原则
- 依赖倒转原则
- 接口隔离原则
- 迪米特法则
- 合成复用原则
- 类族模式
- 简单工厂模式
- 优点
- 缺点
- 主要作用
- 示例
- 文件分类
- 实现效果:
- 工厂方法模式
- 优点
- 缺点
- 主要作用:
- 示例:
- 文件分类
- 实现效果:
- 抽象工厂方法
- 缺点
- 主要作用:
- 示例:
- 文件分类
- 实现效果
- git链接
设计模式概念
所谓设计模式(Design pattern) 是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的 高内聚 和 低耦合。可以这么说,计算机中设计模式指的是一套广为人知、被反复使用、经过分类编目的代码设计经验。使用设计模式是为了可重用代码,让代码更容易被他人理解,最重要的是保证代码可靠性。
设计模式七大准则
开闭原则
开闭原则的核心是:对扩展开放,对修改关闭。在程序需要进⾏拓展的时候,不能去修改原有的代码,⽽是要扩展原有代码,实现⼀个热插拔的效果。所以⼀句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使⽤接⼝和抽象类等,大部分具体设计模式中反复应用这一原则。
单⼀职责原则
一个类只做一件事。每个类应该实现单⼀的职责,如若不然,就应该把类拆分。
比如:UIView负责事件的传递、响应,CALayer负责视图的显示、动画,他们各自都有自己的单一职责。
里氏替换原则
里氏替换原则的主要内容:任何基类可以出现的地方,子类⼀定可以出现。该原则是继承复⽤的基⽯,只有当衍⽣类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。原则上,⼦类对父类的方法尽量不要重写和重载,因为⽗类代表了定义好的结构,通过这个规范的接⼝与外界交互。除非万不得已,⼦类不应该随便破坏它。
依赖倒转原则
面向接口编程,依赖于抽象而不依赖于具体。高层模块不应该依赖底层模块,二者都应该依赖其抽象;我们可以依赖抽象类也可以依赖接口,但是不要依赖具体的子类或者实现类。依赖倒转原则是开闭原则的基础。
接口隔离原则
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使⽤多个隔离的接口,⽐使用单个接口(多个接口⽅法集合到⼀个的接口)要好。
迪米特法则
迪米特法则的核心为:⼀个类对自己依赖的类知道的越少越好。也就是说⽆论被依赖的类多么复杂,都应该将逻辑封装在⽅法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
最少知道原则的另⼀个表达⽅式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现成员变量、⽅法参数、⽅法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。
合成复用原则
合成复用原则是尽量⾸先使用合成/聚合的⽅式,而不是使用继承。此原则和里氏替换原则氏相辅相成的,两者都是详细实现"开-闭"原则的规范。我们先看什么是合成和聚合:
合成
合成是指一个总体对依托他而存在的关系,如一个人对他的房子和家具。该关系依赖性不强,比如人没了,这个关系就自然消失了。
聚合
聚合是比合成关系更强的一种依赖关系,如有一台汽车,汽车对引擎、轮胎的关系就是聚合关系。这些关系就是带有聚合性质的。车没了,该车的引擎和轮胎自然也没了。在我们的设计中,这种关系不应该频繁出现,因为这样会增大设计的耦合度。
明确了合成和聚合关系,再来理解合成复用原则应该就清楚了:我们要尽量找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
类族模式
在之前学习小蓝书的时候我们提到过类族模式,在学习工厂模式之前我们先来看看类族模式,具体关于类族模式可以看看我之前博客:
【Effective Objective - C】—— 读书笔记(二)
其中类族模式最重要的一点作用是:把实现细节隐藏在一套简单的公共接口后面,在学习完工厂模式之后,我们会发现,工厂模式和类族模式在目的上和实现细节上有很大的相同点。
简单工厂模式
专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常具有共同的父类。(总结来说就是把一大堆if-else判断由业务层放到工厂类里面)。
优点
- 根据约定好的参数就可以获取所需要的对象,而不需要知道其创建的细节。减少了系统的耦合度。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,减少开发者的记忆成本。
缺点
- 如果业务上添加新产品的话,就需要修改工厂类原有的判断逻辑,这其实是违背了开闭原则的。
- 在产品类型较多时,有可能造成工厂逻辑过于复杂。所以简单工厂模式比较适合产品种类比较少而且增多的概率很低的情况。
主要作用
通过引入工厂类,使对象的创建和使用分离了。这样的好处是它们可以独立的变化,易维护和扩展。
客户端依赖抽象基类(接口),而不是具体的类,降低了耦合度。
- 有一组相似的对象,需要集中统一创建时。
- 创建对象的过程较为复杂时。
- 对象很多,并且有扩展需求时。
- 客户端不需要知道创建对象的过程时。
- 客户端使用的对象存在变动的可能,或者根本不知道使用哪一个具体对象时。
示例
工厂类中核心代码
通过传递进来的字符串来确定生成的类:
// PhoneFactory.m
NSArray *array = @[@"vivo", @"oppo", @"xiaomi", @"apple"];
+ (id)createPhone:(NSString*)phoneType {
switch ([array indexOfObject:phoneType]) {
case 0:
return [[Vivo alloc] init];
break;
case 1:
return [[Oppo alloc] init];
break;
case 2:
return [[Xiaomi alloc] init];
break;
case 3:
return [[Apple alloc] init];
break;
default:
break;
}
return nil;
}
其中通过工厂类返回的各种类(各种型号手机)必须遵守以下协议:
@protocol PhoneDelegate <NSObject>
- (void)phoneWays;
@end
在各类中实现此方法
#import "Vivo.h"
@implementation Vivo
- (void)phoneWays {
NSLog(@"vivo");
}
@end
文件分类
实现效果:
分析一下,简单工厂方法和类族模式主要区别就是,类族模式实现各子类方法通过继承去重写父类方法,而简单工厂方法中生成的各类和工厂类并不是父子类关系,通过协议来完成各方法。
工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到了子类。就像Cocoa Touch中的NSNumber的numberWithBool和numberWithInt方法,他们传入不同类型的参数,获得NSNumber实例。
优点
- 和直接创建具体对象相比,使用工厂方法创建对象算是最佳的做法。
- 根据所需产品找对应工厂进行生产,不关心产品细节,也不需要知道产品类的类名。
- 当系统中加入新产品时,不需要修改抽象工厂和抽象产品提供的接口,也无须修改客户端和其他的具体工厂和具体产品,而只要添加一个具体工厂和与其对应的具体产品就可以了,符合了“开-闭”原则。
缺点
当系统中加入新产品时,除了需要提供新的产品类之外,还要提供与其对应的具体工厂类。因此系统中类的个数将成对增加,增加了系统的复杂度。
主要作用:
- 编译时无法准确预期需要创建对象的类。
- 类想要其子类决定在运行时创建什么类型的实例。
- 类有若干辅助类为其子类,而你想将返回哪个子类这种信息局部化。
示例:
这个较之前的相比,工厂类中并没有类方法,只有一些方法,简单工厂方法是通过协议的方法去令其他类完成方法,而工厂方法模式是通过继承,令其他类继承工厂类,去重写父类的这几个方法,来看看代码:
工厂类:
// PhoneCenter.m
@implementation PhoneCenter
- (void)beginProductionPhone {
NSLog(@"begin");
}
- (void)succeedProductionPhone {
NSLog(@"succeed");
}
@end
子类:
@interface Vivo : PhoneCenter
@end
@implementation Vivo
- (void)beginProductionPhone {
NSLog(@"begin vivo");
}
- (void)succeedProductionPhone {
NSLog(@"succeed vivo");
}
@end
在viewController中初始化时以父类编译,子类运行
PhoneCenter *a = [[Vivo alloc] init];
[a beginProductionPhone];
[a succeedProductionPhone];
文件分类
实现效果:
抽象工厂方法
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个抽象产品类。抽象工厂模式里每个工厂都会生产多种产品,但不同工厂生产的产品属于不同的系列。抽象工厂模式可以用来解决多产品族的问题。
缺点
- 增加新的产品种类困难,它需要修改抽象工厂的接口。
- 代码结构比较复杂。
主要作用:
- 类想让其子类决定在运行时创建什么,无法在编译时准确确定
- 类有若干个辅助类为其子类,而你想将返回某个子类这一信息局部化
示例:
再下面例子中,我们先来看看文件分类:
文件分类
这次的分类就较之前复杂了很多,我们来一步步看:
首先Manager相当于最大的工厂类,通过这个函数来确定是哪个工厂,apple厂还是google厂:
// FactoryManager.m
+ (BaseFactory *)factoryWithType:(KFactoryType)factoryType {
if (factoryType == KApple) {
return [[AppleFactory alloc] init];
} else if (factoryType == KGoogle) {
return [[GoogleFactory alloc] init];;
}
return nil;
}
接下来到了工厂这步:
首先有一个作为两家工厂的父类,两家工厂继承于此类,并重写此基础类的各方法,以此来展示不同的效果:
// BaseFactory.h
@interface BaseFactory : NSObject
- (BasePhone*)createPhone;
- (BaseWatch*)createWatch;
@end
// BaseFactory.m
@implementation BaseFactory
- (BasePhone*)createPhone {
return nil;
}
- (BaseWatch*)createWatch {
return nil;
}
@end
// AppleFactory.h
@interface AppleFactory : BaseFactory
@end
// AppleFactory.m
@implementation AppleFactory
- (BasePhone *)createPhone {
return [[ApplePhone alloc] init];
}
- (BaseWatch *)createWatch {
return [[AppleWatch alloc] init];
}
在下面的一步我们让apple和google厂分别可以生产手机和手表两种产品,这时候和上一步一样,有一个基础手机类和基础手表类:
// BasePhone.h
@interface BasePhone : NSObject
- (void)phoneCell;
@end
// BasePhone.m
@implementation BasePhone
- (void)phoneCell {
NSLog(@"This is a Phone");
}
@end
它的子类,除了继承并重写父类的方法,还添加了自己的方法:
// ApplePhone.h
@interface ApplePhone : BasePhone
- (void)applePhoneWays;
@end
// ApplePhone.m
@implementation ApplePhone
- (void)phoneCell {
NSLog(@"This an apple phone");
}
- (void)applePhoneWays {
NSLog(@"We can eat apple (phone)");
}
@end
其他类也和此相似
在ViewController中初始化,并执行各个方法:
GooglePhone *googlePhone = (GooglePhone *)[googleFactory createPhone];
//执行方法(1)
[googlePhone phoneCell];
[googlePhone googlePhoneWays];
//确定商品(2)
GoogleWatch *googleWatch = (GoogleWatch *)[googleFactory createWatch];
//执行方法(2)
[googleWatch watchCell];
[googleWatch googleWatchWays];
NSLog(@"-------------------------------------");
//确定工厂
BaseFactory *appleFactory = [FactoryManager factoryWithType:KApple];
//确定商品(1)
ApplePhone *applePhone = (ApplePhone *)[appleFactory createPhone];
//执行方法(1)
[applePhone phoneCell];
[applePhone applePhoneWays];
//确定商品(2)
AppleWatch *appleWatch = (AppleWatch *)[appleFactory createWatch];
//执行方法(2)
[appleWatch watchCell];
[appleWatch appleWatchWays];
实现效果
git链接
类族模式
工厂模式
这一年最后一篇博客了,希望在下一年里继续努力,早日实现梦想。