设计模式-创建型

设计模式-创建型

创建型设计模式包含:单例模式、原型模式、工厂方法模式、抽象工厂模式、建造者模式

单例模式

单例模式在开发中也是最常见的一种设计模式之一,系统原生提供的很多类的设计都采用了单例模式,例如:

FileManager.default
UserDefaults.standard
NotificationCenter.default
UIApplication.shared
URLSession.shared

其目的是为了节省内存资源并保证数据内容的一致性,需要让某些类只能创建一个实例。单例模式有如下特点:

  • 单例类只有一个实例对象
  • 单例类的实例对象由自己创建
  • 需要对外提供一个访问其实例对象的接口

在软件设计中,有关全局共享的资源数据,大型通用的管理类等都可以使用单例模式,例如登录用户的用户信息类、全局的计时器、程序的日志管理类

Swift 单例

class ClassA {
    static let share = ClassA()
}

Objective-C 单例

static XXClassManager *_defaultManager;
+ (instancetype)shareInstance {
    if (!_defaultManager) {
        _defaultManager = [[self alloc] init];
    }
    return _defaultManager;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    if (!_defaultManager) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _defaultManager = [super allocWithZone:zone];
        });
    }
    return _defaultManager;
}
- (id)copyWithZone:(NSZone *)zone{
    return _defaultManager;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
    return _defaultManager;
}

原型模式

以一个已经创建的实例作为原型,通过复制该原型对象来创建出对象,在使用对象时,使用者无需关心对象创建的细节。在iOS开发中:copy 方法就是对原型设计模式的一种实现。主要是提供了一种大量创建复杂对象的方法。

重构前

import Foundation
class Computer {
    var cpu: String
    var host: String
    var screen: String
    var uuid: String
    init(cpu: String, host: String, screen: String) {
        self.cpu = cpu
        self.host = host
        self.screen = screen
        self.uuid = UUID().uuidString
    }
    func logUUID() {
        print(uuid)
    }
}
let computer1 = Computer(cpu: "Intel core i7 7700K", host: "GY088-GDF-60", screen: "3008 x 1692")
computer1.logUUID()

此时若希望创建一台相同配置的电脑则只能使用重复的创建方法

let computer2 = Computer(cpu: "Intel core i7 7700K", host: "GY088-GDF-60", screen: "3008 x 1692")

重构后

import Foundation
class Computer {
    ...
    
    func copy() -> Computer {
        return Computer(cpu: self.cpu, host: self.host, screen: self.screen)
    }
}

新增一个 copy() 方法,此时再创建相同配置的电脑只需调用原型的 copy() 方法即可,省去了配件的创建过程。
使用原型模式,一旦第一个对象被创建,后面的对象创建都将变得非常容易。其中,作为模板对象被称为原型,创建出来的对象拥有和模板对象一致的属性和方法。

工厂方法模式

工厂方法设计模式注重于将对象的创建过程封闭起来,通过定义抽象的工厂接口和商品接口来隐藏负责对象创建的具体类

对上述 Computer 类进行重构

重构后

enum Level {
    case low
    case high
}
protocol ComputerFactoryProtol {
    static func getComputer(level: Level) -> ComputerProtol
}
protocol ComputerProtol {
    var cpu: String { get }
    var host: String { get }
    var screen: String { get }
    var uuid: String { get }
    func logUUID()
}
class Computer: ComputerProtol {
    var cpu: String
    var host: String
    var screen: String
    var uuid: String
    init(cpu: String, host: String, screen: String) {
        self.cpu = cpu
        self.host = host
        self.screen = screen
        self.uuid = UUID().uuidString
    }
    func logUUID() {
        print(uuid)
    }
}
class ComputerFactory: ComputerFactoryProtol {
    static func getComputer(level: Level) -> ComputerProtol {
        switch level {
        case .low:
            return Computer(cpu: "Intel core i5 3300K", host: "GY088-GDF-10", screen: "1920 x 1080")
        case .high:
            return Computer(cpu: "Intel core i7 7700K", host: "GY088-GDF-60", screen: "3008 x 1692")
        }
    }
}

引入的 ComputerFactory 即工厂设计模式的具体体现,外界不再指明具体的配置信息,只需要根据 level 即可创建指定配置的 Computer,
创建高配电脑,例如:

let computer3 = ComputerFactory.getComputer(level: .high)

如果新增加了一种创建方式完全不同的计算机,我们只需要新建一个遵守 ComputerProtol 的计算机类,之后在 ComputerFactory 中统一处理这种新增的计算机类型即可,对使用者完全隐藏。

抽象工厂模式

抽象工厂是对工厂模式的一种升级,核心思路是为各种类型的对象提供一组统一的创建接口,使用者无需关心这些对象具体是如何创建的。

还是上述代码,若我们工厂方法即生产 Computer 又可以生产 TV

重构后

enum Level {
    case low
    case high
}
protocol ComputerFactoryProtol {
    static func getComputer(level: Level) -> ComputerProtol
    static func getTV() -> TVProtol
}
protocol TVProtol {
    var name: String { get }
    func logName()
}
class TV: TVProtol {
    var name: String
    init(name: String) {
        self.name = name
    }
    func logName() {
        print(self.name)
    }
}
protocol ComputerProtol {
    ...
}
class Computer: ComputerProtol {
    ...    
}
class ComputerFactory: ComputerFactoryProtol {
    static func getTV() -> TVProtol {
        return TV(name: "海尔")
    }
    static func getComputer(level: Level) -> ComputerProtol {
        ...
    }
}

重构后工厂类可以创建不同的对象,对于使用者无需关心创建的细节,抽象工厂将对象的创建和使用进行了完全分离。

建造者模式

建造者模式用于复杂对象的创建,使代码聚合性更强,逻辑更加清晰。建造者模式通常与工程模式配合使用,工厂着重于对象的创建,建造者着重于创建复杂对象过程中组成对象的每一部分创建和最终组装。
核心在于将复杂的对象拆解成多个简单对象,通过一步步构建简单对象最终组合成复杂对象。

重构后

enum Foodtype {
    case a
    case b
}
enum Drink {
    case cola
    case juice
}
enum Staple {
    case hamburger
    case chickenRoll
}
class FoodPackage {
    var drink: Drink?
    var staple: Staple?
}
class BuildA {
    var foodPackage = FoodPackage()
    func build() -> FoodPackage {
        foodPackage.drink = .cola
        foodPackage.staple = .hamburger
        return foodPackage
    }
}
class BuildB {
    var foodPackage = FoodPackage()
    func build() -> FoodPackage {
        foodPackage.drink = .juice
        foodPackage.staple = .chickenRoll
        return foodPackage
    }
}
class FoodFactory {
    static func buildFood(type: Foodtype) -> FoodPackage {
        switch type {
        case .a:
            return BuildA().build()
        case .b:
            return BuildB().build()
        }
    }
}
let foodPackage = FoodFactory.buildFood(type: .a)

其中,一个完整的套餐对象由饮料对象、主食对象组成,FoodFactory 为工厂方法,其中根据套餐类型创建不同的套餐对象,具体的套餐对象的组成则是由 BuildABuildB 来完成。 BuildABuildB 是建造者模式的核心类,充当建造者的角色。

总结

  • 单例模式:全局共享数据的最佳实践
  • 原型模式:快速复制对象的便捷途径
  • 工厂方法模式:将对象的创建与使用进行隔离
  • 抽象工厂模式:提供一组接口创建不同类别的产品的实现方法
  • 建造者模式:拆分复杂对象为多个简单对象进行创建