iOS -- 工厂设计模式
设计模式概念
所谓设计模式(Design pattern) 是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的 高内聚 和 低耦合。可以这么说,计算机中设计模式指的是一套广为人知、被反复使用、经过分类编目的代码设计经验。使用设计模式是为了可重用代码,让代码更容易被他人理解,最重要的是保证代码可靠性。
设计模式七大准则
- 单一职责原则 (Single Responsibility Principle):一个类应该只有一个引起它变化的原因。每个类应该负责单一的职责,这样可以提高类的内聚性和可维护性 。
比如:UIView负责事件的传递、响应,CALayer负责视图的显示、动画,他们各自都有自己的单一职责。 - 开放封闭原则(Open-Closed Principle):软件实体(类、模块、函数等)应该**对扩展开放,对修改封闭。**通过使用抽象和接口,可以在不修改现有代码的情况下扩展系统的功能。
- 里氏替换原则(Liskov Substitution Principle):子类对象应该能够替换其父类对象,而程序的行为不受影响。这个原则强调了继承关系的正确使用,确保子类符合父类的接口和行为。
- 依赖倒置原则(Dependency Inversion Principle):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象应该依赖于细节,而不是细节依赖于抽象。
- 接口隔离原则 (Interface Segregation Principle):客户端不应该被迫依赖它们不使用的接口。接口应该尽量小而精确,符合客户端的实际需求。
我们在 Objective-C 中实现了接口和类的定义,遵循了拆分接口的原则。每个类只需实现相关的方法,避免了不必要的方法实现,提高了代码的可读性和灵活性。 - 迪米特法则(Law of Demeter):一个对象应该与其他对象保持最少的知识联系。一个类应该尽量减少对其他类的依赖,只与直接相关的类进行交互。
- 组合/聚合复用原则 (Composition/Aggregation Reuse Principle):优先使用组合和聚合关系,而不是继承关系来实现代码的复用。通过组合和聚合,可以使得系统更灵活、可扩展和易于维护。
合成: 表示一个类拥有其他类的实例,并且这些实例是无法独立存在的一部分。当一个对象被销毁时,其组成部分也会被销毁。
objectivec
@interface Engine : NSObject
// 引擎类的定义
@end
@interface Car : NSObject
@property (nonatomic, strong) Engine *engine;
// 车类的定义,拥有一个引擎实例作为其一部分
@end
@implementation Car
// 类的实现
@end
Car类持有一个引擎(Engine)对象的实例作为其一部分。当Car对象被销毁时,引擎对象也会随之被销毁。
**聚合:**表示一个类与其他类之间存在关联关系,其中一个类是另一个类的容器或集合,但这些类之间的关系并不是强依赖的关系。当一个对象被销毁时,其聚合对象可以继续存在。
objectivec
@interface Student : NSObject
// 学生类的定义
@end
@interface Classroom : NSObject
@property (nonatomic, strong) NSArray<Student *> *students;
// 教室类的定义,包含了学生对象的集合
@end
@implementation Classroom
// 类的实现
@end
Classroom类包含一个学生对象的数组作为其一部分。当Classroom对象被销毁时,学生对象仍然可以存在,因为它们可以属于其他教室或独立存在。
合成和聚合都描述了类之间的关系,合成表示一个类拥有其他类的实例作为其一部分,而聚合表示一个类与其他类之间存在关联关系,其中一个类是另一个类的容器或集合。
简单工厂模式
专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常具有共同的父类。(总结来说就是把一大堆if-else判断由业务层放到工厂类里面)。
优点
- 根据约定好的参数就可以获取所需要的对象,而不需要知道其创建的细节。减少了系统的耦合度。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,减少开发者的记忆成本。
缺点
- 如果业务上添加新产品的话,就需要修改工厂类原有的判断逻辑,这其实是违背了开闭原则的。
- 在产品类型较多时,有可能造成工厂逻辑过于复杂。所以简单工厂模式比较适合产品种类比较少而且增多的概率很低的情况。
主要作用
它提供了一种通过一个工厂类来创建对象的方式,而不需要直接在客户端代码中实例化对象。简单工厂模式将对象的创建和使用分离,使得客户端代码更加灵活,并且可以降低代码的耦合度。
- 有一组相似的对象,需要集中统一创建时。
- 创建对象的过程较为复杂时。
- 对象很多,并且有扩展需求时。
- 客户端不需要知道创建对象的过程时。
- 客户端使用的对象存在变动的可能,或者根本不知道使用哪一个具体对象时。
示例
工厂类中的核心代码
通过传递进来的字符串来确定生成的类。
objectivec
// phoneFactory.m
// 简单工厂模式
//
// Created by mac on 2024/4/18.
//
#import "phoneFactory.h"
#import "Vivo.h"
#import "Oppo.h"
#import "Xiaomi.h"
#import "Apple.h"
@implementation phoneFactory
NSArray* array = @[@"Vivo", @"Oppo", @"Xiaomi", @"Apple"];
+ (nonnull id)createPhone:(nonnull 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];
default:
break;
}
return nil;
}
@end
其中通过工厂类返回的各种类(各种型号手机)必须遵守以下协议:
objectivec
@protocol phoneDelegate <NSObject>
- (void) phoneWays;
@end
然后在各个类中实现该方法
objectivec
#import "Vivo.h"
@implementation Vivo
- (void) phoneWays {
NSLog(@"Vivo");
}
@end
文件分类:
实现效果:
这样的模式就与类族模式有点相似,类族模式最重要的一点是:把实现细节隐藏在一套简单的公共接口后面,并且类之间为继承关系 。学习完工厂模式之后,简单工厂方法和类族模式主要的区别是:生成的各类和工厂类并不是父子类的关系,通过协议来完成各方法。
工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到了子类。就像Cocoa Touch中的NSNumber的numberWithBool和numberWithInt方法,他们传入不同类型的参数,获得NSNumber实例。
优点
- 和直接创建具体对象相比,使用工厂方法创建对象算是最佳的做法。
- 根据所需产品找对应工厂进行生产,不关心产品细节,也不需要知道产品类的类名。
- 当系统中加入新产品时,不需要修改抽象工厂和抽象产品提供的接口,也无须修改客户端和其他的具体工厂和具体产品,而只要添加一个具体工厂和与其对应的具体产品就可以了,符合了"开-闭"原则。
缺点
当系统中加入新产品时,除了需要提供新的产品类之外,还要提供与其对应的具体工厂类。因此系统中类的个数将成对增加,增加了系统的复杂度。
主要作用:
- 编译时无法准确预期需要创建对象的类。
- 类想要其子类决定在运行时创建什么类型的实例。
- 类有若干辅助类为其子类,而你想将返回哪个子类这种信息局部化。
示例:
这个较之前的相比,工厂类中并没有类方法 ,只有一些方法,简单工厂方法是通过协议的方法去令其他类完成方法,而工厂方法模式是通过继承,令其他类继承工厂类,去重写父类的这几个方法。 来看看代码:
工厂类:
objectivec
#import "phoneCenter.h"
@implementation phoneCenter
- (void) beginProductionPhone {
NSLog(@"begin");
}
- (void) succeedProductionPhone {
NSLog(@"succeed");
}
@end
子类:
objectivec
@interface Vivo : phoneCenter
@end
#import "Vivo.h"
@implementation Vivo
- (void) beginProductionPhone {
NSLog(@"begin vivo");
}
- (void) succeedProductionPhone {
NSLog(@"succeed vivo");
}
@end
在viewController中初始化时以父类编译,子类运行
objectivec
PhoneCenter *a = [[Vivo alloc] init];
[a beginProductionPhone];
[a succeedProductionPhone];
文件分类
效果实现:
抽象工厂方法
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个抽象产品类。抽象工厂模式里每个工厂都会生产多种产品,但不同工厂生产的产品属于不同的系列。抽象工厂模式可以用来解决多产品族的问题。
缺点
- 增加新的产品种类困难,它需要修改抽象工厂的接口。
- 代码结构比较复杂。
主要作用:
- 类想让其子类决定在运行时创建什么,无法在编译时准确确定。
- 类有若干个辅助类为其子类,而你想将返回某个子类这一信息局部化。
文件分类
这次的分类就较之前复杂了很多,我们来一步步看:
首先Manager相当于最大的工厂类,通过这个函数来确定是哪个工厂,apple厂还是google厂:
objectivec
#import "FactoryManager.h"
#import "GooleFactory.h"
#import "AppleFactory.h"
@implementation FactoryManager
+ (BaseFactory *)factoryWithType:(KFactoryType)factoryType {
if (factoryType == KApple) {
return [[AppleFactory alloc] init];
} else if (factoryType == KGoogle) {
return [[GooleFactory alloc] init];
}
return nil;
}
@end
接下来到了工厂这步:
首先有一个作为两家工厂的父类,两家工厂继承于此类,并重写此基础类的各方法,以此来展示不同的效果:
objectivec
#import <Foundation/Foundation.h>
#import "BasePhone.h"
#import "BaseWatch.h"
NS_ASSUME_NONNULL_BEGIN
@interface BaseFactory : NSObject
- (BasePhone*)createPhone;
- (BaseWatch*)createWatch;
@end
#import "BaseFactory.h"
@implementation BaseFactory
- (BasePhone *)createPhone {
return nil;
}
- (BaseWatch *)createWatch {
return nil;
}
@end
objectivec
@interface AppleFactory : BaseFactory
@end
@implementation AppleFactory
- (BasePhone *)createPhone {
return [[ApplePhone alloc] init];
}
- (BaseWatch *)createWatch {
return [[AppleWatch alloc] init];
}
@end
在下面的一步我们让apple和google厂分别可以生产手机和手表两种产品,这时候和上一步一样,有一个基础手机类和基础手表类:
objectivec
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface BasePhone : NSObject
- (void) phoneCell;
@end
@implementation BasePhone
- (void)phoneCell {
NSLog(@"This is a Phone");
}
@end
它的子类,除了继承并重写父类的方法,还添加了自己的方法:它的子类,除了继承并重写父类的方法,还添加了自己的方法:
objectivec
@interface ApplePhone : BasePhone
- (void)applePhoneWays;
@end
@implementation ApplePhone
- (void)phoneCell {
NSLog(@"This an apple phone");
}
- (void)applePhoneWays {
NSLog(@"We can eat apple (phone)");
}
@end
其他类也和此相似
在ViewController中初始化,并执行各个方法:
objectivec
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];
运行的结果: