iOS设计模式之观察者模式

176 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

  • 本文主要介绍iOS设计模式中的观察者模式,观察者模式也叫发布-订阅模式。在我们iOS中KVO也是观察者模式

1. 什么是观察者模式

我们之前说航空交通需要集中的空中交通管制,有很多的操作员坐在操控塔里盯着自己的雷达屏幕,以确保不会发生空中相撞。飞行员把他们的无线电调到特定的频道,收听(观察)周围的交通状况。如果交通管制员向收听这个频道的飞行员广播了某些预警或警告,飞行员可以用某些行动表示已收到了消息。因此在这一模式中,任何人都知道他们观察哪个交通管制,反过来却不然。
订阅杂志也是这样,当从杂志发行商订阅杂志时候,读者把收货信息提供给发行商,这样有新的杂志就可以第一时间收到,读者不会收到没有订阅的杂志。或者我们在微博上关注某个用户,当她发布动态的时候,我们会第一时间收到通知
我们把这一思想引入面向对象软件设计中来,用于消除具有不同行为对象之间的耦合(或者用其他不同的行为来拓展现有的行为)。通过这一个模式,不同对象可以协同工作,同时它们可以被复用于其他地方。我们称这种模式为观察者模式
观察者模式是一种发布-订阅模型Observersubject订阅通知,发生改变后第一时间通知观察者。我们在oc中使用的RAC以及Swift框架中的RXSwift都是大量使用了观察者模式。对于iOS中本身的KVO可以看下我之前的文章KVO原理以及 自定义KVO

观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

2. 什么时候使用观察者模式

以下情形下,可以考虑使用这一模式。

  • 有两种抽象类相互依赖。将它们封装在各自的对象中,就可以对它们单独进行改变和复用。
  • 对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
  • 一个对象必须通知其他对象,而它又不知道其他对象是什么

3. 代码展示

在iOS中,使用通知和kvo达到观察者模式。我们对对象的setter方法进行监听,添加观察者,当观察对象的属性发生改变触发了setter方法后,通知我们的观察者进行方法的调用,我们之后进行处理。这里只是简单的说明下,具体可以上面提到的文章。

import XCTest

class ObserverRealWorld: XCTestCase {

    func test() {

        let cartManager = CartManager()

        let navigationBar = UINavigationBar()
        let cartVC = CartViewController()

        cartManager.add(subscriber: navigationBar)
        cartManager.add(subscriber: cartVC)

        let apple = Food(id: 111, name: "Apple", price: 10, calories: 20)
        cartManager.add(product: apple)

        let tShirt = Clothes(id: 222, name: "T-shirt", price: 200, size: "L")
        cartManager.add(product: tShirt)

        cartManager.remove(product: apple)
    }
}

protocol CartSubscriber: CustomStringConvertible {

    func accept(changed cart: [Product])
}

protocol Product {

    var id: Int { get }
    var name: String { get }
    var price: Double { get }

    func isEqual(to product: Product) -> Bool
}

extension Product {

    func isEqual(to product: Product) -> Bool {
        return id == product.id
    }
}

struct Food: Product {

    var id: Int
    var name: String
    var price: Double

    /// Food-specific properties
    var calories: Int
}

struct Clothes: Product {

    var id: Int
    var name: String
    var price: Double

    /// Clothes-specific properties
    var size: String
}

class CartManager {

    private lazy var cart = [Product]()
    private lazy var subscribers = [CartSubscriber]()

    func add(subscriber: CartSubscriber) {
        print("CartManager: I'am adding a new subscriber: (subscriber.description)")
        subscribers.append(subscriber)
    }

    func add(product: Product) {
        print("\nCartManager: I'am adding a new product: (product.name)")
        cart.append(product)
        notifySubscribers()
    }

    func remove(subscriber filter: (CartSubscriber) -> (Bool)) {
        guard let index = subscribers.firstIndex(where: filter) else { return }
        subscribers.remove(at: index)
    }

    func remove(product: Product) {
        guard let index = cart.firstIndex(where: { $0.isEqual(to: product) }) else { return }
        print("\nCartManager: Product '(product.name)' is removed from a cart")
        cart.remove(at: index)
        notifySubscribers()
    }

    private func notifySubscribers() {
        subscribers.forEach({ $0.accept(changed: cart) })
    }
}

extension UINavigationBar: CartSubscriber {

    func accept(changed cart: [Product]) {
        print("UINavigationBar: Updating an appearance of navigation items")
    }

    open override var description: String { return "UINavigationBar" }
}

class CartViewController: UIViewController, CartSubscriber {

    func accept(changed cart: [Product]) {
        print("CartViewController: Updating an appearance of a list view with products")
    }

    open override var description: String { return "CartViewController" }
}

运行结果

CartManager: I'am adding a new subscriber: UINavigationBar
CartManager: I'am adding a new subscriber: CartViewController

CartManager: I'am adding a new product: Apple
UINavigationBar: Updating an appearance of navigation items
CartViewController: Updating an appearance of a list view with products

CartManager: I'am adding a new product: T-shirt
UINavigationBar: Updating an appearance of navigation items
CartViewController: Updating an appearance of a list view with products

CartManager: Product 'Apple' is removed from a cart
UINavigationBar: Updating an appearance of navigation items
CartViewController: Updating an appearance of a list view with products

4. 总结

观察者模式在iOS还是比较常用的一种设计模式,但是一般都是配合一些框架进行使用比如RXSwift,我们数据绑定在taleView上,视图跟随数据进行刷新变化。或者我们表单,监听我们的条件是否满足从而更新按钮的UI状态。本质上也是为了解决数据和我们UI的耦合性问题,从而达到UI复用的效果。