Skip to content

设计模式

面试点

六大设计原则,责任链,桥接,适配器,单例,命令

设计原则

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)是一种行为设计模式,它允许多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合。该模式沿着包含多个处理对象的链传递请求,直到有一个对象处理它为止。

关键点

  1. 处理链:创建一条包含多个处理对象的链。
  2. 传递请求:每个处理对象都有一个指向下一个处理对象的引用。请求沿着链传递,直到被某个对象处理为止。
  3. 灵活性:可以在运行时通过动态改变链的成员或顺序来改变系统的行为。

示例

假设我们有一个客户服务系统,用户的请求可以是普通请求、技术请求或高级请求。我们使用责任链模式来处理这些请求。

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;
}

解释

  1. Handler:抽象处理者类,定义了处理请求的接口,并持有对下一个处理者的引用。
  2. GeneralHandler、TechnicalHandler、AdvancedHandler:具体的处理者类,实现了特定类型请求的处理逻辑。
  3. 请求传递:当一个请求到达时,处理者检查它是否能够处理该请求。如果可以,则处理请求;如果不可以,则将请求传递给下一个处理者。

优点

  1. 降低耦合度:请求的发送者和处理者解耦,发送者无需知道具体的处理者是谁。
  2. 动态组合:可以在运行时动态地增加或改变处理者链的顺序。

缺点

  1. 处理不保证:如果链的末端没有合适的处理者,请求可能得不到处理。
  2. 性能开销:长链可能导致性能问题,尤其是在请求大量传递时。

通过责任链模式,我们可以将请求处理逻辑分散到多个处理者中,使得系统更加灵活和可扩展。

桥接

桥接模式(Bridge Pattern)是一种结构型设计模式,它的主要目的是将抽象部分与它的实现部分分离,使它们可以独立地变化。桥接模式通过提供一个抽象层来隔离具体实现,达到了更灵活、更易于扩展的目的。

关键点

  1. 分离抽象和实现:通过将抽象部分和实现部分分离,使它们可以独立地变化。
  2. 组合而不是继承:使用组合关系来代替继承关系,使得抽象部分和实现部分可以动态组合。

示例 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;
}

解释

  1. Color:颜色的抽象接口,定义了 applyColor 方法。
  2. RedColor 和 BlueColor:具体的颜色类,实现了 Color 接口。
  3. Shape:形状的抽象类,持有一个 Color 对象,并定义了 draw 方法。
  4. Circle 和 Rectangle:具体的形状类,实现了 Shapedraw 方法。
  5. 组合而不是继承Shape 持有一个 Color 对象的引用,而不是通过继承来扩展颜色的功能。

优点

  1. 分离抽象和实现:可以独立地扩展形状和颜色,而不需要修改现有的代码。
  2. 降低耦合度:抽象部分和实现部分可以独立变化。
  3. 提高灵活性:可以动态地组合不同的形状和颜色。

缺点

  1. 复杂度增加:增加了系统的复杂性,需要更多的类和接口。

通过桥接模式,我们将形状和颜色的实现分离,使得它们可以独立地变化和扩展。这种设计提高了系统的灵活性和可维护性。

示例 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)的实现。桥接模式的主要目的是将抽象部分与实现部分分离,使它们可以独立地变化。

在这个示例中,我们有两类对象:BaseObjectABaseObjectB,以及它们的具体实现类。BaseObjectA 持有一个 BaseObjectB 的引用,通过组合而不是继承来实现不同的功能。这种设计使得 BaseObjectABaseObjectB 可以独立扩展,而无需修改现有的代码。

