iOS设计模式

在iOS开发中,设计模式是解决常见业务场景、优化代码结构、提升可维护性与可扩展性的核心手段。无论是Objective-C(OC)还是Swift开发,设计模式的应用都贯穿始终------从系统框架(如UIKit)的底层实现,到业务逻辑的分层封装,合理运用设计模式能让代码更简洁、健壮,同时降低团队协作的成本。本文将聚焦iOS开发中最常用、最核心的12种设计模式,结合OC与Swift双语言示例,拆解其原理、应用场景与实操细节,帮助开发者快速掌握并灵活运用。

一、设计模式核心认知

设计模式并非固定的代码模板,而是一套经过长期实践总结的"代码设计经验",其核心目标是解决以下问题:

  • 解耦:降低类与类、模块与模块之间的依赖,让代码更易修改与扩展;
  • 复用:减少重复代码,提升开发效率,避免"复制粘贴"带来的维护隐患;
  • 清晰:让代码结构更具可读性,便于团队理解与协作;
  • 健壮:应对业务变化时,最小化代码修改范围,降低引入bug的风险。

iOS开发中,设计模式主要分为三大类:创建型模式(负责对象的创建)、结构型模式(负责对象的组合与结构优化)、行为型模式(负责对象的交互与行为管理)。下文将重点讲解12种核心模式,结合iOS实际开发场景展开。

二、创建型模式:高效管理对象创建(4种)

创建型模式的核心是"如何创建对象",通过封装对象创建的细节,避免直接使用new/alloc初始化带来的耦合,让对象创建更灵活、可控制。iOS开发中常用的4种创建型模式为:单例模式、工厂模式、建造者模式、原型模式。

2.1 单例模式(Singleton Pattern)

单例模式是iOS开发中最基础、最常用的模式,其核心是:确保一个类在整个应用生命周期中,只存在一个实例对象,且提供全局唯一的访问入口。常用于管理全局资源(如网络请求、本地存储、配置管理等),避免重复创建对象造成的资源浪费。

核心准则与注意事项

实现单例需遵循三个核心准则,确保其唯一性与安全性:

  • 唯一性:整个应用生命周期中,该类只能有一个实例,禁止外部通过初始化方法创建新实例;
  • 线程安全:多线程环境下,需保证实例创建过程不会出现"多次初始化"的问题;
  • 全局访问:提供统一的类方法(如sharedInstance),让整个应用可便捷访问该实例。

实操示例(OC+Swift)

OC实现(经典线程安全版,基于dispatch_once):

objectivec 复制代码
#import <Foundation/Foundation.h>

@interface NetworkManager : NSObject
// 全局访问入口
+ (instancetype)sharedInstance;
// 示例方法:发起网络请求
- (void)sendRequestWithURL:(NSString *)url;
@end

@implementation NetworkManager

// 静态变量存储唯一实例
static NetworkManager *sharedInstance = nil;
// 确保线程安全,dispatch_once保证代码只执行一次
static dispatch_once_t onceToken;

+ (instancetype)sharedInstance {
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

// 重写init方法,禁止外部通过alloc init创建实例
- (instancetype)init {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super init];
    });
    return sharedInstance;
}

// 禁止copy操作(避免通过copy创建新实例)
- (id)copyWithZone:(NSZone *)zone {
    return sharedInstance;
}

- (void)sendRequestWithURL:(NSString *)url {
    NSLog(@"发起网络请求:%@", url);
}

@end

Swift实现(简洁线程安全版,利用全局变量懒加载特性):

swift 复制代码
import Foundation

class NetworkManager {
    // 全局访问入口,static let 保证懒加载且线程安全(自动满足dispatch_once特性)
    static let shared = NetworkManager()
    
    // 私有初始化方法,禁止外部创建实例
    private init() {}
    
    // 示例方法:发起网络请求
    func sendRequest(with url: String) {
        print("发起网络请求:(url)")
    }
}

// 使用方式
let manager = NetworkManager.shared
manager.sendRequest(with: "https://www.example.com")

应用场景

系统框架中的单例示例:UIApplication.shared(应用全局唯一实例)、NSUserDefaults.standard(本地存储全局实例)、NSNotificationCenter.default(通知中心全局实例);

业务开发中的单例示例:网络请求管理类、本地缓存管理类、全局配置类、日志管理类等。

2.2 工厂模式(Factory Pattern)

工厂模式的核心是:封装对象的创建逻辑,通过"工厂类"统一创建不同类型的对象,外部无需关心对象的具体创建细节,只需通过工厂获取所需对象。常用于对象创建逻辑复杂、需统一管理,或需动态切换对象类型的场景。

实操示例(Swift,模拟视图创建工厂)

场景:APP中需要创建不同类型的按钮(普通按钮、圆角按钮、渐变按钮),通过工厂类统一管理创建逻辑:

