Objective-C中的设计模式:工厂模式与抽象工厂
一、工厂模式概述
在Objective - C编程中,工厂模式是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建和使用分离。这种模式通过一个工厂类来负责创建对象,而不是在客户端代码中直接实例化对象。这样做的好处是,如果对象的创建过程发生变化,比如需要更改初始化参数或者创建逻辑,只需要修改工厂类的代码,而不需要在所有使用该对象的地方进行修改,从而提高了代码的可维护性和可扩展性。
(一)简单工厂模式
简单工厂模式并不是一种正式的设计模式,它更像是一种编程习惯。它有一个工厂类,负责根据不同的条件创建不同类型的对象。
1. 示例场景
假设我们正在开发一个图形绘制程序,有圆形(Circle)和矩形(Rectangle)两种图形,我们希望通过一个工厂类来创建这些图形对象。
2. 代码实现
首先定义图形的基类Shape
:
#import <Foundation/Foundation.h>
@interface Shape : NSObject
- (void)draw;
@end
@implementation Shape
- (void)draw {
NSLog(@"Drawing a shape.");
}
@end
然后定义圆形类Circle
:
#import "Shape.h"
@interface Circle : Shape
@end
@implementation Circle
- (void)draw {
NSLog(@"Drawing a circle.");
}
@end
再定义矩形类Rectangle
:
#import "Shape.h"
@interface Rectangle : Shape
@end
@implementation Rectangle
- (void)draw {
NSLog(@"Drawing a rectangle.");
}
@end
接着创建简单工厂类ShapeFactory
:
#import <Foundation/Foundation.h>
#import "Shape.h"
#import "Circle.h"
#import "Rectangle.h"
@interface ShapeFactory : NSObject
+ (Shape *)createShapeWithType:(NSString *)type;
@end
@implementation ShapeFactory
+ (Shape *)createShapeWithType:(NSString *)type {
if ([type isEqualToString:@"circle"]) {
return [[Circle alloc] init];
} else if ([type isEqualToString:@"rectangle"]) {
return [[Rectangle alloc] init];
}
return nil;
}
@end
在客户端代码中使用简单工厂:
#import <Foundation/Foundation.h>
#import "ShapeFactory.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Shape *circle = [ShapeFactory createShapeWithType:@"circle"];
[circle draw];
Shape *rectangle = [ShapeFactory createShapeWithType:@"rectangle"];
[rectangle draw];
}
return 0;
}
3. 简单工厂模式的优缺点
优点:
- 工厂类包含了对象创建的逻辑,客户端只需要关心如何使用对象,而不需要知道对象创建的细节,提高了代码的可维护性。
- 当需要添加新的产品(如添加新的图形类型)时,只需要在工厂类中添加相应的创建逻辑,对客户端代码影响较小。
缺点:
- 工厂类的职责过重,随着产品类型的增加,工厂类的
createShapeWithType:
方法会变得非常庞大,难以维护。 - 违反了开闭原则,当添加新的产品类型时,需要修改工厂类的代码。
(二)工厂方法模式
工厂方法模式是在简单工厂模式的基础上进行改进,它将对象的创建逻辑延迟到子类中实现。工厂类不再负责具体产品的创建,而是定义一个创建产品的抽象方法,由具体的子类来实现这个方法创建相应的产品。
1. 示例场景
仍然以图形绘制程序为例,我们通过工厂方法模式来改进代码。
2. 代码实现
首先,图形相关的类Shape
、Circle
和Rectangle
保持不变。
定义抽象工厂类ShapeFactory
:
#import <Foundation/Foundation.h>
#import "Shape.h"
@interface ShapeFactory : NSObject
- (Shape *)createShape;
@end
@implementation ShapeFactory
- (Shape *)createShape {
return nil;
}
@end
然后创建具体的工厂子类CircleFactory
:
#import "ShapeFactory.h"
#import "Circle.h"
@interface CircleFactory : ShapeFactory
@end
@implementation CircleFactory
- (Shape *)createShape {
return [[Circle alloc] init];
}
@end
再创建RectangleFactory
:
#import "ShapeFactory.h"
#import "Rectangle.h"
@interface RectangleFactory : ShapeFactory
@end
@implementation RectangleFactory
- (Shape *)createShape {
return [[Rectangle alloc] init];
}
@end
在客户端代码中使用工厂方法模式:
#import <Foundation/Foundation.h>
#import "CircleFactory.h"
#import "RectangleFactory.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
ShapeFactory *circleFactory = [[CircleFactory alloc] init];
Shape *circle = [circleFactory createShape];
[circle draw];
ShapeFactory *rectangleFactory = [[RectangleFactory alloc] init];
Shape *rectangle = [rectangleFactory createShape];
[rectangle draw];
}
return 0;
}
3. 工厂方法模式的优缺点
优点:
- 符合开闭原则,当需要添加新的产品类型时,只需要创建一个新的具体工厂子类,而不需要修改现有的工厂类代码。
- 工厂类的职责更加单一,每个具体工厂子类只负责创建一种产品,提高了代码的可维护性和可扩展性。
缺点:
- 随着产品类型的增加,需要创建大量的具体工厂子类,导致代码量增加,项目结构变得复杂。
二、抽象工厂模式
抽象工厂模式是工厂模式的一种更高级的形式。它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式允许客户端在不了解具体产品类的情况下创建对象,并且可以轻松地切换产品族。
(一)抽象工厂模式的结构
- 抽象工厂(Abstract Factory):定义了创建一系列产品对象的抽象方法。
- 具体工厂(Concrete Factory):实现抽象工厂中定义的抽象方法,创建一组具体的产品对象。
- 抽象产品(Abstract Product):定义了产品的抽象接口。
- 具体产品(Concrete Product):实现抽象产品接口,具体的产品对象。
- 客户端(Client):使用抽象工厂创建产品对象,而不依赖具体的产品类和具体的工厂类。
(二)示例场景
假设我们正在开发一个跨平台的UI框架,需要创建不同平台(如iOS和安卓)的按钮(Button)和文本框(TextBox)。我们可以使用抽象工厂模式来创建这些UI组件。
(三)代码实现
- 定义抽象产品类
- 首先定义抽象按钮类
Button
:
- 首先定义抽象按钮类
#import <Foundation/Foundation.h>
@interface Button : NSObject
- (void)render;
@end
@implementation Button
- (void)render {
NSLog(@"Rendering a button.");
}
@end
- 再定义抽象文本框类`TextBox`:
#import <Foundation/Foundation.h>
@interface TextBox : NSObject
- (void)render;
@end
@implementation TextBox
- (void)render {
NSLog(@"Rendering a text box.");
}
@end
- 定义具体产品类
- iOS平台的按钮类
iOSButton
:
- iOS平台的按钮类
#import "Button.h"
@interface iOSButton : Button
@end
@implementation iOSButton
- (void)render {
NSLog(@"Rendering an iOS button.");
}
@end
- iOS平台的文本框类`iOSTextBox`:
#import "TextBox.h"
@interface iOSTextBox : TextBox
@end
@implementation iOSTextBox
- (void)render {
NSLog(@"Rendering an iOS text box.");
}
@end
- 安卓平台的按钮类`AndroidButton`:
#import "Button.h"
@interface AndroidButton : Button
@end
@implementation AndroidButton
- (void)render {
NSLog(@"Rendering an Android button.");
}
@end
- 安卓平台的文本框类`AndroidTextBox`:
#import "TextBox.h"
@interface AndroidTextBox : TextBox
@end
@implementation AndroidTextBox
- (void)render {
NSLog(@"Rendering an Android text box.");
}
@end
- 定义抽象工厂类
#import <Foundation/Foundation.h>
#import "Button.h"
#import "TextBox.h"
@interface GUIFactory : NSObject
- (Button *)createButton;
- (TextBox *)createTextBox;
@end
@implementation GUIFactory
- (Button *)createButton {
return nil;
}
- (TextBox *)createTextBox {
return nil;
}
@end
- 定义具体工厂类
- iOS工厂类
iOSGUIFactory
:
- iOS工厂类
#import "GUIFactory.h"
#import "iOSButton.h"
#import "iOSTextBox.h"
@interface iOSGUIFactory : GUIFactory
@end
@implementation iOSGUIFactory
- (Button *)createButton {
return [[iOSButton alloc] init];
}
- (TextBox *)createTextBox {
return [[iOSTextBox alloc] init];
}
@end
- 安卓工厂类`AndroidGUIFactory`:
#import "GUIFactory.h"
#import "AndroidButton.h"
#import "AndroidTextBox.h"
@interface AndroidGUIFactory : GUIFactory
@end
@implementation AndroidGUIFactory
- (Button *)createButton {
return [[AndroidButton alloc] init];
}
- (TextBox *)createTextBox {
return [[AndroidTextBox alloc] init];
}
@end
- 客户端代码
#import <Foundation/Foundation.h>
#import "iOSGUIFactory.h"
#import "AndroidGUIFactory.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 使用iOS工厂创建UI组件
GUIFactory *iOSFactory = [[iOSGUIFactory alloc] init];
Button *iOSButton = [iOSFactory createButton];
[iOSButton render];
TextBox *iOSTextBox = [iOSFactory createTextBox];
[iOSTextBox render];
// 使用安卓工厂创建UI组件
GUIFactory *androidFactory = [[AndroidGUIFactory alloc] init];
Button *androidButton = [androidFactory createButton];
[androidButton render];
TextBox *androidTextBox = [androidFactory createTextBox];
[androidTextBox render];
}
return 0;
}
(四)抽象工厂模式的优缺点
优点:
- 客户端不需要了解具体产品的创建细节,只需要和抽象工厂交互,降低了客户端和具体产品类之间的耦合度。
- 可以轻松地切换产品族,比如从iOS平台的UI组件切换到安卓平台的UI组件,只需要更换具体的工厂对象。
- 符合开闭原则,当需要添加新的产品族时,只需要创建新的具体工厂类,而不需要修改现有的代码。
缺点:
- 当产品族中的产品类型增加时,抽象工厂和具体工厂类的接口和实现都需要进行修改,违背了开闭原则。
- 抽象工厂模式的实现相对复杂,增加了系统的理解和维护成本。
三、工厂模式与抽象工厂模式的比较
- 创建对象的粒度
- 工厂模式:工厂方法模式主要创建单一类型的对象,简单工厂模式虽然可以根据不同条件创建不同类型对象,但本质上也是围绕同一类产品(如图形绘制示例中的各种图形)。
- 抽象工厂模式:创建一系列相关或相互依赖的对象,如UI框架示例中的按钮和文本框,这些对象构成一个产品族。
- 可维护性和可扩展性
- 工厂模式:工厂方法模式在添加新的产品类型时,通过创建新的具体工厂子类来实现,符合开闭原则,可维护性和可扩展性较好。简单工厂模式在添加新的产品类型时需要修改工厂类代码,可维护性和可扩展性较差。
- 抽象工厂模式:在添加新的产品族时,只需要创建新的具体工厂类,符合开闭原则,可维护性和可扩展性较好。但当产品族中的产品类型增加时,需要修改抽象工厂和具体工厂类的接口和实现,违背开闭原则,可维护性和扩展性较差。
- 应用场景
- 工厂模式:适用于对象创建过程相对简单,且只需要创建单一类型对象的场景。比如在游戏开发中创建不同类型的角色,角色之间相对独立,没有复杂的依赖关系,可以使用工厂方法模式。
- 抽象工厂模式:适用于需要创建一系列相关或相互依赖对象,并且需要在不同的产品族之间进行切换的场景。比如在跨平台应用开发中,创建不同平台的UI组件,这些组件之间可能存在一定的关联和依赖,适合使用抽象工厂模式。
四、实际应用案例
- iOS开发中的视图创建 在iOS开发中,视图控制器(ViewController)的视图加载过程可以使用工厂模式。例如,一个应用有多种不同风格的登录界面,如普通登录界面、第三方登录界面等。可以创建一个视图工厂类,根据不同的需求创建相应的登录视图。
#import <UIKit/UIKit.h>
// 抽象视图类
@interface LoginView : UIView
- (void)setupView;
@end
@implementation LoginView
- (void)setupView {
// 通用的视图设置代码
}
@end
// 普通登录视图
@interface NormalLoginView : LoginView
@end
@implementation NormalLoginView
- (void)setupView {
// 普通登录视图的设置代码
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
label.text = @"Normal Login";
[self addSubview:label];
}
@end
// 第三方登录视图
@interface ThirdPartyLoginView : LoginView
@end
@implementation ThirdPartyLoginView
- (void)setupView {
// 第三方登录视图的设置代码
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
label.text = @"Third - Party Login";
[self addSubview:label];
}
@end
// 视图工厂类
@interface LoginViewFactory : NSObject
+ (LoginView *)createLoginViewWithType:(NSString *)type;
@end
@implementation LoginViewFactory
+ (LoginView *)createLoginViewWithType:(NSString *)type {
if ([type isEqualToString:@"normal"]) {
return [[NormalLoginView alloc] init];
} else if ([type isEqualToString:@"thirdParty"]) {
return [[ThirdPartyLoginView alloc] init];
}
return nil;
}
@end
// 视图控制器
@interface LoginViewController : UIViewController
@end
@implementation LoginViewController
- (void)viewDidLoad {
[super viewDidLoad];
LoginView *loginView = [LoginViewFactory createLoginViewWithType:@"normal"];
[loginView setupView];
[self.view addSubview:loginView];
}
@end
- 游戏开发中的角色创建 在游戏开发中,创建不同类型的角色可以使用工厂方法模式。假设游戏中有战士(Warrior)、法师(Mage)等角色。
#import <Foundation/Foundation.h>
// 抽象角色类
@interface Character : NSObject
- (void)attack;
@end
@implementation Character
- (void)attack {
NSLog(@"Character is attacking.");
}
@end
// 战士类
@interface Warrior : Character
@end
@implementation Warrior
- (void)attack {
NSLog(@"Warrior is attacking with sword.");
}
@end
// 法师类
@interface Mage : Character
@end
@implementation Mage
- (void)attack {
NSLog(@"Mage is attacking with magic.");
}
@end
// 抽象角色工厂类
@interface CharacterFactory : NSObject
- (Character *)createCharacter;
@end
@implementation CharacterFactory
- (Character *)createCharacter {
return nil;
}
@end
// 战士工厂类
@interface WarriorFactory : CharacterFactory
@end
@implementation WarriorFactory
- (Character *)createCharacter {
return [[Warrior alloc] init];
}
@end
// 法师工厂类
@interface MageFactory : CharacterFactory
@end
@implementation MageFactory
- (Character *)createCharacter {
return [[Mage alloc] init];
}
@end
// 游戏场景类
@interface GameScene : NSObject
@property (nonatomic, strong) Character *character;
@end
@implementation GameScene
- (instancetype)initWithFactory:(CharacterFactory *)factory {
self = [super init];
if (self) {
self.character = [factory createCharacter];
}
return self;
}
- (void)startGame {
[self.character attack];
}
@end
// 使用示例
int main(int argc, const char * argv[]) {
@autoreleasepool {
CharacterFactory *warriorFactory = [[WarriorFactory alloc] init];
GameScene *warriorScene = [[GameScene alloc] initWithFactory:warriorFactory];
[warriorScene startGame];
CharacterFactory *mageFactory = [[MageFactory alloc] init];
GameScene *mageScene = [[GameScene alloc] initWithFactory:mageFactory];
[mageScene startGame];
}
return 0;
}
- 跨平台开发中的组件创建 在跨平台开发中,创建不同平台的文件系统操作组件可以使用抽象工厂模式。假设我们有iOS和安卓两个平台,每个平台有自己的文件读取(FileReader)和文件写入(FileWriter)组件。
#import <Foundation/Foundation.h>
// 抽象文件读取类
@interface FileReader : NSObject
- (NSString *)readFile:(NSString *)fileName;
@end
@implementation FileReader
- (NSString *)readFile:(NSString *)fileName {
return nil;
}
@end
// 抽象文件写入类
@interface FileWriter : NSObject
- (BOOL)writeFile:(NSString *)content toFile:(NSString *)fileName;
@end
@implementation FileWriter
- (BOOL)writeFile:(NSString *)content toFile:(NSString *)fileName {
return NO;
}
@end
// iOS文件读取类
@interface iOSFileReader : FileReader
@end
@implementation iOSFileReader
- (NSString *)readFile:(NSString *)fileName {
NSLog(@"Reading file on iOS: %@", fileName);
return @"iOS file content";
}
@end
// iOS文件写入类
@interface iOSFileWriter : FileWriter
@end
@implementation iOSFileWriter
- (BOOL)writeFile:(NSString *)content toFile:(NSString *)fileName {
NSLog(@"Writing file on iOS: %@ with content: %@", fileName, content);
return YES;
}
@end
// 安卓文件读取类
@interface AndroidFileReader : FileReader
@end
@implementation AndroidFileReader
- (NSString *)readFile:(NSString *)fileName {
NSLog(@"Reading file on Android: %@", fileName);
return @"Android file content";
}
@end
// 安卓文件写入类
@interface AndroidFileWriter : FileWriter
@end
@implementation AndroidFileWriter
- (BOOL)writeFile:(NSString *)content toFile:(NSString *)fileName {
NSLog(@"Writing file on Android: %@ with content: %@", fileName, content);
return YES;
}
@end
// 抽象文件系统工厂类
@interface FileSystemFactory : NSObject
- (FileReader *)createFileReader;
- (FileWriter *)createFileWriter;
@end
@implementation FileSystemFactory
- (FileReader *)createFileReader {
return nil;
}
- (FileWriter *)createFileWriter {
return nil;
}
@end
// iOS文件系统工厂类
@interface iOSFileSystemFactory : FileSystemFactory
@end
@implementation iOSFileSystemFactory
- (FileReader *)createFileReader {
return [[iOSFileReader alloc] init];
}
- (FileWriter *)createFileWriter {
return [[iOSFileWriter alloc] init];
}
@end
// 安卓文件系统工厂类
@interface AndroidFileSystemFactory : FileSystemFactory
@end
@implementation AndroidFileSystemFactory
- (FileReader *)createFileReader {
return [[AndroidFileReader alloc] init];
}
- (FileWriter *)createFileWriter {
return [[AndroidFileWriter alloc] init];
}
@end
// 应用类
@interface App : NSObject
@property (nonatomic, strong) FileReader *fileReader;
@property (nonatomic, strong) FileWriter *fileWriter;
@end
@implementation App
- (instancetype)initWithFactory:(FileSystemFactory *)factory {
self = [super init];
if (self) {
self.fileReader = [factory createFileReader];
self.fileWriter = [factory createFileWriter];
}
return self;
}
- (void)doFileOperations {
NSString *fileName = @"test.txt";
NSString *content = @"Some content";
[self.fileWriter writeFile:content toFile:fileName];
NSString *readContent = [self.fileReader readFile:fileName];
NSLog(@"Read content: %@", readContent);
}
@end
// 使用示例
int main(int argc, const char * argv[]) {
@autoreleasepool {
FileSystemFactory *iOSFactory = [[iOSFileSystemFactory alloc] init];
App *iOSApp = [[App alloc] initWithFactory:iOSFactory];
[iOSApp doFileOperations];
FileSystemFactory *androidFactory = [[AndroidFileSystemFactory alloc] init];
App *androidApp = [[App alloc] initWithFactory:androidFactory];
[androidApp doFileOperations];
}
return 0;
}
通过以上实际应用案例,可以更深入地理解工厂模式和抽象工厂模式在Objective - C开发中的应用场景和优势。在实际项目中,应根据具体的需求和场景选择合适的设计模式,以提高代码的可维护性、可扩展性和可复用性。