前言

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

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

设计模式有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. 入库大文件csv文件

    LOAD DATA LOCAL INFILE 'D:\\ss\\chongzhi\\T_RORD.csv' INTO TABLE cz_T_RECHARGE_SET_RECORDFIELDS TERM ...

  2. react 获取文件流导出功能

    记录一下: 根据后台接口返回的文件流,前端实现导出下载,使用(react+ts) 1.请求方法 (这里写法绕开拦截器) // 导出日志 export async function exportLog( ...

  3. 微信小程序开发遇到的注意事项及奇怪事

    1.wx.uploadFile上传文件时只支持本地文件(相册或者拍摄的),网络文件不可以,可以将网络文件用wx.downloadFile下载到本地在下载,下载以后会返回一个微信临时地址然后再下载 2. ...

  4. iterm2免密自动登陆服务器

    之前的配置方式出现了less命令查看文本格式紊乱,以及输入的命令也是紊乱的,导致没办法正常使用 以前的配置方式如下: 在iterm2里配置command,如下图 2. online文件如下: #!/u ...

  5. 对volatile修饰的变量使用memset函数

    背景 今天面试了一家公司,面试官问了我一个开放性的问题.大致意思是,为什么对volatile修饰的变量调用memset函数,编译的时候会报错.当然,我是不知道为什么啦.之前没有遇到过嘛.不过我还是做了 ...

  6. 20181224《网络攻防技术》Exp 8 Web综合

    20181224<网络攻防技术>Exp 8 Web综合 目录 20181224<网络攻防技术>Exp 8 Web综合 相关知识点总结 web前端 web后端 数据库编程 实验内 ...

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

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

  8. SpringBoot+MybatisPlus 多数据源问题

    SpringBoot+Mybatis 多数据源报错 使用了2个数据源 @Bean("dataSource") @ConfigurationProperties(prefix = & ...

  9. Docker+jenkins 运行 python 自动化

    一.实现思路 在 Linux 服务器安装 docker 创建 jenkins 容器 根据自动化项目依赖包构建 python 镜像(构建自动化 python 环境) 运行新的 python 容器,执行 ...

  10. C++ || 用类 写交换函数 ||函数指针传递

    点击查看代码 #include <iostream> using namespace std; void swap(int* a,int* b) //函数参数为指针形式 { int p = ...