详细分析

  1. 抽象类和具体实现类的定义

    • BaseObjectA 是抽象类,定义了 handle 方法。
    • ObjectA1ObjectA2BaseObjectA 的具体实现类。
    • BaseObjectB 是抽象类,定义了 fetchData 方法。
    • ObjectB1ObjectB2BaseObjectB 的具体实现类。
  2. 桥接实现

    • BaseObjectA 持有一个 BaseObjectB 的引用,这就是桥接模式的核心。通过这种组合关系,我们可以在 BaseObjectA 的实现中调用 BaseObjectB 的方法,从而将两者的实现分离。
  3. 具体的业务逻辑

    • BridgeDemo 类通过组合 BaseObjectABaseObjectB 来实现具体的业务逻辑。在 fetch 方法中,它创建了 ObjectA1ObjectB1,并将 ObjectB1 赋值给 ObjectA1objB 属性,然后调用 handle 方法。
    • 这种设计使得我们可以轻松地将 ObjectB1 替换为 ObjectB2,而无需修改 ObjectA1 的代码,反之亦然。

总结

  • 分离抽象和实现BaseObjectABaseObjectB 分别定义了抽象部分,具体的实现由它们的子类 ObjectA1ObjectA2ObjectB1ObjectB2 完成。
  • 组合而不是继承BaseObjectA 持有 BaseObjectB 的引用,实现了组合关系,而不是通过继承来实现功能扩展。
  • 灵活性:这种设计使得我们可以轻松地扩展和修改 BaseObjectABaseObjectB 的具体实现,而不需要改动现有的代码。

通过桥接模式,我们可以创建一个灵活、易于扩展和维护的系统结构。

适配器

一个现有的类需要适应变化的问题

  • 对象适配器
  • 类适配器

#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

说明

  1. 静态变量sharedInstance 是一个静态变量,用于存储单例实例。
static MySingleton *sharedInstance = nil;
  1. dispatch_oncedispatch_once 确保代码块只执行一次,从而保证单例实例只创建一次。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    sharedInstance = [[super allocWithZone:NULL] init];
});
  1. 重写 allocWithZone::重写 allocWithZone: 方法确保即使通过 alloc 方法创建对象,也返回同一个单例实例。
+ (id)allocWithZone:(struct _NSZone *)zone {
    return [self sharedInstance];
}
  1. 重写 copyWithZone::实现 NSCopying 协议并重写 copyWithZone: 方法,确保即使通过复制方法创建对象,也返回同一个单例实例。
- (id)copyWithZone:(NSZone *)zone {
    return self;
}
  1. 初始化方法:重写 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;
}

在这个示例中,singleton1singleton2singleton3singleton4 都指向同一个 MySingleton 实例。因此会输出以下结果:

Both instances are the same
AllocWithZone instance is the same
CopyWithZone instance is the same

通过这种方式,重写 allocWithZone:copyWithZone: 方法确保了即使通过 alloccopy 方法创建实例,也始终返回同一个单例实例,从而保证了单例模式的唯一性。

命令

行为参数化,降低代码重合度。

命令模式是一种行为设计模式,它将请求封装成对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式的主要优点是解耦了命令的发出者和执行者,并且可以方便地添加新的命令。

命令模式结构

  1. 命令(Command):定义命令接口,声明执行的方法。
  2. 具体命令(ConcreteCommand):实现命令接口,定义接收者(Receiver)并实现执行方法。
  3. 调用者(Invoker):持有命令对象并在某个时间点调用命令对象的执行方法。
  4. 接收者(Receiver):执行命令请求的对象。
  5. 客户端(Client):创建具体的命令对象并设置其接收者。

在你的代码中,以下部分对应命令模式的各个组成部分:

  1. 命令(Command)Command类及其子类。
  2. 具体命令(ConcreteCommand):实现具体业务逻辑的命令子类(当前示例中未展示,但可以扩展Command类)。
  3. 调用者(Invoker)CommandManger类。
  4. 接收者(Receiver):实际执行命令的对象或逻辑(当前在Command类的execute方法中)。
  5. 客户端(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数组中移除命令。

总结

命令模式在你的代码中很好地实现了对命令的管理和执行,解耦了命令的发出者和执行者。通过这种模式,你可以方便地扩展新的命令类型,并灵活地管理命令的执行和取消。