swift 复制代码
import UIKit

// 按钮类型枚举
enum ButtonType {
    case normal      // 普通按钮
    case rounded     // 圆角按钮
    case gradient    // 渐变按钮
}

// 按钮工厂类
class ButtonFactory {
    // 静态方法,根据类型创建对应按钮
    static func createButton(type: ButtonType, title: String) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle(title, for: .normal)
        button.setTitleColor(.white, for: .normal)
        
        switch type {
        case .normal:
            // 普通按钮:黑色背景
            button.backgroundColor = .black
        case .rounded:
            // 圆角按钮:蓝色背景+圆角
            button.backgroundColor = .blue
            button.layer.cornerRadius = 8
            button.clipsToBounds = true
        case .gradient:
            // 渐变按钮:红橙渐变背景
            let gradientLayer = CAGradientLayer()
            gradientLayer.colors = [UIColor.red.cgColor, UIColor.orange.cgColor]
            gradientLayer.frame = button.bounds
            button.layer.insertSublayer(gradientLayer, at: 0)
        }
        
        return button
    }
}

// 使用方式
let normalBtn = ButtonFactory.createButton(type: .normal, title: "普通按钮")
let roundedBtn = ButtonFactory.createButton(type: .rounded, title: "圆角按钮")
let gradientBtn = ButtonFactory.createButton(type: .gradient, title: "渐变按钮")

应用场景

视图创建(统一管理不同样式的视图)、网络请求适配器(根据不同接口类型创建不同的请求对象)、数据解析(根据不同数据格式创建不同的解析器)等。

2.3 建造者模式(Builder Pattern)

建造者模式的核心是:将复杂对象的构建过程与表示分离,通过"建造者"类逐步构建对象,允许同一构建过程创建不同的表示。与工厂模式不同,建造者模式更注重"分步构建",适合创建属性多、构建逻辑复杂的对象(如模型、复杂视图)。

实操示例(Swift,模拟用户模型建造)

场景:用户模型包含多个可选属性(用户名、年龄、头像、手机号等),通过建造者模式分步构建,避免初始化方法参数过多的问题:

swift 复制代码
import Foundation

// 目标对象:用户模型
class User {
    var username: String?
    var age: Int?
    var avatarURL: String?
    var phone: String?
    
    // 私有初始化,只能通过建造者创建
    private init() {}
    
    // 建造者类(嵌套在User中,便于访问私有属性)
    class Builder {
        private let user = User()
        
        // 分步设置属性,返回自身实现链式调用
        func setUsername(_ username: String) -> Builder {
            user.username = username
            return self
        }
        
        func setAge(_ age: Int) -> Builder {
            user.age = age
            return self
        }
        
        func setAvatarURL(_ url: String) -> Builder {
            user.avatarURL = url
            return self
        }
        
        func setPhone(_ phone: String) -> Builder {
            user.phone = phone
            return self
        }
        
        // 构建最终对象
        func build() -> User {
            return user
        }
    }
}

// 使用方式(链式调用,灵活设置属性)
let user = User.Builder()
    .setUsername("iOSDeveloper")
    .setAge(28)
    .setAvatarURL("https://example.com/avatar.jpg")
    .setPhone("13800138000")
    .build()

print("用户名:(user.username ?? ""),年龄:(user.age ?? 0)")

应用场景

复杂模型构建(如用户模型、商品模型,属性多且部分可选)、复杂视图构建(如自定义弹窗、表格单元格,需分步设置样式、内容)、网络请求参数构建(分步设置请求头、参数、请求方式)等。

2.4 原型模式(Prototype Pattern)

原型模式的核心是:通过复制(克隆)一个已存在的对象(原型),来创建新的对象,无需重新初始化,提升对象创建效率,尤其适合创建复杂对象或频繁创建相同/相似对象的场景。iOS中通过NSCopying协议实现原型模式。

实操示例(OC,模拟商品模型克隆)

objectivec 复制代码
#import <Foundation/Foundation.h>

// 商品模型(遵循NSCopying协议,实现克隆)
@interface GoodsModel : NSObject <NSCopying>
@property (nonatomic, copy) NSString *goodsId;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGFloat price;
@property (nonatomic, copy) NSString *desc;

// 初始化方法
- (instancetype)initWithGoodsId:(NSString *)goodsId name:(NSString *)name price:(CGFloat)price desc:(NSString *)desc;
@end

@implementation GoodsModel

- (instancetype)initWithGoodsId:(NSString *)goodsId name:(NSString *)name price:(CGFloat)price desc:(NSString *)desc {
    self = [super init];
    if (self) {
        self.goodsId = goodsId;
        self.name = name;
        self.price = price;
        self.desc = desc;
    }
    return self;
}

// 实现NSCopying协议方法,完成克隆(浅拷贝)
- (id)copyWithZone:(NSZone *)zone {
    GoodsModel *copyModel = [[[self class] allocWithZone:zone] init];
    copyModel.goodsId = self.goodsId;
    copyModel.name = self.name;
    copyModel.price = self.price;
    copyModel.desc = self.desc;
    return copyModel;
}

@end

// 使用方式
// 1. 创建原型对象
GoodsModel *prototypeGoods = [[GoodsModel alloc] initWithGoodsId:@"1001" name:@"iPhone 15" price:5999.0 desc:@"苹果手机"];

// 2. 克隆原型对象(无需重新初始化,直接复制属性)
GoodsModel *copyGoods1 = [prototypeGoods copy];
copyGoods1.price = 5799.0; // 修改克隆对象属性,不影响原型

GoodsModel *copyGoods2 = [prototypeGoods copy];
copyGoods2.name = @"iPhone 15 Pro"; // 另一个克隆对象,独立于原型和其他克隆体

NSLog(@"原型:%@,克隆1:%@,克隆2:%@", prototypeGoods.name, copyGoods1.name, copyGoods2.name);

应用场景

频繁创建相似对象(如列表中的商品模型、聊天消息模型)、复杂对象的快速复制(如已配置好的视图、模型)、避免重复初始化带来的性能损耗等。

三、结构型模式:优化对象组合与结构(4种)

结构型模式的核心是"如何组合对象,形成更灵活、更高效的结构",通过合理的对象组合,降低代码耦合,提升代码的可扩展性。iOS开发中常用的4种结构型模式为:代理模式、适配器模式、外观模式、装饰器模式。

3.1 代理模式(Delegate Pattern)

代理模式是iOS开发中最核心的行为型+结构型模式,其核心是:定义一个协议(Protocol),让一个对象(代理方)代替另一个对象(委托方)执行某些操作,或传递某些事件,实现对象间的通信与解耦。常用于"反向传值""事件回调"场景,是UIKit框架中最常用的模式之一。

核心角色

  • 委托方(Delegatee):发起事件、需要被代理的对象(如UITableView);
  • 代理方(Delegate):遵循协议,实现协议中的方法,处理委托方发起的事件(如UIViewController);
  • 协议(Protocol):定义代理需要实现的方法,规范委托方与代理方的通信接口。

实操示例(OC,模拟登录页面反向传值)

less 复制代码
#import <UIKit/UIKit.h>

// 1. 定义代理协议
@protocol LoginViewControllerDelegate <NSObject>
// 代理方法:登录成功,传递用户名
- (void)loginSuccessWithUsername:(NSString *)username;
// 代理方法:登录取消
- (void)loginCanceled;
@end

// 2. 委托方:登录页面
@interface LoginViewController : UIViewController
// 代理属性(weak修饰,避免循环引用)
@property (nonatomic, weak) id<LoginViewControllerDelegate> delegate;
@end

@implementation LoginViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = .white;
    
    // 登录按钮
    UIButton *loginBtn = [UIButton buttonWithType:UIButtonTypeSystem];
    loginBtn.frame = CGRectMake(50, 200, 300, 50);
    [loginBtn setTitle:@"登录" forState:UIControlStateNormal];
    [loginBtn addTarget:self action:@selector(loginBtnClicked) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:loginBtn];
    
    // 取消按钮
    UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeSystem];
    cancelBtn.frame = CGRectMake(50, 300, 300, 50);
    [cancelBtn setTitle:@"取消" forState:UIControlStateNormal];
    [cancelBtn addTarget:self action:@selector(cancelBtnClicked) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:cancelBtn];
}

