iOS设计模式之代理模式

2,749 阅读4分钟

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

本文主要介绍iOS设计模式中的代理模式,代理模式顾名思义是通过控制对另一些对象的访问,相当中间添加了一个中间类来访问对象。

1. 什么是代理模式

比如我们一般登陆一些会员制的网站,通常一些功能只有会员才能访问,普通用户只能访问一些基础的内容,付费内容则需要充值会员才能访问。那么和代理模式有啥关系,代理的一个常见用处是作为一个轻量的替身对象,它运去客户端首先访问一些廉价的信息或功能,知道充值会员才提供付费的资源。因此代理在一开始向用户提供试用会员资格,当用户愿意为真正的高价的会员资格付费的时候,代理会敞开大门让用户访问付费会员的功能。
信用卡是银行账户的代理, 银行账户则是一大捆现金的代理。 它们都实现了同样的接口, 均可用于进行支付。 消费者会非常满意, 因为不必随身携带大量现金; 商店老板同样会十分高兴, 因为交易收入能以电子化的方式进入商店的银行账户中, 无需担心存款时出现现金丢失或被抢劫的情况。
通常带路模式有以下一些代理

  • 远程代理:为位于不同地址空间或网络上的对象提供本地代表。
  • 虚拟代理:更具需要创建重型对象。
  • 保护代理: 更具各种访问权限控制原对象的访问
  • 智能引用代理: 通过对真正对象的引用进行计数来管理内存。也用于锁定真正对象,让其他对象不能对其进行修改。

代理模式:为其他对象提供一种代理以控制对这个对象的访问。

2. 什么时候使用代理模式

下面的情形可以考虑使用代理模式

  • 需要一个远程代理,为位于不同地址空间或网络中的对象提供本地代表
  • 需要一个虚拟代理,来根据要求创建重型的对象。
  • 需要一个保护代理,来根据不同访问权限控制对原对象的访问。
  • 需要一个智能引用代理,通过对实体对象的引用进行计算来管理内存。

3. 代码展示

import XCTest

/// The Subject interface declares common operations for both RealSubject and
/// the Proxy. As long as the client works with RealSubject using this
/// interface, you'll be able to pass it a proxy instead of a real subject.
protocol Subject {

    func request()
}

/// The RealSubject contains some core business logic. Usually, RealSubjects are
/// capable of doing some useful work which may also be very slow or sensitive -
/// e.g. correcting input data. A Proxy can solve these issues without any
/// changes to the RealSubject's code.
class RealSubject: Subject {

    func request() {
        print("RealSubject: Handling request.")
    }
}

/// The Proxy has an interface identical to the RealSubject.
class Proxy: Subject {

    private var realSubject: RealSubject

    /// The Proxy maintains a reference to an object of the RealSubject class.
    /// It can be either lazy-loaded or passed to the Proxy by the client.
    init(_ realSubject: RealSubject) {
        self.realSubject = realSubject
    }

    /// The most common applications of the Proxy pattern are lazy loading,
    /// caching, controlling the access, logging, etc. A Proxy can perform one
    /// of these things and then, depending on the result, pass the execution to
    /// the same method in a linked RealSubject object.
    func request() {

        if (checkAccess()) {
            realSubject.request()
            logAccess()
        }
    }

    private func checkAccess() -> Bool {

        /// Some real checks should go here.

        print("Proxy: Checking access prior to firing a real request.")

        return true
    }

    private func logAccess() {
        print("Proxy: Logging the time of request.")
    }
}

/// The client code is supposed to work with all objects (both subjects and
/// proxies) via the Subject interface in order to support both real subjects
/// and proxies. In real life, however, clients mostly work with their real
/// subjects directly. In this case, to implement the pattern more easily, you
/// can extend your proxy from the real subject's class.
class Client {
    // ...
    static func clientCode(subject: Subject) {
        // ...
        print(subject.request())
        // ...
    }
    // ...
}

/// Let's see how it all works together.
class ProxyConceptual: XCTestCase {

    func test() {
        print("Client: Executing the client code with a real subject:")
        let realSubject = RealSubject()
        Client.clientCode(subject: realSubject)

        print("\nClient: Executing the same client code with a proxy:")
        let proxy = Proxy(realSubject)
        Client.clientCode(subject: proxy)
    }
}

4.小结

在iOS中我们有一个NSProxy类,NSProxy类实现了NSObject协议,所以NSProxy对象实际上也是NSObject类型。NSProxy类是一个抽象基类。代理模式会将所有实际工作委派给一些其他对象。 除非代理是某个服务的子类, 否则每个代理方法最后都应该引用一个服务对象