设计模式¶
面试点¶
六大设计原则,责任链,桥接,适配器,单例,命令
设计原则¶
1. 单一职责原则¶
一个类只负责一件事。比如 UIView 只负责事件传递,事件响应,CALayer 负责内容显示,动画。
解释: 每个类应该仅负责一项职责或功能。这样可以提高代码的可维护性,因为修改一项功能时不会影响其他功能。
示例:
// User.h
@interface User : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *email;
@end
// User.m
@implementation User
@end
// UserRepository.h
@interface UserRepository : NSObject
- (void)saveUser:(User *)user;
@end
// UserRepository.m
@implementation UserRepository
- (void)saveUser:(User *)user {
// 保存用户数据到数据库
NSLog(@"User saved: %@", user.name);
}
@end
2. 开闭原则¶
对修改关闭,对扩展开放
解释:当需要更改一个类的行为时,应该通过扩展类而不是修改已有的类来实现。这有助于保持系统的稳定性和可扩展性。
示例: 通过继承和多态实现不同形状的绘制,而不修改已有代码。
// Shape.h
@interface Shape : NSObject
- (void)draw;
@end
// Circle.h
@interface Circle : Shape
@end
// Circle.m
@implementation Circle
- (void)draw {
NSLog(@"Drawing a Circle");
}
@end
// Square.h
@interface Square : Shape
@end
// Square.m
@implementation Square
- (void)draw {
NSLog(@"Drawing a Square");
}
@end
// Drawing.h
@interface Drawing : NSObject
- (void)drawShapes:(NSArray<Shape *> *)shapes;
@end
// Drawing.m
@implementation Drawing
- (void)drawShapes:(NSArray<Shape *> *)shapes {
for (Shape *shape in shapes) {
[shape draw];
}
}
@end
3. 接口隔离原则¶
定义:客户端不应该依赖那些它不需要的接口。
解释:使用多个专门的协议,而不是一个庞大臃肿的协议
示例:
// Printer.h
@protocol Printer <NSObject>
- (void)printDocument:(NSString *)document;
@end
// Scanner.h
@protocol Scanner <NSObject>
- (void)scanDocument:(NSString *)document;
@end
// MultiFunctionPrinter.h
@interface MultiFunctionPrinter : NSObject <Printer, Scanner>
@end
// MultiFunctionPrinter.m
@implementation MultiFunctionPrinter
- (void)printDocument:(NSString *)document {
NSLog(@"Printing: %@", document);
}
- (void)scanDocument:(NSString *)document {
NSLog(@"Scanning: %@", document);
}
@end
// SimplePrinter.h
@interface SimplePrinter : NSObject <Printer>
@end
// SimplePrinter.m
@implementation SimplePrinter
- (void)printDocument:(NSString *)document {
NSLog(@"Printing: %@", document);
}
@end
4. 依赖倒置原则¶
高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
解释:通过依赖于抽象(接口或抽象类)而不是具体的实现类来减少模块之间的耦合。比如数据库的增删改查,定义好接口,上层业务调用都依赖于接口定义,你用什么数据库或者文件存储,对上层义务是感知不到的。
示例:
// Database.h
@protocol Database <NSObject>
- (void)connect;
- (void)insertData:(NSString *)data;
- (NSString *)fetchData;
- (void)deleteData:(NSString *)data;
@end
// MySQLDatabase.h
#import "Database.h"
@interface MySQLDatabase : NSObject <Database>
@end
// MySQLDatabase.m
@implementation MySQLDatabase
- (void)connect {
NSLog(@"Connecting to MySQL Database");
}
- (void)insertData:(NSString *)data {
NSLog(@"Inserting data into MySQL Database: %@", data);
}
- (NSString *)fetchData {
NSLog(@"Fetching data from MySQL Database");
return @"Data from MySQL Database";
}
- (void)deleteData:(NSString *)data {
NSLog(@"Deleting data from MySQL Database: %@", data);
}
@end
// SQLiteDatabase.h
#import "Database.h"
@interface SQLiteDatabase : NSObject <Database>
@end
// SQLiteDatabase.m
@implementation SQLiteDatabase
- (void)connect {
NSLog(@"Connecting to SQLite Database");
}
- (void)insertData:(NSString *)data {
NSLog(@"Inserting data into SQLite Database: %@", data);
}
- (NSString *)fetchData {
NSLog(@"Fetching data from SQLite Database");
return @"Data from SQLite Database";
}
- (void)deleteData:(NSString *)data {
NSLog(@"Deleting data from SQLite Database: %@", data);
}
@end
// Application.h
#import "Database.h"
@interface Application : NSObject
- (instancetype)initWithDatabase:(id<Database>)database;
- (void)performDatabaseOperations;
@end
// Application.m
@implementation Application {
id<Database> _database;
}
- (instancetype)initWithDatabase:(id<Database>)database {
if (self = [super init]) {
_database = database;
}
return self;
}
- (void)performDatabaseOperations {
[_database connect];
[_database insertData:@"Sample Data"];
NSString *data = [_database fetchData];
NSLog(@"Fetched Data: %@", data);
[_database deleteData:@"Sample Data"];
}
@end
#import <Foundation/Foundation.h>
#import "Application.h"
#import "MySQLDatabase.h"
#import "SQLiteDatabase.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 使用MySQL数据库
id<Database> mySQLDatabase = [[MySQLDatabase alloc] init];
Application *appWithMySQL = [[Application alloc] initWithDatabase:mySQLDatabase];
[appWithMySQL performDatabaseOperations];
// 使用SQLite数据库
id<Database> sqliteDatabase = [[SQLiteDatabase alloc] init];
Application *appWithSQLite = [[Application alloc] initWithDatabase:sqliteDatabase];
[appWithSQLite performDatabaseOperations];
}
return 0;
}
5. 里氏替换原则¶
父类可以被子类无缝替换,且原有功能不受任何影响。
解释:子类应能够替换其父类,而不会影响程序的正确性。即子类应保持父类行为的完整性。
示例:
// Shape.h
@interface Shape : NSObject
- (float)area;
@end
// Rectangle.h
@interface Rectangle : Shape
@property (nonatomic) float width;
@property (nonatomic) float height;
@end
// Rectangle.m
@implementation Rectangle
- (float)area {
return self.width * self.height;
}
@end
// Square.h
@interface Square : Shape
@property (nonatomic) float sideLength;
@end
// Square.m
@implementation Square
- (void)setSideLength:(float)sideLength {
_sideLength = sideLength;
}
- (float)area {
return self.sideLength * self.sideLength;
}
@end
// ShapePrinter.h
@interface ShapePrinter : NSObject
- (void)printAreaOfShape:(Shape *)shape;
@end
// ShapePrinter.m
@implementation ShapePrinter
- (void)printAreaOfShape:(Shape *)shape {
NSLog(@"Area: %f", [shape area]);
}
@end
// Main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
Rectangle *rect = [Rectangle new];
rect.width = 4;
rect.height = 5;
Square *square = [Square new];
square.sideLength = 4;
ShapePrinter *printer = [ShapePrinter new];
[printer printAreaOfShape:rect]; // Area: 20.000000
[printer printAreaOfShape:square]; // Area: 16.000000
}
return 0;
}
6. 迪米特法则¶
一个对象应当对其他对象有尽可能少的了解
高内聚,低耦合
解释:对象之间的交互应尽量减少,仅限于直接的朋友,避免过多的依赖关系。
示例:Car类对Engine类的详细实现不知情,只是使用其公开接口。
// Engine.h
@interface Engine : NSObject
- (void)start;
@end
// Engine.m
@implementation Engine
- (void)start {
NSLog(@"Engine started");
}
@end
// Car.h
@interface Car : NSObject
- (void)start;
@end
// Car.m
@implementation Car {
Engine *_engine;
}
- (instancetype)init {
if (self = [super init]) {
_engine = [Engine new];
}
return self;
}
- (void)start {
[_engine start];
}
@end
责任链¶
责任链模式(Chain of Responsibility)是一种行为设计模式,它允许多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合。该模式沿着包含多个处理对象的链传递请求,直到有一个对象处理它为止。
关键点¶
- 处理链:创建一条包含多个处理对象的链。
- 传递请求:每个处理对象都有一个指向下一个处理对象的引用。请求沿着链传递,直到被某个对象处理为止。
- 灵活性:可以在运行时通过动态改变链的成员或顺序来改变系统的行为。
示例¶
假设我们有一个客户服务系统,用户的请求可以是普通请求、技术请求或高级请求。我们使用责任链模式来处理这些请求。
Step 1: 定义处理请求的抽象处理者
// Request.h
@interface Request : NSObject
@property (nonatomic, strong) NSString *type;
@property (nonatomic, strong) NSString *content;
- (instancetype)initWithType:(NSString *)type content:(NSString *)content;
@end
@implementation Request
- (instancetype)initWithType:(NSString *)type content:(NSString *)content {
self = [super init];
if (self) {
self.type = type;
self.content = content;
}
return self;
}
@end
// Handler.h
@interface Handler : NSObject
@property (nonatomic, strong) Handler *nextHandler;
- (void)handleRequest:(Request *)request;
@end
@implementation Handler
- (void)handleRequest:(Request *)request {
if (self.nextHandler) {
[self.nextHandler handleRequest:request];
} else {
NSLog(@"No handler available for request: %@", request.content);
}
}
@end
Step 2: 定义具体的处理者
// GeneralHandler.h
@interface GeneralHandler : Handler
@end
@implementation GeneralHandler
- (void)handleRequest:(Request *)request {
if ([request.type isEqualToString:@"General"]) {
NSLog(@"GeneralHandler handling request: %@", request.content);
} else {
[super handleRequest:request];
}
}
@end
// TechnicalHandler.h
@interface TechnicalHandler : Handler
@end
@implementation TechnicalHandler
- (void)handleRequest:(Request *)request {
if ([request.type isEqualToString:@"Technical"]) {
NSLog(@"TechnicalHandler handling request: %@", request.content);
} else {
[super.handleRequest:request];
}
}
@end
// AdvancedHandler.h
@interface AdvancedHandler : Handler
@end
@implementation AdvancedHandler
- (void)handleRequest:(Request *)request {
if ([request.type isEqualToString:@"Advanced"]) {
NSLog(@"AdvancedHandler handling request: %@", request.content);
} else {
[super.handleRequest:request];
}
}
@end
Step 3: 创建处理链并处理请求
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 创建处理者
GeneralHandler *generalHandler = [GeneralHandler new];
TechnicalHandler *technicalHandler = [TechnicalHandler new];
AdvancedHandler *advancedHandler = [AdvancedHandler new];
// 设置责任链
generalHandler.nextHandler = technicalHandler;
technicalHandler.nextHandler = advancedHandler;
// 创建请求
Request *generalRequest = [[Request alloc] initWithType:@"General" content:@"General inquiry"];
Request *technicalRequest = [[Request alloc] initWithType:@"Technical" content:@"Technical issue"];
Request *advancedRequest = [[Request alloc] initWithType:@"Advanced" content:@"Advanced problem"];
// 处理请求
[generalHandler handleRequest:generalRequest]; // GeneralHandler handling request: General inquiry
[generalHandler handleRequest:technicalRequest]; // TechnicalHandler handling request: Technical issue
[generalHandler handleRequest:advancedRequest]; // AdvancedHandler handling request: Advanced problem
}
return 0;
}
解释
- Handler:抽象处理者类,定义了处理请求的接口,并持有对下一个处理者的引用。
- GeneralHandler、TechnicalHandler、AdvancedHandler:具体的处理者类,实现了特定类型请求的处理逻辑。
- 请求传递:当一个请求到达时,处理者检查它是否能够处理该请求。如果可以,则处理请求;如果不可以,则将请求传递给下一个处理者。
优点¶
- 降低耦合度:请求的发送者和处理者解耦,发送者无需知道具体的处理者是谁。
- 动态组合:可以在运行时动态地增加或改变处理者链的顺序。
缺点¶
- 处理不保证:如果链的末端没有合适的处理者,请求可能得不到处理。
- 性能开销:长链可能导致性能问题,尤其是在请求大量传递时。
通过责任链模式,我们可以将请求处理逻辑分散到多个处理者中,使得系统更加灵活和可扩展。
桥接¶
桥接模式(Bridge Pattern)是一种结构型设计模式,它的主要目的是将抽象部分与它的实现部分分离,使它们可以独立地变化。桥接模式通过提供一个抽象层来隔离具体实现,达到了更灵活、更易于扩展的目的。
关键点¶
- 分离抽象和实现:通过将抽象部分和实现部分分离,使它们可以独立地变化。
- 组合而不是继承:使用组合关系来代替继承关系,使得抽象部分和实现部分可以动态组合。
示例 1¶
假设我们有一个绘图程序,支持不同的形状(如圆形和矩形)和不同的颜色(如红色和蓝色)。我们希望可以独立地扩展形状和颜色,而不需要修改现有的代码。
Step 1: 定义颜色的抽象接口
// Color.h
@protocol Color <NSObject>
- (void)applyColor;
@end
Step 2: 实现具体的颜色类
// RedColor.h
#import "Color.h"
@interface RedColor : NSObject <Color>
@end
@implementation RedColor
- (void)applyColor {
NSLog(@"Applying red color");
}
@end
// BlueColor.h
#import "Color.h"
@interface BlueColor : NSObject <Color>
@end
@implementation BlueColor
- (void)applyColor {
NSLog(@"Applying blue color");
}
@end
Step 3: 定义形状的抽象类
// Shape.h
#import "Color.h"
@interface Shape : NSObject
@property (nonatomic, strong) id<Color> color;
- (instancetype)initWithColor:(id<Color>)color;
- (void)draw;
@end
@implementation Shape
- (instancetype)initWithColor:(id<Color>)color {
self = [super init];
if (self) {
self.color = color;
}
return self;
}
- (void)draw {
// 抽象方法,子类实现
}
@end
Step 4: 实现具体的形状类
// Circle.h
#import "Shape.h"
@interface Circle : Shape
@end
@implementation Circle
- (void)draw {
NSLog(@"Drawing a circle");
[self.color applyColor];
}
@end
// Rectangle.h
#import "Shape.h"
@interface Rectangle : Shape
@end
@implementation Rectangle
- (void)draw {
NSLog(@"Drawing a rectangle");
[self.color applyColor];
}
@end
Step 5: 使用桥接模式
int main(int argc, const char * argv[]) {
@autoreleasepool {
id<Color> red = [RedColor new];
id<Color> blue = [BlueColor new];
Shape *redCircle = [[Circle alloc] initWithColor:red];
[redCircle draw]; // Drawing a circle, Applying red color
Shape *blueRectangle = [[Rectangle alloc] initWithColor:blue];
[blueRectangle draw]; // Drawing a rectangle, Applying blue color
}
return 0;
}
解释¶
- Color:颜色的抽象接口,定义了
applyColor
方法。 - RedColor 和 BlueColor:具体的颜色类,实现了
Color
接口。 - Shape:形状的抽象类,持有一个
Color
对象,并定义了draw
方法。 - Circle 和 Rectangle:具体的形状类,实现了
Shape
的draw
方法。 - 组合而不是继承:
Shape
持有一个Color
对象的引用,而不是通过继承来扩展颜色的功能。
优点¶
- 分离抽象和实现:可以独立地扩展形状和颜色,而不需要修改现有的代码。
- 降低耦合度:抽象部分和实现部分可以独立变化。
- 提高灵活性:可以动态地组合不同的形状和颜色。
缺点¶
- 复杂度增加:增加了系统的复杂性,需要更多的类和接口。
通过桥接模式,我们将形状和颜色的实现分离,使得它们可以独立地变化和扩展。这种设计提高了系统的灵活性和可维护性。
示例 2¶
#import <Foundation/Foundation.h>
#import "BaseObjectA.h"
#import "BaseObjectB.h"
NS_ASSUME_NONNULL_BEGIN
@interface BridgeDemo : NSObject
@property (nonatomic, strong) BaseObjectA *objA;
- (void)fetch;
@end
NS_ASSUME_NONNULL_END
#import "BridgeDemo.h"
#import "ObjectA1.h"
#import "ObjectA2.h"
#import "ObjectB1.h"
#import "ObjectB2.h"
@implementation BridgeDemo
// 根据实际业务使用哪套具体数据
/*
a1 --> b1,b2,b3
a2 --> b1,b2,b3
a3 --> b1,b2,b3
*/
- (void)fetch
{
// 创建一个具体的 class a
_objA = [[ObjectA1 alloc] init];
// 创建一个具体的 class b
BaseObjectB *b1 = [[ObjectB1 alloc] init];
_objA.objB = b1;
[_objA handle];
}
@end
#import <Foundation/Foundation.h>
#import "BaseObjectB.h"
NS_ASSUME_NONNULL_BEGIN
@interface BaseObjectA : NSObject
// 桥接模式的核心实现
@property (nonatomic, strong) BaseObjectB *objB;
// 获取数据
- (void)handle;
@end
NS_ASSUME_NONNULL_END
#import "BaseObjectA.h"
@implementation BaseObjectA
/*
a1 --> b1,b2,b3
a2 --> b1,b2,b3
a3 --> b1,b2,b3
*/
- (void)handle {
[self.objB fetchData];
}
@end
#import "BaseObjectA.h"
NS_ASSUME_NONNULL_BEGIN
@interface ObjectA1 : BaseObjectA
@end
NS_ASSUME_NONNULL_END
#import "ObjectA1.h"
@implementation ObjectA1
- (void)handle {
// before 业务逻辑操作
[super handle];
// after 业务逻辑操作
}
@end
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ObjectA2 : NSObject
@end
NS_ASSUME_NONNULL_END
#import "ObjectA2.h"
@implementation ObjectA2
@end
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface BaseObjectB : NSObject
- (void)fetchData;
@end
NS_ASSUME_NONNULL_END
#import "BaseObjectB.h"
@implementation BaseObjectB
- (void)fetchData {}
@end
#import "BaseObjectB.h"
NS_ASSUME_NONNULL_BEGIN
@interface ObjectB1 : BaseObjectB
@end
NS_ASSUME_NONNULL_END
#import "ObjectB1.h"
@implementation ObjectB1
- (void)fetchData {
// 第一套网络数据
}
@end
#import "BaseObjectB.h"
NS_ASSUME_NONNULL_BEGIN
@interface ObjectB2 : BaseObjectB
@end
NS_ASSUME_NONNULL_END
#import "ObjectB2.h"
@implementation ObjectB2
- (void)fetchData {
// 第二套网络数据
}
@end
这是一个经典的桥接模式(Bridge Pattern)的实现。桥接模式的主要目的是将抽象部分与实现部分分离,使它们可以独立地变化。
在这个示例中,我们有两类对象:BaseObjectA
和 BaseObjectB
,以及它们的具体实现类。BaseObjectA
持有一个 BaseObjectB
的引用,通过组合而不是继承来实现不同的功能。这种设计使得 BaseObjectA
和 BaseObjectB
可以独立扩展,而无需修改现有的代码。
详细分析
-
抽象类和具体实现类的定义
BaseObjectA
是抽象类,定义了handle
方法。ObjectA1
和ObjectA2
是BaseObjectA
的具体实现类。BaseObjectB
是抽象类,定义了fetchData
方法。ObjectB1
和ObjectB2
是BaseObjectB
的具体实现类。
-
桥接实现
BaseObjectA
持有一个BaseObjectB
的引用,这就是桥接模式的核心。通过这种组合关系,我们可以在BaseObjectA
的实现中调用BaseObjectB
的方法,从而将两者的实现分离。
-
具体的业务逻辑
BridgeDemo
类通过组合BaseObjectA
和BaseObjectB
来实现具体的业务逻辑。在fetch
方法中,它创建了ObjectA1
和ObjectB1
,并将ObjectB1
赋值给ObjectA1
的objB
属性,然后调用handle
方法。- 这种设计使得我们可以轻松地将
ObjectB1
替换为ObjectB2
,而无需修改ObjectA1
的代码,反之亦然。
总结
- 分离抽象和实现:
BaseObjectA
和BaseObjectB
分别定义了抽象部分,具体的实现由它们的子类ObjectA1
、ObjectA2
和ObjectB1
、ObjectB2
完成。 - 组合而不是继承:
BaseObjectA
持有BaseObjectB
的引用,实现了组合关系,而不是通过继承来实现功能扩展。 - 灵活性:这种设计使得我们可以轻松地扩展和修改
BaseObjectA
和BaseObjectB
的具体实现,而不需要改动现有的代码。
通过桥接模式,我们可以创建一个灵活、易于扩展和维护的系统结构。
适配器¶
一个现有的类需要适应变化的问题
- 对象适配器
- 类适配器
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Target : NSObject
- (void)request;
@end
NS_ASSUME_NONNULL_END
#import "Target.h"
@implementation Target
- (void)request {
// 原有逻辑
}
@end
#import <Foundation/Foundation.h>
#import "Target.h"
NS_ASSUME_NONNULL_BEGIN
@interface CoolTarget : NSObject
@property (nonatomic, strong) Target *target;
- (void)request;
@end
NS_ASSUME_NONNULL_END
#import "CoolTarget.h"
@implementation CoolTarget
- (void)request {
// 额外处理
[self.target request];
// 额外处理
}
@end
单例¶
单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。单例模式在 Objective-C 中的实现非常常见,用于管理共享资源,如配置对象、网络连接对象等。
单例模式的实现步骤
- 声明一个类方法来获取*单例实例。
- 确保类的构造方法私有化,以防止通过 alloc 和 init 方法创建新的实例。
- 创建一个静态变量来存储单例实例。
在 Objective-C 中,allocWithZone:
和 copyWithZone:
方法用于创建对象的新实例。然而,为了确保单例模式的唯一性,我们需要防止使用这些方法创建新的实例。你可以通过重写这些方法来确保单例模式的唯一性。
重写¶
首先,我们需要重写 allocWithZone:
方法来确保在调用 alloc
创建实例时返回同一个单例实例。其次,我们还需要实现 copyWithZone:
方法来防止创建单例的副本。
以下是一个示例,展示如何在单例类中重写这些方法:
示例类¶
#import <Foundation/Foundation.h>
@interface MySingleton : NSObject <NSCopying>
+ (instancetype)sharedInstance;
@end
@implementation MySingleton
+ (instancetype)sharedInstance {
static MySingleton *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
return sharedInstance;
}
+ (id)allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (instancetype)init {
self = [super init];
if (self) {
// 初始化代码
}
return self;
}
@end
说明¶
- 静态变量:
sharedInstance
是一个静态变量,用于存储单例实例。
static MySingleton *sharedInstance = nil;
dispatch_once
:dispatch_once
确保代码块只执行一次,从而保证单例实例只创建一次。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
- 重写
allocWithZone:
:重写allocWithZone:
方法确保即使通过alloc
方法创建对象,也返回同一个单例实例。
+ (id)allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
- 重写
copyWithZone:
:实现NSCopying
协议并重写copyWithZone:
方法,确保即使通过复制方法创建对象,也返回同一个单例实例。
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- 初始化方法:重写
init
方法进行实例的初始化。
- (instancetype)init {
self = [super init];
if (self) {
// 初始化代码
}
return self;
}
示例¶
如何使用单例模式:
#import "MySingleton.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MySingleton *singleton1 = [MySingleton sharedInstance];
MySingleton *singleton2 = [MySingleton sharedInstance];
if (singleton1 == singleton2) {
NSLog(@"Both instances are the same");
} else {
NSLog(@"Instances are different");
}
// 测试 allocWithZone:
MySingleton *singleton3 = [[MySingleton alloc] init];
if (singleton1 == singleton3) {
NSLog(@"AllocWithZone instance is the same");
} else {
NSLog(@"AllocWithZone instances are different");
}
// 测试 copyWithZone:
MySingleton *singleton4 = [singleton1 copy];
if (singleton1 == singleton4) {
NSLog(@"CopyWithZone instance is the same");
} else {
NSLog(@"CopyWithZone instances are different");
}
}
return 0;
}
在这个示例中,singleton1
、singleton2
、singleton3
和 singleton4
都指向同一个 MySingleton
实例。因此会输出以下结果:
Both instances are the same
AllocWithZone instance is the same
CopyWithZone instance is the same
通过这种方式,重写 allocWithZone:
和 copyWithZone:
方法确保了即使通过 alloc
和 copy
方法创建实例,也始终返回同一个单例实例,从而保证了单例模式的唯一性。
命令¶
行为参数化,降低代码重合度。
命令模式是一种行为设计模式,它将请求封装成对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式的主要优点是解耦了命令的发出者和执行者,并且可以方便地添加新的命令。
命令模式结构¶
- 命令(Command):定义命令接口,声明执行的方法。
- 具体命令(ConcreteCommand):实现命令接口,定义接收者(Receiver)并实现执行方法。
- 调用者(Invoker):持有命令对象并在某个时间点调用命令对象的执行方法。
- 接收者(Receiver):执行命令请求的对象。
- 客户端(Client):创建具体的命令对象并设置其接收者。
在你的代码中,以下部分对应命令模式的各个组成部分:
- 命令(Command):
Command
类及其子类。 - 具体命令(ConcreteCommand):实现具体业务逻辑的命令子类(当前示例中未展示,但可以扩展
Command
类)。 - 调用者(Invoker):
CommandManger
类。 - 接收者(Receiver):实际执行命令的对象或逻辑(当前在
Command
类的execute
方法中)。 - 客户端(Client):使用
CommandManger
执行命令的地方(未展示,可以是某个视图控制器或其他业务逻辑)。
代码解析¶
Command 类
Command
类是一个抽象的命令类,定义了执行、取消和完成的方法:
@interface Command : NSObject
@property (nonatomic, copy, nullable) CommandCompletionCallBack completion;
- (void)execute;
- (void)cancel;
- (void)done;
@end
@implementation Command
- (void)execute {
[self done];
}
- (void)cancel {
self.completion = nil;
}
- (void)done {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.completion) {
self.completion(self);
}
self.completion = nil;
[[CommandManger sharedManager].arrayCommands removeObject:self];
});
}
@end
CommandManger 类
CommandManger
类是命令模式中的调用者,负责管理和调用命令:
@interface CommandManger : NSObject
@property (nonatomic, strong) NSMutableArray<Command *> *arrayCommands;
+ (instancetype)sharedManager;
+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack) completion;
+ (void)cancelCommand:(Command *)cmd;
@end
@implementation CommandManger
+ (instancetype)sharedManager {
static CommandManger *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[CommandManger alloc] init];
});
return manager;
}
- (instancetype)init {
self = [super init];
if (self) {
_arrayCommands = @[].mutableCopy;
}
return self;
}
+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack)completion {
if (cmd) {
if (![self _isExecutingCommand:cmd]) {
[[CommandManger sharedManager].arrayCommands addObject:cmd];
cmd.completion = completion;
[cmd execute];
}
}
}
+ (void)cancelCommand:(Command *)cmd {
if (cmd) {
[[CommandManger sharedManager].arrayCommands removeObject:cmd];
[cmd cancel];
}
}
+ (BOOL)_isExecutingCommand:(Command *)cmd {
if (cmd && [[self sharedManager].arrayCommands containsObject:cmd]) {
return YES;
}
return NO;
}
@end
工作原理¶
- 执行命令:
executeCommand:completion:
方法用于执行命令。它首先检查命令是否已经在执行,如果没有则将命令添加到arrayCommands
数组中,设置完成回调,并调用命令的execute
方法。 - 取消命令:
cancelCommand:
方法用于取消命令。它将命令从arrayCommands
数组中移除,并调用命令的cancel
方法。 - 完成命令:
done
方法用于标记命令完成,调用完成回调并从arrayCommands
数组中移除命令。
总结¶
命令模式在你的代码中很好地实现了对命令的管理和执行,解耦了命令的发出者和执行者。通过这种模式,你可以方便地扩展新的命令类型,并灵活地管理命令的执行和取消。