前语

规划形式是一种高档编程技巧,也是一种通用的解决方案。它能在不同的运用场景中运用,它能够进步代码的可读性、可复用性和可维护性。规划形式的学习能进步咱们的编程能力以及代码质量,一起也能进步咱们的开发功率,削减代码的维护成本。

掌握规划形式关于开发软件而言是非常重要的,熟练地运用规划形式能让咱们愈加自信地去编写程序,另一方面,这也对咱们的面试至关重要,这但是妥妥的加分项呢。所以为了咱们的代码质量和未来发展而言,咱们都要对规划形式有必定的了解和运用。

规划形式有23种,在一篇文章中全部讲完肯定是不可能的,这篇文章会先介绍 署理形式(Proxy Pattern),本文的代码示例用的是 Swift 言语编写。

署理形式(Proxy Pattern)

署理形式(Proxy Pattern)是一种结构型规划形式,结构型形式描绘如何将类或方针按某种布局组成更大的结构。它答应你供给一个署理方针来操控对另一个方针的拜访。署理方针拥有与实践方针相同的接口,因而它能够被用来替代实践方针。

署理方针能够在调用实践方针之前或之后履行一些额定的操作,例如记录日志、缓存数据、操控拜访权限等。这种形式通常被用于长途署理、虚拟署理(Virtual Proxy)、保护署理和推迟加载等运用场景。

署理形式:为其他方针供给一种署理以操控对这个方针的拜访。 最 初 的 定 义 出 现 于 《设 计 模 式 》 ( A d d i s o n – W e s l e y , 1 9 9 4 ) 。

代码示例

署理形式(Proxy Pattern)通用类图

设计模式-用代理模式(Proxy Pattern)来拯救你的代码:打造可靠的程序设计

  • Subject 主题接口,界说了实在主题和署理主题的公共接口,客户端能够经过主题接口来拜访实在主题或者署理主题。
  • RealSubject 实在主题,完成了主题接口,界说了真实的业务逻辑。
  • Proxy 署理主题,也完成了主题接口,一起还持有了一个实在主题的引用,客户端经过署理主题来拜访实在主题,署理主题能够对拜访进行操控。

以下是通用代码:

protocol Subject {
    func request()
}
class RealSubject: Subject {
    func request() {
        print("RealSubject handling request")
    }
}
class Proxy: Subject {
    private let realSubject = RealSubject()
    func request() {
        print("Proxy handling request")
        realSubject.request()
    }
}

客户端调用代码:

let subject = Proxy()
subject.request()

伪代码(老默,我想吃鱼了)

忽然想到了一个很风趣的比如,比如说我现在想吃鱼,那吃鱼得先去菜市场把鱼买回来吧,一般来说都是自己去菜市场里买鱼。但是我现在只想吃鱼而不想亲自去买鱼,那我就和署理人说一声,说我想吃鱼了,署理人就会替代我去菜市场把鱼带回来给我,亦或者是署理人找的别人去买也是能够的。伪代码如下所示:

protocol 主题接口 {
    func 去买鱼()
}
class 老默: 主题接口 {
    func 去买鱼() {
        print("把鱼买回来了")
    }
}
class 署理人: 主题接口 {
    private let 我是老默 = 老默()
    func 去买鱼() {
        我是老默.去买鱼()
    }
}

客户端调用的伪代码如下所示:

let 我是一个署理 = 署理类()
我是一个署理.去买鱼()

每次想吃鱼咱们就找到署理人,由它来安排,至于谁去我也不在乎。

虚拟署理(Virtual Proxy)

咱们拿虚拟署理(Virtual Proxy)来作为比如解说说明,它是署理形式(Proxy Pattern)的一种,它在署理形式的运用中比较常见。

虚拟署理(Virtual Proxy)操控拜访创建开支大的资源,其经过在署理方针和实践方针之间添加一层署理层,来完成对实践方针的推迟加载或缓存。