// 登录按钮点击事件:通知代理登录成功
- (void)loginBtnClicked {
    NSString *username = @"iOSDeveloper";
    // 先判断代理是否实现了该方法,避免崩溃
    if ([self.delegate respondsToSelector:@selector(loginSuccessWithUsername:)]) {
        [self.delegate loginSuccessWithUsername:username];
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}

// 取消按钮点击事件:通知代理登录取消
- (void)cancelBtnClicked {
    if ([self.delegate respondsToSelector:@selector(loginCanceled)]) {
        [self.delegate loginCanceled];
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}

@end

// 3. 代理方:主页面
@interface MainViewController : UIViewController <LoginViewControllerDelegate>
@end

@implementation MainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = .white;
    
    // 跳转到登录页面
    UIButton *toLoginBtn = [UIButton buttonWithType:UIButtonTypeSystem];
    toLoginBtn.frame = CGRectMake(50, 100, 300, 50);
    [toLoginBtn setTitle:@"去登录" forState:UIControlStateNormal];
    [toLoginBtn addTarget:self action:@selector(toLoginBtnClicked) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:toLoginBtn];
}

- (void)toLoginBtnClicked {
    LoginViewController *loginVC = [[LoginViewController alloc] init];
    // 设置代理为当前页面
    loginVC.delegate = self;
    [self presentViewController:loginVC animated:YES completion:nil];
}

// 实现代理方法:登录成功
- (void)loginSuccessWithUsername:(NSString *)username {
    NSLog(@"登录成功,用户:%@", username);
    // 后续逻辑:更新UI、保存用户信息等
}

// 实现代理方法:登录取消
- (void)loginCanceled {
    NSLog(@"登录取消");
}

@end

应用场景

系统框架中的代理示例:UITableViewDelegate(表格事件代理)、UITextFieldDelegate(输入框事件代理)、UIScrollViewDelegate(滚动视图事件代理);

业务开发中的代理示例:页面反向传值、弹窗按钮点击回调、自定义视图事件回调等。

3.2 适配器模式(Adapter Pattern)

适配器模式属于结构型模式,其核心作用是将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以协同工作。由于OC不支持多继承,iOS开发中通常实现对象适配器,通过组合而非继承的方式实现接口转换。

核心场景

当现有类的接口与业务需求不匹配,且无法直接修改现有类(如系统类、第三方库类)时,可通过适配器模式进行接口转换,避免修改原有代码,符合"开闭原则"。最形象的示例就是手机充电:手机数据线(目标接口)与电源插座(被适配接口)不兼容,通过转换头(适配器)实现兼容供电。

实操示例(OC,模拟充电适配器)

less 复制代码
#import <Foundation/Foundation.h>

// 1. 目标接口:手机数据线协议(客户期望的接口)
@protocol DataLineProtocol <NSObject>
- (void)connect; // 数据线连接方法
@end

// 2. 被适配者:电源插座(现有接口,与目标接口不兼容)
@interface EleSocket : NSObject
- (void)specialConnect; // 插座的特殊连接方法(与connect接口不匹配)
@end

@implementation EleSocket
- (void)specialConnect {
    NSLog(@"specialConnect--电源接通,开始供电");
}
@end

// 3. 适配器:转换电源插座接口,使其符合数据线协议
@interface DataAdapter : NSObject <DataLineProtocol>
// 持有被适配者实例(对象适配器核心:组合而非继承)
@property (nonatomic, strong) EleSocket *eleSocket;

// 初始化方法:传入被适配者
- (instancetype)initWithEleSocket:(EleSocket *)socket;
@end

@implementation DataAdapter
- (instancetype)initWithEleSocket:(EleSocket *)socket {
    self = [super init];
    if (self) {
        self.eleSocket = socket;
    }
    return self;
}

// 实现目标接口方法,内部调用被适配者的方法
- (void)connect {
    [self.eleSocket specialConnect];
}
@end

// 4. 客户端:手机(使用目标接口)
@interface Mobile : NSObject
@property (nonatomic, strong) id<DataLineProtocol> dataLine; // 依赖目标接口

- (instancetype)initWithDataLine:(id<DataLineProtocol>)line;
- (void)charge; // 充电方法
@end

@implementation Mobile
- (instancetype)initWithDataLine:(id<DataLineProtocol>)line {
    self = [super init];
    if (self) {
        self.dataLine = line;
    }
    return self;
}

- (void)charge {
    [self.dataLine connect]; // 调用目标接口,无需关心具体实现
    NSLog(@"手机开始充电");
}
@end

// 使用方式
EleSocket *socket = [[EleSocket alloc] init];
DataAdapter *adapter = [[DataAdapter alloc] initWithEleSocket:socket];
Mobile *mobile = [[Mobile alloc] initWithDataLine:adapter];
[mobile charge];

应用场景

第三方库接口适配(如将第三方网络库接口适配成项目统一的网络请求接口)、系统类接口扩展(如将系统UI组件的接口适配成自定义组件的接口)、旧代码兼容(将旧项目的接口适配成新项目的接口规范)。

3.3 外观模式(Facade Pattern)

外观模式提供了一个统一的接口,用来访问子系统中的一群接口,定义了一个高层接口,让子系统更容易使用。其核心是"简化接口",将复杂子系统的多个接口封装成一个统一的入口,客户端只需调用这个入口方法,无需关心子系统内部的复杂逻辑。

外观模式与适配器模式的区别:适配器模式是"转换接口",解决接口不兼容问题;外观模式是"简化接口",解决子系统调用复杂的问题,不改变原有接口。

实操示例(OC,模拟餐馆点餐场景)

场景:餐馆点餐涉及服务员接单、厨师做菜、厨师上菜三个子系统,通过外观类(餐馆)封装这些子系统,客户端只需调用"点餐"方法,无需分别与服务员、厨师交互。

objectivec 复制代码
#import <Foundation/Foundation.h>

// 子系统1:服务员
@interface Waiter : NSObject
- (void)handleOrder; // 处理订单
@end

@implementation Waiter
- (void)handleOrder {
    NSLog(@"服务员接收订单,确认菜品");
}
@end

// 子系统2:厨师
@interface Cook : NSObject
- (void)cook;      // 做菜
- (void)complete;  // 上菜
@end

@implementation Cook
- (void)cook {
    NSLog(@"厨师开始做菜");
}
- (void)complete {
    NSLog(@"厨师做好菜,上菜");
}
@end

// 外观类:餐馆(统一入口)
@interface Hotel : NSObject
// 持有子系统实例
@property (nonatomic, strong) Waiter *waiter;
@property (nonatomic, strong) Cook *cook;

// 初始化方法:初始化子系统
- (instancetype)initWithWaiter:(Waiter *)waiter cook:(Cook *)cook;
// 统一接口:点餐
- (void)order;
@end

@implementation Hotel
- (instancetype)initWithWaiter:(Waiter *)waiter cook:(Cook *)cook {
    self = [super init];
    if (self) {
        self.waiter = waiter;
        self.cook = cook;
    }
    return self;
}

// 封装子系统接口,提供统一入口
- (void)order {
    [self.waiter handleOrder];
    [self.cook cook];
    [self.cook complete];
}
@end

// 使用方式(客户端只需与外观类交互)
Waiter *waiter = [[Waiter alloc] init];
Cook *cook = [[Cook alloc] init];
Hotel *hotel = [[Hotel alloc] initWithWaiter:waiter cook:cook];
[hotel order]; // 一句话完成点餐流程

应用场景

复杂业务流程封装(如登录流程:封装接口请求、数据存储、UI跳转等子步骤,提供统一的login方法)、第三方SDK封装(将第三方SDK的多个接口封装成项目所需的统一接口)、模块间交互(通过外观类统一暴露模块接口,降低模块间的耦合)。

3.4 装饰器模式(Decorator Pattern)

装饰器模式的核心是:动态地给一个对象添加额外的职责,无需修改原对象的代码,也无需继承,比继承更灵活。常用于给现有对象扩展功能,且扩展功能可灵活组合的场景(如视图样式扩展、功能增强)。

实操示例(Swift,模拟按钮样式装饰)

场景:给普通按钮动态添加圆角、阴影、渐变等样式,无需修改按钮本身的代码,可灵活组合装饰效果:

swift 复制代码
import UIKit

// 抽象组件:按钮基类(被装饰的对象)
protocol ButtonDecoratable {
    func setupStyle() -> UIButton
}

// 具体组件:普通按钮(基础对象)
class NormalButton: ButtonDecoratable {
    func setupStyle() -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle("基础按钮", for: .normal)
        button.backgroundColor = .gray
        button.setTitleColor(.white, for: .normal)
        return button
    }
}

