文章目录
- 一、设计模式
- 创建型模式
- 结构型模式
- 行为型模式
- 二、设计模式七大准则
- 三、简单工厂模式
- 四、工厂方法模式
- 五、抽象工厂模式
一、设计模式
设计模式是指在特定上下文中解决常见问题时所采用的一套可复用的解决方案。这些模式是面向对象编程中的通用概念,广泛应用于各种编程语言和平台。设计模式能够帮助开发者编写更加高效、可读性强、易于维护的代码。它们通常分为三大类:创建型模式、结构型模式和行为型模式。工厂模式属于创建型模式,这类模式关注于对象的创建过程,提供了创建对象的不同方式,以降低耦合度并提高代码的灵活性。
创建型模式
这类模式关注于对象的创建过程,提供了创建对象的不同方式,以降低耦合度并提高代码的灵活性。
- 单例模式:确保一个类只有一个实例,并提供一个全局访问点
- 工厂模式:提供一个创建对象的接口,但让子类决定实例化哪一个类。
- 抽象工厂模式:为创建一系列相关或相互依赖的对象提供一个接口,而无需指定它们具体的类。
- 建造者模式:分步骤构建复杂对象,用户只需指定类型和内容,不需知道内部构造细节。
- 原型模式:通过克隆已有对象来创建新对象,减少创建成本。
结构型模式
这类模式关注于如何组合类和对象以获得更大的结构,同时保持结构的灵活和高效。
- 适配器模式:将一个类的接口转换成客户期望的另一个接口。
- 桥接模式:将抽象部分与实现部分分离,使它们可以独立变化。
- 装饰模式:动态地给一个对象添加一些额外的职责,而不改变其结构。
- 组合模式:将对象组合成树形结构以表示“整体-部分”层次结构。
- 外观模式:为一组复杂的子系统提供一个统一的接口,以简化高层模块的使用。
行为型模式
这类模式关注于对象间的通信,以及职责的分配。
- 责任链模式:将请求沿着链传递,直到有对象处理它。
- 命令模式:将请求封装成对象,以便使用不同的请求、队列请求、日志请求等。
- 迭代器模式:提供一种方法顺序访问聚合对象的元素,而又不暴露其内部表示。
- 观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
- 策略模式:定义一系列算法,将它们一个个封装起来,并使它们可以相互替换。
- 模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。
- 访问者模式:在不改变数据结构的前提下,增加作用于这些元素的新操作。
二、设计模式七大准则
- 单一职责原则
每个类或者模块应该有且只有一个改变的理由。也就是说,一个类应当专注于做好一件事情,这样可以提高类的内聚性,减少耦合。
比如:UIView负责事件的传递、响应,CALayer负责视图的显示、动画,他们各自都有自己的单一职责。
- 开放封闭原则
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着当需要改变行为时,应该通过扩展而不是修改已有的代码来实现。
-
里氏替换原则
子类应当能够替换它们的基类并且不影响程序的正确性。换言之,使用基类的地方能够无差别地使用子类对象。 -
依赖倒置原则
高层模块不应依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。 -
接口隔离原则
客户端不应该被迫依赖它不需要的接口。接口应该尽量细化,避免臃肿的接口定义,客户端只依赖它实际需要的方法。 -
迪米特法则/ 最小知识原则
一个对象应当对其他对象有最少的了解。一个类应该只和它的朋友通信,不要和朋友的朋友通信,以减少耦合。 -
合成/聚合复用原则
尽量使用对象组合而非类继承来达到复用的目的。组合使得系统更加灵活,容易扩展和维护。
合成
合成是指一个总体对依托他而存在的关系,如一个人对他的房子和家具。该关系依赖性不强,比如人没了,这个关系就自然消失了。
聚合
聚合是比合成关系更强的一种依赖关系,如有一台汽车,汽车对引擎、轮胎的关系就是聚合关系。这些关系就是带有聚合性质的。车没了,该车的引擎和轮胎自然也没了。在我们的设计中,这种关系不应该频繁出现,因为这样会增大设计的耦合度。
明确了合成和聚合关系,再来理解合成复用原则应该就清楚了:我们要尽量找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
三、简单工厂模式
简单工厂模式(Simple Factory Pattern)是一种常用的设计模式,它属于创建型模式的一种。这种模式旨在为创建对象提供一个统一的接口,隐藏了创建对象的具体逻辑,使得客户端不需要知道所创建对象的具体类,从而降低了对象之间的耦合度,提高了代码的灵活性和可维护性。
专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常具有共同的父类。(总结来说就是把一大堆if-else判断由业务层放到工厂类里面)。
优点
- 根据约定好的参数就可以获取所需要的对象,而不需要知道其创建的细节。减少了系统的耦合度。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,减少开发者的记忆成本。
缺点
- 如果业务上添加新产品的话,就需要修改工厂类原有的判断逻辑,这其实是违背了开闭原则的。
- 在产品类型较多时,有可能造成工厂逻辑过于复杂。所以简单工厂模式比较适合产品种类比较少而且增多的概率很低的情况。
主要作用
通过引入工厂类,使对象的创建和使用分离了。这样的好处是它们可以独立的变化,易维护和扩展。
客户端依赖抽象基类(接口),而不是具体的类,降低了耦合度。
- 有一组相似的对象,需要集中统一创建时。
- 创建对象的过程较为复杂时。
- 对象很多,并且有扩展需求时。
- 客户端不需要知道创建对象的过程时。
- 客户端使用的对象存在变动的可能,或者根本不知道使用哪一个具体对象时。
文件分类
通过传递进来的字符串来确定生成的类:
//CYZActionFactory.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface CYZActionFactory : NSObject
+(id)createAction:(NSString*)actionType;
@end
NS_ASSUME_NONNULL_END
//CYZActionFactory.m
#import "CYZActionFactory.h"
#import "CYZWork.h"
#import "CYZPlay.h"
#import "CYZStudy.h"
@implementation CYZActionFactory
+(id)createAction:(NSString *)actionType {
if ([actionType isEqualToString:@"work"]) {
return [[CYZWork alloc] init];
} else if([actionType isEqualToString:@"play"]) {
return [[CYZPlay alloc] init];
} else {
return [[CYZStudy alloc] init];;
}
}
@end
其中通过工厂类返回的各种类(各种行为)必须遵守以下协议:
@protocol AcitonFactoryDelegate <NSObject>
- (void)actionSelect;
@end
在各类中实现此方法:
// CYZWork.m
#import "CYZWork.h"
@implementation CYZWork
- (void)actionSelect {
[self work];
}
- (void)work {
NSLog(@"I love work!");
}
@end
运行结果如下:
简单工厂方法和类族模式主要区别就是,类族模式实现各子类方法通过继承去重写父类方法,而简单工厂方法中生成的各类和工厂类并不是父子类关系,通过协议来完成各方法。
四、工厂方法模式
工厂方法模式(Factory Method Pattern)是iOS开发中一种常用的设计模式,属于创建型模式的一种。它为创建对象提供了一种灵活的方式,让子类来决定实例化哪一个类,从而实现了对象创建的延迟化和抽象化,增强了代码的扩展性和可维护性。
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到了子类。就像Cocoa Touch中的NSNumber的numberWithBool和numberWithInt方法,他们传入不同类型的参数,获得NSNumber实例。
优点
和直接创建具体对象相比,使用工厂方法创建对象算是最佳的做法。
根据所需产品找对应工厂进行生产,不关心产品细节,也不需要知道产品类的类名。
当系统中加入新产品时,不需要修改抽象工厂和抽象产品提供的接口,也无须修改客户端和其他的具体工厂和具体产品,而只要添加一个具体工厂和与其对应的具体产品就可以了,符合了“开-闭”原则。
缺点
当系统中加入新产品时,除了需要提供新的产品类之外,还要提供与其对应的具体工厂类。因此系统中类的个数将成对增加,增加了系统的复杂度。
主要作用:
编译时无法准确预期需要创建对象的类。
类想要其子类决定在运行时创建什么类型的实例。
类有若干辅助类为其子类,而你想将返回哪个子类这种信息局部化。
工厂类中并没有类方法,只有一些方法,简单工厂方法是通过协议的方法去令其他类完成方法,而工厂方法模式是通过继承,令其他类继承工厂类,去重写父类的这几个方法,下面是代码:
文件分类
工厂类:
//FactoryMethodClass.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface FactoryMethodClass : NSObject
- (void)createAction;
@end
NS_ASSUME_NONNULL_END
//FactoryMethodClass.m
#import "FactoryMethodClass.h"
@implementation FactoryMethodClass
- (void)createAction {
NSLog(@"action");
}
@end
子类:
//CYZWork.h
#import <Foundation/Foundation.h>
#import "FactoryMethodClass.h"
NS_ASSUME_NONNULL_BEGIN
@interface CYZWork : FactoryMethodClass
@end
NS_ASSUME_NONNULL_END
/CYZWork.m
#import "CYZWork.h"
@implementation CYZWork
- (void)createAction {
[self createWork];
}
- (void)createWork {
NSLog(@"I love work");
}
@end
在viewController中初始化时以父类编译,子类运行
if ([button.titleLabel.text isEqualToString:@"work"]) {
FactoryMethodClass* object = [[CYZWork alloc] init];
[object createAction];
} else if ([button.titleLabel.text isEqualToString:@"play"]) {
FactoryMethodClass* object = [[CYZPlay alloc] init];
[object createAction];
} else {
FactoryMethodClass* object = [[CYZStudy alloc] init];
[object createAction];
}
运行结果如下:
五、抽象工厂模式
抽象工厂方法(Abstract Factory Method)是设计模式中的一个概念,它属于创建型模式。抽象工厂模式是对简单工厂模式的进一步扩展,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。与简单工厂模式相比,抽象工厂模式可以生成多个产品族,每个族内有一系列相关的产品。
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个抽象产品类。抽象工厂模式里每个工厂都会生产多种产品,但不同工厂生产的产品属于不同的系列。抽象工厂模式可以用来解决多产品族的问题。
优点
- 为创建一系列相关或相互依赖的对象提供了一个统一的接口,使得客户端可以通过相同的接口来获取不同类型的对象,而无需直接实例化具体产品类。
- 通过抽象化产品创建过程,可以在不同的地方重用相同的工厂接口来创建对象,减少了代码重复,增加了代码的复用率。
缺点
- 增加新的产品种类困难,它需要修改抽象工厂的接口。
- 代码结构比较复杂。
主要作用:
- 类想让其子类决定在运行时创建什么,无法在编译时准确确定
- 类有若干个辅助类为其子类,而你想将返回某个子类这一信息局部化
文件分类
首先Manager相当于最大的工厂类,通过这个类里的函数来确定是哪个工厂,apple厂还是banana厂:
//FactoryManager.h
#import <Foundation/Foundation.h>
#import "BaseFactory.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, CYFactoryType) {
CYApple,
CYBanana
};
@interface FactoryManager : NSObject
+ (id)factoryWithType:(CYFactoryType)factoryType;
@end
NS_ASSUME_NONNULL_END
//FactoryManager.m
#import "FactoryManager.h"
#import "AppleFactory.h"
#import "BananaFactory.h"
@implementation FactoryManager
+ (id)factoryWithType:(CYFactoryType)factoryType {
if (factoryType == CYApple) {
return [[AppleFactory alloc] init];
} else if (factoryType == CYBanana) {
return [[BananaFactory alloc] init];;
}
return nil;
}
@end
接下来到了工厂这步:
首先有一个作为两家工厂的父类,两家工厂继承于此类,并重写此基础类的各方法,以此来展示不同的效果:
父类工厂:
//BaseFactory.h
#import <Foundation/Foundation.h>
#import "BasePhone.h"
#import "BasePad.h"
NS_ASSUME_NONNULL_BEGIN
@interface BaseFactory : NSObject
- (BasePhone*)createPhone;
- (BasePad*)createPad;
@end
NS_ASSUME_NONNULL_END
//BaseFatcory.m
#import "BaseFactory.h"
@implementation BaseFactory
- (BasePhone*)createPhone {
return nil;
}
- (BasePad*)createPad {
return nil;
}
@end
子类工厂:
//AppleFactory.h
#import <Foundation/Foundation.h>
#import "BaseFactory.h"
NS_ASSUME_NONNULL_BEGIN
@interface AppleFactory : BaseFactory
@end
NS_ASSUME_NONNULL_END
//AppleFactory.m
#import "AppleFactory.h"
#import "ApplePhone.h"
#import "ApplePad.h"
@implementation AppleFactory
- (BasePhone*)createPhone {
return [[ApplePhone alloc] init];
}
- (BasePad*)createPad {
return [[ApplePad alloc] init];
}
@end
//BananaFactory.h
#import <Foundation/Foundation.h>
#import "BaseFactory.h"
NS_ASSUME_NONNULL_BEGIN
@interface BananaFactory : BaseFactory
@end
NS_ASSUME_NONNULL_END
//BananaFactory.m
#import "BananaFactory.h"
#import "BananaPhone.h"
#import "BananaPad.h"
@implementation BananaFactory
- (BasePhone*)createPhone {
return [[BananaPhone alloc] init];
}
- (BasePad*)createPad {
return [[BananaPad alloc] init];
}
@end
在下面的一步我们让apple和banana厂分别可以生产手机和平板两种产品,这时候和上一步一样,有一个基础手机类和基础平板类:
基础手机类:
//BasePhone.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface BasePhone : NSObject
- (void)phoneCell;
@end
NS_ASSUME_NONNULL_END
//BasePhone.m
#import "BasePhone.h"
@implementation BasePhone
- (void)phoneCell {
NSLog(@"This is a Phone");
}
@end
苹果手机类:
//ApplePhone.h
#import <Foundation/Foundation.h>
#import "BasePhone.h"
NS_ASSUME_NONNULL_BEGIN
@interface ApplePhone : BasePhone
@end
NS_ASSUME_NONNULL_END
//ApplePhone.m
#import "ApplePhone.h"
@implementation ApplePhone
- (void)phoneCell {
NSLog(@"This is an applePhone");
}
@end
香蕉手机类:
//BananaPhone.h
#import <Foundation/Foundation.h>
#import "BasePhone.h"
NS_ASSUME_NONNULL_BEGIN
@interface BananaPhone : BasePhone
@end
NS_ASSUME_NONNULL_END
//BananaPhone.m
#import "BananaPhone.h"
@implementation BananaPhone
- (void)phoneCell {
NSLog(@"This is a bananaPhone");
}
@end
平板类和上面相似这里就不再赘述
下面是ViewContrroller类
#import "ViewController.h"
#import "FactoryManager.h"
#import "ApplePhone.h"
#import "AppleFactory.h"
#import "ApplePad.h"
#import "BananaFactory.h"
#import "BananaPhone.h"
#import "BananaPad.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
BaseFactory *appleFactory = [FactoryManager factoryWithType:CYApple];
ApplePhone* applePhone = (ApplePhone*)[appleFactory createPhone];
[applePhone phoneCell];
ApplePad* applePad = (ApplePad*)[appleFactory createPad];
[applePad padCell];
NSLog(@"-------------------------------------");
BaseFactory *bananaFactory = [FactoryManager factoryWithType:CYBanana];
BananaPhone *bananaPhone = (BananaPhone*)[bananaFactory createPhone];
[bananaPhone phoneCell];
BananaPad* bananaPad = (BananaPad*)[bananaFactory createPad];
[bananaPad padCell];
}
运行结果如下: