设计模式之创建型-iOS

225 阅读5分钟

设计模式是一种特定的解决问题的方法,是基于软件设计原则的具体实现。设计模式是从多种场景中提取出来的、经过实践验证的解决方案,通常提供了具体的代码实现和设计架构。常见的设计模式有单例模式、工厂模式、观察者模式、策略模式等等,这些模式为开发人员提供了特定场景下的具体解决方案,帮助开发人员实现更为优秀的应用程序。 最为知名的是GOF模式,GOF是Gang of Four的缩写,由四个著名的软件工程师在1994年出版书中总结。

经典的GOF设计模式有23个,分为创建型、结构型、行为三大类模式,这里主要介绍创建型模式。 结构型模式分为以下5种设计模式,这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。

一、 工厂方法(Factory Method)

核心思想:定义创建对象的接口,但由子类决定实例化哪个类

将对象的创建延迟到子类中,父类只定义创建对象的接口。

下面以一个集成多个人脸识别SDK功能为例,需求是客户端集成多个厂商渠道SDK后要实现后台控制分流比例。 工厂方法模式做法:

  • 定义产品协议:创建的目标(即具体产品类)需要遵守的协议FaceSession,协议中的具体方法是比较统一的人脸识别需要的步骤,比如字节人脸、商汤人脸、旷视人脸。
  • 定义各个厂商产品类: 需遵守并根据自己厂商的逻辑实现FaceSession协议的方法,底层会依赖各个厂商SDK。
  • 定义工厂协议:各个工厂类需要遵守的协议FaceFactory, 里面有创建方法返回FaceSession类型.
  • 定义具体工厂类:遵守FaceFactory,并在创建方法中封装具体的产品类的具体细节。

使用方:

=》拉取后台的分流数据,此时可以确定目前是哪个具体工厂类`AAAFactoy`.
=》由工厂类`AAAFactoy`创建对应的`FaceSession`(外部只需要知道是FaceSession类型不需要知道具体类).
=》根据它的需要拉取此次人脸需要的数据作为参数.
=》调用`FaceSession`中人脸方法.
=》真实`FaceSession`类型调用对应厂商SDK的方法发起人脸
=》接收识别结果及用户操作回调等处理

收益:

  • 开闭原则:主体功能逻辑在新增或者更换人脸识别厂商时不需要改动,只需要变更对应的工厂类和产品类。
  • 职责单一:各司其职,使用方不需要关心具体的工厂和产品,只需要知道有协议中方法满足需求。
  • 可插拔:移除厂商SDK时不需要改动其他代码,移除对应类即可;新增厂商SDK时只需要新增对应的工厂和产品类,对已有流程无修改。

由简单工厂模式演变而来,由于简单工厂模式并未被认定为GOF经典设计模式, 所以没有列在这里。

二、抽象工厂(Abstract Factory)

核心思想:创建一系列相关或相互依赖的对象族,无需指定它们的具体类。相比工厂方法可创建多个产品组成的产品族

抽象工厂模式通过引入一个抽象工厂接口,该接口声明了一组用于创建不同类型产品的方法。具体的产品创建由实现了抽象工厂接口的具体工厂类负责。通过使用抽象工厂模式,客户端可以创建一系列相关的产品,而无需关心每个具体产品的创建细节。

主要角色:
  • 抽象产品接口:定义了产品的公共方法。
  • 具体产品类:实现了抽象产品接口。
  • 抽象工厂接口:定义了创建一系列相关产品的方法。
  • 具体工厂类:实现了抽象工厂接口,负责创建一系列相关产品。

相比工厂方法,抽象工厂方法模式的工厂能创建一系列相关的产品。

三、生成器模式(Builder Pattern)

将一个复杂对象的构造过程分解成多个简单的步骤,可以灵活地组合这些步骤来创建不同的对象变体。

核心思想:“分步构建复杂对象,让构建过程和表示分离”

实例
import Foundation

// 产品:网络请求配置
struct NetworkRequest {
    let url: URL
    let method: HTTPMethod
    let headers: [String: String]
    let body: Data?
    let timeout: TimeInterval
    let cachePolicy: URLRequest.CachePolicy
    
    enum HTTPMethod: String {
        case get = "GET"
        case post = "POST"
        case put = "PUT"
        case delete = "DELETE"
    }
}

// Builder
class NetworkRequestBuilder {
    private var url: URL?
    private var method: NetworkRequest.HTTPMethod = .get
    private var headers: [String: String] = [:]
    private var body: Data?
    private var timeout: TimeInterval = 30.0
    private var cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy
    
    func setURL(_ urlString: String) -> Self {
        self.url = URL(string: urlString)
        return self
    }
    
    func setMethod(_ method: NetworkRequest.HTTPMethod) -> Self {
        self.method = method
        return self
    }
    
    func addHeader(key: String, value: String) -> Self {
        headers[key] = value
        return self
    }
    
    func setJSONBody<T: Encodable>(_ body: T) -> Self {
        self.body = try? JSONEncoder().encode(body)
        addHeader(key: "Content-Type", value: "application/json")
        return self
    }
    
    func setTimeout(_ timeout: TimeInterval) -> Self {
        self.timeout = timeout
        return self
    }
    
    func setCachePolicy(_ policy: URLRequest.CachePolicy) -> Self {
        self.cachePolicy = policy
        return self
    }
    
    func build() -> NetworkRequest? {
        guard let url = url else {
            print("URL不能为空")
            return nil
        }
        
        return NetworkRequest(
            url: url,
            method: method,
            headers: headers,
            body: body,
            timeout: timeout,
            cachePolicy: cachePolicy
        )
    }
    
    func buildURLRequest() -> URLRequest? {
        guard let networkRequest = build() else { return nil }
        
        var request = URLRequest(url: networkRequest.url)
        request.httpMethod = networkRequest.method.rawValue
        request.allHTTPHeaderFields = networkRequest.headers
        request.httpBody = networkRequest.body
        request.timeoutInterval = networkRequest.timeout
        request.cachePolicy = networkRequest.cachePolicy
        
        return request
    }
}

// 使用示例
class APIClient {
    func login(username: String, password: String) {
        let loginBody = ["username": username, "password": password]
        
        guard let request = NetworkRequestBuilder()
            .setURL("https://api.example.com/login")
            .setMethod(.post)
            .setJSONBody(loginBody)
            .addHeader(key: "Accept", value: "application/json")
            .setTimeout(15)
            .buildURLRequest()
        else { return }
        
        URLSession.shared.dataTask(with: request) { data, response, error in
            // 处理响应
        }.resume()
    }
}

四、原型模式(Prototype Pattern)

不需要知道对象的具体类型,只需要实现复制自身的能力。当直接创建对象成本较高时,通过复制现有对象来快速获得新实例。

核心思想:“以原型实例为基础,通过复制(克隆)来创建新对象”

类比iOS中的NSCopying协议,定义这样一个协议,协议中有copy对象的实例方法。当一个类需要有复制功能时,可以遵守该协议并写自己的实现。

五、单例模式

全局只存在一个实例。

  • 提供一个静态的构建方法,如shared;
  • 私有化实例初始化方法。