// 装饰器抽象类(遵循组件协议,持有组件实例)
class ButtonDecorator: ButtonDecoratable {
    private let button: ButtonDecoratable
    
    init(button: ButtonDecoratable) {
        self.button = button
    }
    
    func setupStyle() -> UIButton {
        return button.setupStyle()
    }
}

// 具体装饰器1:圆角装饰
class RoundedDecorator: ButtonDecorator {
    override func setupStyle() -> UIButton {
        let button = super.setupStyle()
        button.layer.cornerRadius = 10
        button.clipsToBounds = true
        return button
    }
}

// 具体装饰器2:阴影装饰
class ShadowDecorator: ButtonDecorator {
    override func setupStyle() -> UIButton {
        let button = super.setupStyle()
        button.layer.shadowColor = UIColor.black.cgColor
        button.layer.shadowOffset = CGSize(width: 2, height: 2)
        button.layer.shadowOpacity = 0.5
        button.layer.shadowRadius = 4
        return button
    }
}

// 具体装饰器3:渐变装饰
class GradientDecorator: ButtonDecorator {
    override func setupStyle() -> UIButton {
        let button = super.setupStyle()
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [UIColor.blue.cgColor, UIColor.purple.cgColor]
        gradientLayer.frame = button.bounds
        button.layer.insertSublayer(gradientLayer, at: 0)
        return button
    }
}

// 使用方式(灵活组合装饰器)
// 1. 普通按钮(无装饰)
let normalBtn = NormalButton().setupStyle()

// 2. 圆角+阴影按钮(组合两个装饰器)
let roundedShadowBtn = ShadowDecorator(button: RoundedDecorator(button: NormalButton())).setupStyle()

// 3. 圆角+渐变+阴影按钮(组合三个装饰器)
let fullStyleBtn = ShadowDecorator(button: GradientDecorator(button: RoundedDecorator(button: NormalButton()))).setupStyle()

应用场景

