前言

设计模式是一种高级编程技巧,也是一种通用的解决方案。它能在不同的应用场景中使用,它可以提高代码的可读性、可复用性和可维护性。设计模式的学习能提高我们的编程能力以及代码质量,同时也能提高我们的开发效率,减少代码的维护成本。

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

设计模式有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)通用类图

  • 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)控制访问创建开销大的资源,其通过在代理对象和实际对象之间添加一层代理层,来实现对实际对象的延迟加载或缓存。

  • 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)可能会使类的数量增加,也可能会增加代码的复杂度,因为它涉及到多个对象之间的协作。代理模式可能会导致有性能损失,因为客户端需要通过代理对象来访问真实对象,从而增加了额外的开销。

结语

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

记得以前看过的一个新闻,有一句结尾的话印象挺深刻的,分享出来给大家:“时间过得真系快,又系时候讲拜拜”。话题转回来,我会定期发布一些技术文章,如果这篇文章对你有帮助,请你关注我。

关于作者

博文作者:GarveyCalvin

公众号:凡人程序猿

本文版权归作者所有,欢迎转载,但必须保留此段声明,并给出原文链接,谢谢合作!

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

  1. 乐在其中设计模式(C#) - 代理模式(Proxy Pattern)

    原文:乐在其中设计模式(C#) - 代理模式(Proxy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 代理模式(Proxy Pattern) 作者:webabcd 介绍 为 ...

  2. 二十四种设计模式:代理模式(Proxy Pattern)

    代理模式(Proxy Pattern) 介绍为其他对象提供一个代理以控制对这个对象的访问. 示例有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这个对 ...

  3. c#设计模式之代理模式(Proxy Pattern)

    引言 代理这个词语,大家在现实世界已经频繁的接触过,例如火车站代理售票点,因为这些代理售票点的存在,我们不必要去火车站的售票处就可以查询或者取到火车票.代理点本身是没有能力生产车票的,我们在代理处享受 ...

  4. 乐在其中设计模式(C#) - 代理模式(Proxy Pattern)【转】

    介绍 为其他对象提供一个代理以控制对这个对象的访问. 示例 有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这个对象的访问. MessageModel ...

  5. 设计模式系列之代理模式(Proxy Pattern)——对象的间接访问

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  6. 设计模式 - 代理模式(proxy pattern) 未使用代理模式 具体解释

    代理模式(proxy pattern) 未使用代理模式 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 部分代码參考: http://blog.csdn. ...

  7. 代理模式(Proxy pattern)

    代理模式(proxy pattern):作用:为其他对象提供一种代理,以控制对这个对象的访问.代理对象在客户端对象和目标对象之间起中介的作用. 代理模式涉及到的角色: 抽象角色:声明真实对象和代理对象 ...

  8. 设计模式——代理模式(Proxy Pattern)

    代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问. UML图: 模型设计: Subject类: package com.cnblog.clarck; /** * Subject 类 ...

  9. 13.代理模式(Proxy Pattern)

    using System; namespace Test { //抽象角色:声明真实对象和代理对象的共同接口. //代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象, //同时代理 ...

  10. 大熊君说说JS与设计模式之------代理模式Proxy

    一,总体概要 1,笔者浅谈 当我们浏览网页时,网页中的图片有时不会立即展示出来,这就是通过虚拟代理来替代了真实的图片,而代理存储了真实图片的路径和尺寸,这就是代理方式的一种. 代理模式是比较有用途的一 ...

随机推荐

  1. python学习笔记-简介

    python简介 python是一种简单易学,功能强大的编程语言,他有高效的高层数据结构,简单而有效的实现面向对象编程.python是一种解释性语言,在多数平台的多个领域都是理想的脚本语言,特别适用于 ...

  2. grafana配置邮箱告警

    [root@localhost grafana]# cd /etc/grafana/ [root@localhost grafana]# vim grafana.ini 注意:发件邮件要开启smtp服 ...

  3. Coder vs Programmer: Difference You Should Know

    In this tech-driven world, you may have heard the terms 'coder' and 'programmer' used interchangeabl ...

  4. Vuex的核心State

    State提供唯一的公共数据源,所有共享的数据都要统一放到 Store的 State 中进行存储. import Vue from 'vue' import Vuex from 'vuex' Vue. ...

  5. pytorch卷积模块

    nn.Conv2d() 常用的参数有in_channels,out_channels,kernel_size,stride,padding; 除此之外还有参数dilation,groups,bias ...

  6. 常用功能系列---【JWT生成Token实现接口登录认证方案思路】

    JWT生成Token实现接口登录认证方案思路 方案一(双token实现无感刷新) 在token中,refreshToken的作用主要是避免token过期时,前端用户突然退出登录,跳转至登录页面. 但是 ...

  7. js 将多层json对象 转化为一层json

    const parse = data => { const uid = `uid_${Date.now()}`; const process = (input, prefix = '', jso ...

  8. maya灯光导入houdini插件开发

    加入工作室时师兄给了两道测试题,由于第一道是完善师兄的一个houdini项目管理插件,我只是开发了一些小功能,所以不好意思拿出来. 第二道题就完全是由自己开发的一个小插件,功能是把maya里的灯光导入 ...

  9. Pintia 7-3 列车调度

    7-3 列车调度 (25 分) 火车站的列车调度铁轨的结构如下图所示. 两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有N条平行的轨道.每趟列车从入口可以选择任意一条轨 ...

  10. 解决git仓库项目 添加到github非空仓库冲突问题 error: failed to push some refs to 'https://github.com/Qtoken/......'

    error: failed to push some refs to 'https://github.com/Qtoken/......' 1. 问题描述:执行命令:git push origin m ...