设计模式-用代理模式(Proxy Pattern)来拯救你的代码:打造可靠的程序设计

  • ImageLoader 界说一个协议,规定了署理方针和实践方针需求完成的方法。
  • RealImageLoader 是遵从 ImageLoader 的实践方针,是实践干事的图片加载器。
  • ImageLoaderProxy 是遵从 ImageLoader 的署理方针,它持有 RealImageLoader 的引用,并且还完成了一个简略的图片缓存机制,以防止重复下载相同的图片。

咱们首先编写 ImageLoader 协议,界说相同的接口方法:

protocol ImageLoader {
    func loadImage(url: URL, completion: @escaping (UIImage?) -> Void)
}

接下来,编写图片加载器 RealImageLoader 类,并遵从 ImageLoader 协议:

class RealImageLoader: ImageLoader {
    func loadImage(url: URL, completion: @escaping (UIImage?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else {
                completion(nil)
                return
            }
            let image = UIImage(data: data)
            completion(image)
        }.resume()
    }
}

咱们运用 URLSession 来履行一个异步的网络恳求,将图片数据下载下来,并将其转换为 UIImage 方针并传递给回调闭包。

接下来,编写 ImageLoaderProxy 署理类:

class ImageLoaderProxy: ImageLoader {
    private let realImageLoader = RealImageLoader()
    private var cachedImages: [URL: UIImage] = [:]
    func loadImage(url: URL, completion: @escaping (UIImage?) -> Void) {
        if let cachedImage = cachedImages[url] {
            completion(cachedImage)
        } else {
            completion(UIImage(named: "image_loading_bg"))
            realImageLoader.loadImage(url: url) { [weak self] image in
                guard let self = self else { return }
                if let image = image {
                    self.cachedImages[url] = image
                }
                completion(image)
            }
        }
    }
}

当客户端调用 loadImage(url:completion:) 方法时,署理方针首先会判断是否现已缓存了对应的图片,如果现已缓存,则直接返回缓存的图片,不然先传递一个默许的加载图片用于显示过渡,再运用真实的图片加载器 RealImageLoader 加载图片,并在加载完成后缓存图片。

最终编写客户端的调用代码:

let imageLoader: ImageLoader = ImageLoaderProxy()
if let url = URL(string: "https://img2.baidu.com/it/u=2082637540,462915030&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=888") {
	imageLoader.loadImage(url: url) { [weak self] image in
		DispatchQueue.main.async {
			self?.imageView.image = image
		}
	}
}

这里需求注意的是,imageLoader在网络恳求的过程中不能被开释,不然闭包里的 self 会为空,你能够放在 controller 的属性傍边。

DispatchQueue.main.async 方法使线程回到主线程履行图片的加载。

作用呈现

建议在开发者工具傍边设置你的网络,将其设置为低速率的,这样方便咱们查看作用。

设计模式-用代理模式(Proxy Pattern)来拯救你的代码:打造可靠的程序设计

设计模式-用代理模式(Proxy Pattern)来拯救你的代码:打造可靠的程序设计

总结

署理形式(Proxy Pattern)答应你供给一个署理方针来操控对另一个方针的拜访,隐藏详细的完成细节,必定程序上降低了体系的耦合度。能够起到保护方针方针的伤。能够对方针方针的功能添加,如本文介绍虚拟署理运用的图片加载比如,在其中加入了图片缓存便是这个形式。

当然它也是有缺点的,运用署理形式(Proxy Pattern)可能会使类的数量添加,也可能会添加代码的复杂度,因为它涉及到多个方针之间的协作。署理形式可能会导致有性能损失,因为客户端需求经过署理方针来拜访实在方针,然后添加了额定的开支。

结语

本文章的代码现已整理到 GitHub 上,请点击链接获取。

记得曾经看过的一个新闻,有一句结尾的话印象挺深入的,分享出来给我们:“时间过得真系快,又系时候讲拜拜”。论题转回来,我会定时发布一些技术文章,如果这篇文章对你有帮助,请你重视我。

关于作者

博文作者:GarveyCalvin 大众号:凡人程序猿 本文版权归作者一切,欢迎转载,但必须保留此段声明,并给出原文链接,谢谢合作!