视图样式扩展(如按钮、卡片的圆角、阴影、渐变组合)、功能增强(如给网络请求添加日志、缓存、重试功能)、动态扩展对象职责(如给文本框添加输入校验、格式转换功能)等。

四、行为型模式:规范对象交互与行为(4种)

行为型模式的核心是"如何规范对象之间的交互,分配对象的职责",让对象之间的通信更灵活、更可控。iOS开发中常用的4种行为型模式为:观察者模式、命令模式、迭代器模式、策略模式。

4.1 观察者模式(Observer Pattern)

观察者模式的核心是:定义对象间的一种一对多的依赖关系,当一个对象(被观察者)的状态发生变化时,所有依赖它的对象(观察者)都会收到通知,并自动更新。iOS中最典型的实现就是通知中心(NSNotificationCenter),此外KVO(键值观察)也是观察者模式的一种应用。

核心角色

  • 被观察者(Subject):维护一组观察者,提供添加、移除观察者及通知观察者的方法;
  • 观察者(Observer):定义接收通知的方法,当被观察者状态变化时,执行相应的更新操作。

实操示例(Swift,基于通知中心实现)

swift 复制代码
import Foundation

// 1. 定义通知名称(全局常量,避免字符串硬编码)
let UserLoginNotification = NSNotification.Name("UserLoginNotification")

// 2. 被观察者:登录管理类(发送通知)
class LoginManager {
    static let shared = LoginManager()
    private init() {}
    
    // 登录方法:登录成功后发送通知
    func login(username: String, password: String) {
        // 模拟登录请求
        print("登录请求:用户名(username),密码(password)")
        print("登录成功")
        
        // 发送通知,携带用户信息(可选)
        NotificationCenter.default.post(
            name: UserLoginNotification,
            object: self,
            userInfo: ["username": username]
        )
    }
}

// 3. 观察者1:首页(接收登录通知,更新UI)
class HomeViewController {
    init() {
        // 注册通知(添加观察者)
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleLoginNotification(_:)),
            name: UserLoginNotification,
            object: nil
        )
    }
    
    // 接收通知的方法
    @objc func handleLoginNotification(_ notification: Notification) {
        guard let username = notification.userInfo?["username"] as? String else { return }
        print("首页收到登录通知:用户(username)已登录,更新首页UI")
    }
    
    // 销毁时移除观察者(避免内存泄漏)
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

