当前位置: 首页 >  网技达人 >  设计模式-用代理模式(Proxy Pattern)来拯救你的代码:打造可靠的程序设计

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

导读:前言.设计模式是一种高级编程技巧,也是一种通用的解决方案。它能在不同的应用场景中使用,它可以提高代码的可读性、可复用性和可维护性。设计模式的学习能提高我们的编程能力以及代码质量,同时也能提高我们的开发效率,减少代码的维护成本。.掌握设计模式对于开发软件而言是非常重要的,熟练地应用

前言

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

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

设计模式有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
公众号:凡人程序猿
本文版权归作者所有,欢迎转载,但必须保留此段声明,并给出原文链接,谢谢合作!

内容
  • Git是如何工作的
    Git是如何工作的
    2023-12-12
    Git是如何工作的.首先,必须确定代码的存储位置。 常见的假设是只有两个位置 , 一个位于 Github 等远程服务器上
  • 浅析华为云Astro的5大关键能力技术
    浅析华为云Astro的5大关键能
    2023-12-11
    摘要: 本文以技术方案视角,对华为云Astro低代码平台的一些核心功能进行简要介绍。.背景介绍.低代码开发基于可视化开发
  • RocketMQ消费者是如何负载均衡的
    RocketMQ消费者是如何负载
    2023-12-09
    摘要:RocketMQ 支持两种消息模式:集群消费( Clustering )和广播消费( Broadcasting )
  • gitlab ci 集成 eslint/prettier/tsc 做代码审查,并使用 eslint 输出作为显示代码质量
    gitlab ci 集成 esl
    2023-12-09
    前言.想自动化一下公司里代码的部分审查,最初想用 reviewdog 的,但是公司的域名基本都在 VPN 中访问的,gi
  • 基于YOLOv5的目标检测系统详解(附MATLAB GUI版代码)
    基于YOLOv5的目标检测系统详
    2023-12-08
    摘要:本文重点介绍了基于YOLOv5目标检测系统的MATLAB实现,用于智能检测物体种类并记录和保存结果,对各种物体检测
  • Git 小技巧:忽略某些文件的更改
    Git 小技巧:忽略某些文件的更
    2023-12-07
    *以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq
  • 架构师日记-如何写的一手好代码
    架构师日记-如何写的一手好代码
    2023-12-06
    作者:京东零售 刘慧卿.一 前言.在日常工作中,我经常听到部分同学抱怨代码质量问题,潜台词是:“除了自己的代码,其他人写
  • Qt-FFmpeg开发-视频播放【软解码 + OpenGL显示RGB图像】(3)
    Qt-FFmpeg开发-视频播放
    2023-12-06
    Qt-FFmpeg开发-视频播放【软解码 + OpenGL显示RGB图像】.目录.Qt-FFmpeg开发-视频播放【软解
  • java代码审计-XSS
    java代码审计-XSS
    2023-12-04
    0x01 前言.XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行
  • SonarQube系列-通过配置扫描分析范围,聚焦关键问题
    SonarQube系列-通过配置
    2023-12-04
    在许多情况下,你可能不希望分析项目中每个源文件的各个方面。例如,项目可能包含生成的代码、库中的源代码或有意复制的代码。在
  • FlashDuty Changelog 2023-09-07 | 新增深色模式与主题配置
    FlashDuty Change
    2023-12-04
    FlashDuty:一站式告警响应平台,前往此地址免费体验!.FlashDuty.现在已经全面支持了深色模式,这为您提供
  • 1.4 编写简易ShellCode弹窗
    1.4 编写简易ShellCod
    2023-12-02
    在前面的章节中相信读者已经学会了使用Metasploit工具生成自己的ShellCode代码片段了,本章将继续深入探索关
  • SonarQube系列-架构与外部集成
    SonarQube系列-架构与外
    2023-12-02
    介绍.Sonar是一个代码质量管理的开源平台,基于Java开发的,用于管理源代码的质量,通过插件形式,可以支持包括jav
  • 生态景观设计
    生态景观设计
    2023-12-06
    生态景观设计产品介绍.产品功能.我们的生态景观设计产品旨在通过创造自然、宜人的环境,提升人们生活的品质。我们的设计团队将
  • 绿化工程材料供应
    绿化工程材料供应
    2023-12-16
    绿化工程材料供应.产品功能.我们的绿化工程材料供应主要用于城市绿化、园林景观建设、庭院绿化等相关项目。产品种类丰富,覆盖
  • 水系景观设计
    水系景观设计
    2023-12-16
    水系景观设计.产品功能.水系景观设计是一种专门用于规划和设计水景的产品。它可以帮助用户在各种场地中创建出迷人的水景,提升
  • 景观照明工程
    景观照明工程
    2023-12-16
    景观照明工程.产品功能.景观照明工程是一种专门为户外景观设计的照明方案。它既可以美化城市风景,提升城市形象,也可以为人们
  • 景观灯具**
    景观灯具**
    2024-01-10
    景观灯具**.产品功能.防水设计,可适应各种恶劣天气.高亮度LED灯源,节能环保.多种灯具款式可选,满足不同景观需求.灯
  • 公园景观规划设计
    公园景观规划设计
    2024-01-10
    公园景观规划设计.产品功能.我们的公园景观规划设计产品旨在为城市和乡村地区提供高质量的公共休闲空间。我们致力于通过规划和
  • 风景名胜区规划设计
    风景名胜区规划设计
    2023-12-31
    风景名胜区规划设计.我们很高兴地向您介绍我们的产品——风景名胜区规划设计。作为专业的规划设计团队,我们致力于为风景名胜区
  • 遗址公园规划设计
    遗址公园规划设计
    2023-12-26
    遗址公园规划设计.产品功能.遗址公园规划设计是一款专为文化遗址地区而设计的规划软件,旨在帮助规划者更好地设计和规划遗址公
  • 喷泉设计与安装
    喷泉设计与安装
    2023-12-21
    喷泉设计与安装.喷泉是园林景观中不可或缺的元素之一,无论是在公园、**还是私人花园中,喷泉都能为环境增添灵动的气息,成为
  • 园林绿化工程
    园林绿化工程
    2024-01-05
    园林绿化工程产品介绍.产品功能.园林绿化工程是一项专业化的服务,旨在打造美丽的绿色环境。我们的园林绿化工程团队致力于设计