// 4. 观察者2:个人中心(接收登录通知,加载用户信息)
class ProfileViewController {
    init() {
        // 注册通知
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleLoginNotification(_:)),
            name: UserLoginNotification,
            object: nil
        )
    }
    
    @objc func handleLoginNotification(_ notification: Notification) {
        guard let username = notification.userInfo?["username"] as? String else { return }
        print("个人中心收到登录通知:加载用户(username)的个人信息")
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

// 使用方式
let homeVC = HomeViewController()
let profileVC = ProfileViewController()
LoginManager.shared.login(username: "iOSDeveloper", password: "123456")

应用场景

系统框架中的示例:NSNotificationCenter(全局通知)、KVO(监听对象属性变化);

业务开发中的示例:登录状态变化通知、网络请求结果回调、数据更新通知(如列表数据刷新)等。

4.2 命令模式(Command Pattern)

命令模式的核心是:将一个请求封装成一个对象(命令对象),让请求的发起者(客户端)与请求的执行者(接收者)解耦,客户端无需知道执行者的具体实现,只需调用命令对象的执行方法即可。常用于需要记录请求、撤销请求、批量执行请求的场景。

实操示例(Swift,模拟按钮命令)

swift 复制代码
import UIKit

// 1. 命令协议:定义命令的执行方法
protocol Command {
    func execute() // 执行命令
    func undo()    // 撤销命令(可选)
}

// 2. 接收者:具体执行命令的对象(如灯光)
class Light {
    var isOn: Bool = false
    
    func turnOn() {
        isOn = true
        print("灯光打开")
    }
    
    func turnOff() {
        isOn = false
        print("灯光关闭")
    }
}

// 3. 具体命令1:打开灯光命令
class TurnOnLightCommand: Command {
    private let light: Light
    
    // 初始化时传入接收者
    init(light: Light) {
        self.light = light
    }
    
    // 执行命令:调用接收者的方法
    func execute() {
        light.turnOn()
    }
    
    // 撤销命令:恢复到执行前的状态
    func undo() {
        light.turnOff()
    }
}

// 4. 具体命令2:关闭灯光命令
class TurnOffLightCommand: Command {
    private let light: Light
    
    init(light: Light) {
        self.light = light
    }
    
    func execute() {
        light.turnOff()
    }
    
    func undo() {
        light.turnOn()
    }
}

// 5. 发起者:按钮(调用命令对象,无需知道接收者)
class Button {
    private var command: Command?
    
    // 设置命令
    func setCommand(_ command: Command) {
        self.command = command
    }
    
    // 点击按钮:执行命令
    func click() {
        command?.execute()
    }
    
    // 撤销按钮:执行撤销命令
    func undoClick() {
        command?.undo()
    }
}

// 使用方式
let light = Light()
let turnOnCommand = TurnOnLightCommand(light: light)
let turnOffCommand = TurnOffLightCommand(light: light)

let button = Button()
// 设置"打开灯光"命令,点击按钮执行
button.setCommand(turnOnCommand)
button.click() // 输出:灯光打开

// 撤销操作
button.undoClick() // 输出:灯光关闭

// 切换"关闭灯光"命令,点击按钮执行
button.setCommand(turnOffCommand)
button.click() // 输出:灯光关闭

应用场景

按钮点击事件封装、批量操作(如批量删除、批量保存)、撤销/重做功能(如文本编辑、绘图应用)、任务队列(如将多个命令加入队列,依次执行)等。

4.3 迭代器模式(Iterator Pattern)

迭代器模式的核心是:提供一种方法顺序访问一个聚合对象(如数组、集合)中的各个元素,而无需暴露该对象的内部表示。iOS中系统已内置迭代器(如for-in循环、NSEnumerator),开发中可直接使用,也可自定义迭代器适配特殊聚合对象。

实操示例(Swift,自定义迭代器)

场景:自定义一个商品集合,实现迭代器,支持顺序访问商品元素,无需暴露集合内部存储结构:

swift 复制代码
import Foundation

// 商品模型
class Goods {
    var name: String
    var price: CGFloat
    
    init(name: String, price: CGFloat) {
        self.name = name
        self.price = price
    }
}

// 迭代器协议(定义迭代方法)
protocol GoodsIteratorProtocol {
    func hasNext() -> Bool // 是否有下一个元素
    func next() -> Goods?  // 获取下一个元素
}

// 具体迭代器:商品集合迭代器
class GoodsIterator: GoodsIteratorProtocol {
    private var goodsList: [Goods]
    private var currentIndex = 0
    
    init(goodsList: [Goods]) {
        self.goodsList = goodsList
    }
    
    func hasNext() -> Bool {
        return currentIndex < goodsList.count
    }
    
    func next() -> Goods? {
        guard hasNext() else { return nil }
        let goods = goodsList[currentIndex]
        currentIndex += 1
        return goods
    }
}

// 聚合对象:商品集合(持有迭代器)
class GoodsCollection {
    private var goodsList: [Goods] = []
    
    // 添加商品
    func addGoods(_ goods: Goods) {
        goodsList.append(goods)
    }
    
    // 获取迭代器
    func createIterator() -> GoodsIteratorProtocol {
        return GoodsIterator(goodsList: goodsList)
    }
}

// 使用方式
let collection = GoodsCollection()
collection.addGoods(Goods(name: "iPhone 15", price: 5999.0))
collection.addGoods(Goods(name: "iPad Pro", price: 7999.0))
collection.addGoods(Goods(name: "AirPods Pro", price: 1999.0))

// 获取迭代器,顺序访问元素
let iterator = collection.createIterator()
while iterator.hasNext() {
    if let goods = iterator.next() {
        print("商品:(goods.name),价格:(goods.price)")
    }
}

应用场景

自定义集合类(如自定义列表、栈、队列)的元素遍历、复杂数据结构(如树形结构)的顺序访问、需统一遍历接口(如同时遍历数组、字典、自定义集合)等。

4.4 策略模式(Strategy Pattern)

策略模式的核心是:定义一系列算法,将每个算法封装成独立的策略类,使它们可以相互替换,客户端可根据场景动态选择不同的算法,无需修改客户端代码。常用于算法多变、需灵活切换的场景(如支付方式、排序算法、校验规则)。

实操示例(Swift,模拟支付策略)

场景:APP支持多种支付方式(微信支付、支付宝支付、银联支付),通过策略模式封装不同支付算法,可动态切换支付方式:

swift 复制代码
import Foundation

// 策略协议:定义支付算法接口
protocol PaymentStrategy {
    func pay(amount: CGFloat) -> Bool // 支付方法,返回支付结果
}

// 具体策略1:微信支付
class WechatPayment: PaymentStrategy {
    func pay(amount: CGFloat) -> Bool {
        print("微信支付:(amount)元")
        // 模拟支付逻辑
        return true
    }
}

// 具体策略2:支付宝支付
class AlipayPayment: PaymentStrategy {
    func pay(amount: CGFloat) -> Bool {
        print("支付宝支付:(amount)元")
        // 模拟支付逻辑
        return true
    }
}

// 具体策略3:银联支付
class UnionPayPayment: PaymentStrategy {
    func pay(amount: CGFloat) -> Bool {
        print("银联支付:(amount)元")
        // 模拟支付逻辑
        return true
    }
}

// 上下文:支付管理类(持有策略,提供切换策略和执行支付的方法)
class PaymentManager {
    private var strategy: PaymentStrategy?
    
    // 切换支付策略
    func setPaymentStrategy(_ strategy: PaymentStrategy) {
        self.strategy = strategy
    }
    
    // 执行支付
    func executePayment(amount: CGFloat) -> Bool {
        guard let strategy = strategy else {
            print("未选择支付方式")
            return false
        }
        return strategy.pay(amount: amount)
    }
}

// 使用方式(动态切换支付策略)
let paymentManager = PaymentManager()

// 选择微信支付
paymentManager.setPaymentStrategy(WechatPayment())
paymentManager.executePayment(amount: 99.0)

// 切换到支付宝支付
paymentManager.setPaymentStrategy(AlipayPayment())
paymentManager.executePayment(amount: 199.0)

// 切换到银联支付
paymentManager.setPaymentStrategy(UnionPayPayment())
paymentManager.executePayment(amount: 299.0)

应用场景

支付方式切换、数据排序算法切换(如升序、降序、自定义排序)、表单校验规则切换(如手机号校验、邮箱校验、身份证校验)、网络请求方式切换(如GET、POST、PUT)等。

五、iOS设计模式应用原则与避坑指南

5.1 核心应用原则

  • 按需使用:设计模式不是"越多越好",根据业务场景选择合适的模式,避免过度设计(如简单页面无需使用代理模式,直接使用block回调即可);
  • 优先解耦:所有设计模式的核心都是"解耦",避免类与类之间的强依赖,让代码更易维护;
  • 遵循开闭原则:对扩展开放,对修改关闭,通过设计模式让代码在新增功能时,无需修改原有代码;
  • 贴合系统框架:iOS系统框架本身大量使用设计模式(如代理、单例、观察者),遵循系统的设计风格,能让代码更贴合iOS开发规范。

5.2 常见避坑点

  • 单例模式:避免滥用单例(如非全局资源无需使用单例),注意线程安全与内存泄漏(OC中用weak修饰相关引用,Swift中避免循环引用);
  • 代理模式:避免循环引用(代理属性用weak修饰),调用代理方法前需判断代理是否实现该方法,避免崩溃;
  • 观察者模式:通知使用后需及时移除观察者,避免内存泄漏;KVO监听时需正确处理属性变化,避免过度监听;
  • 避免过度设计:如简单的接口转换无需使用适配器模式,直接封装一个工具方法即可;简单的对象创建无需使用工厂模式,直接初始化即可;
  • 装饰器/策略模式:避免过度拆分,若扩展功能简单,无需封装成独立的装饰器/策略类,避免增加代码复杂度。

六、总结

设计模式是iOS开发的"内功",掌握核心设计模式,能让开发者在面对复杂业务场景时,快速给出合理的代码设计方案,提升开发效率与代码质量。本文讲解的12种核心模式(4种创建型、4种结构型、4种行为型),覆盖了iOS开发中绝大多数场景,其应用贯穿于UI开发、业务逻辑、系统交互等各个层面。

实际开发中,无需死记硬背设计模式的模板,而是要理解其核心思想与应用场景,结合业务需求灵活运用------有时一个业务场景可能需要结合多种设计模式(如单例+代理、工厂+适配器),关键是达到"解耦、复用、清晰、健壮"的目标。随着开发经验的积累,对设计模式的理解会更加深刻,代码设计也会更加优雅。

相关推荐
xmdy58662 小时前
Flutter+开源鸿蒙实战|校园易生活Day2 第三方库批量集成+全局Toast提示+网络状态监听+首页轮播图+资讯卡片布局
flutter·开源·harmonyos
恋猫de小郭2 小时前
Flutter 3.44 发布前夕,官方宣布 SwiftPM 将完全取代 CocoaPods
android·前端·flutter
张风捷特烈2 小时前
状态管理大乱斗#06 | Riverpod 源码评析 (下) - 外功心法
android·前端·flutter
神奇的程序员11 小时前
开发了一个管理本地开发环境的软件
前端·flutter
xmdy586612 小时前
Flutter+开源鸿蒙实战|智联邻里Day9 系统权限适配+应用全局分享+缓存深度优化+版本更新弹窗
flutter·开源·harmonyos
maaath16 小时前
【maaath】Flutter for OpenHarmony 乐器学习应用开发实战
flutter·华为·harmonyos
maaath1 天前
【maaath】 Flutter for OpenHarmony 实战:电池优化应用开发指南
flutter·华为·harmonyos
勤劳打代码1 天前
Flutter 架构日记 —— 可演进的 Flutter Dialog 组件
flutter·架构
Eric_HYD1 天前
Flutter 字体字生效原理解析
flutter