learn.lianglianglee.com/专栏/白话设计模式 28 讲(完)/07 代理模式:帮我拿一下快递.md.html
2022-05-11 19:04:14 +08:00

683 lines
22 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>07 代理模式:帮我拿一下快递.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/白话设计模式 28 讲(完)/00 生活中的设计模式:启程之前,请不要错过我.md.html">00 生活中的设计模式:启程之前,请不要错过我.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/01 监听模式:坑爹的热水器.md.html">01 监听模式:坑爹的热水器.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/02 适配模式:身高不够鞋来凑.md.html">02 适配模式:身高不够鞋来凑.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/03 状态模式:人与水的三态.md.html">03 状态模式:人与水的三态.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/04 单例模式:你是我生命的唯一.md.html">04 单例模式:你是我生命的唯一.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/05 职责模式:我的假条去哪了.md.html">05 职责模式:我的假条去哪了.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/06 中介模式:找房子问中介.md.html">06 中介模式:找房子问中介.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/白话设计模式 28 讲(完)/07 代理模式:帮我拿一下快递.md.html">07 代理模式:帮我拿一下快递.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/08 装饰模式:你想怎么穿就怎么穿.md.html">08 装饰模式:你想怎么穿就怎么穿.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/09 工厂模式:你要拿铁还是摩卡.md.html">09 工厂模式:你要拿铁还是摩卡.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/10 迭代模式:下一个就是你了.md.html">10 迭代模式:下一个就是你了.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/11 组合模式:自己组装电脑.md.html">11 组合模式:自己组装电脑.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/12 构建模式:想要车还是庄园.md.html">12 构建模式:想要车还是庄园.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/13 克隆模式:给你一个分身术.md.html">13 克隆模式:给你一个分身术.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/14 策略模式:怎么来不重要,人到就行.md.html">14 策略模式:怎么来不重要,人到就行.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/15 命令模式:大闸蟹,走起!.md.html">15 命令模式:大闸蟹,走起!.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/16 备忘模式:好记性不如烂笔头.md.html">16 备忘模式:好记性不如烂笔头.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/17 享元模式:颜料很贵必须充分利用.md.html">17 享元模式:颜料很贵必须充分利用.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/18 外观模式:学妹别慌,学长帮你.md.html">18 外观模式:学妹别慌,学长帮你.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/19 访问模式:一千个读者一千个哈姆雷特.md.html">19 访问模式:一千个读者一千个哈姆雷特.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/20 生活中的设计模式:与经典设计模式的不解渊源.md.html">20 生活中的设计模式:与经典设计模式的不解渊源.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/21 生活中的设计模式:那些未完待续的设计模式.md.html">21 生活中的设计模式:那些未完待续的设计模式.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/22 深入解读过滤器模式:制作一杯鲜纯细腻的豆浆.md.html">22 深入解读过滤器模式:制作一杯鲜纯细腻的豆浆.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/23 深入解读对象池技术:共享让生活更便捷.md.html">23 深入解读对象池技术:共享让生活更便捷.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/24 深入解读回调机制:把你技能亮出来.md.html">24 深入解读回调机制:把你技能亮出来.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/25 谈谈我对设计模式的理解.md.html">25 谈谈我对设计模式的理解.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/26 谈谈我对设计原则的思考.md.html">26 谈谈我对设计原则的思考.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/27 谈谈我对项目重构的看法.md.html">27 谈谈我对项目重构的看法.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>07 代理模式:帮我拿一下快递</h1>
<p>【故事剧情】</p>
<blockquote>
<p>八月中秋已过冬天急速飞来……一场秋雨一场寒十场秋雨穿上棉在下了两场秋雨之后Tony 已经冻的瑟瑟发抖了。周六Tony 在京东上买了一双雪地鞋准备过冬了,但是忘了选择京东自营的货源,第二天穿新鞋的梦想又不能如期实现了。</p>
<p>周二Tony 正在思考一个业务逻辑的实现方式这时一通电话来了“您好圆通快递。您的东西到了过来取一下快递”。Tony 愣了一下,转念明白:是周六买的鞋子,本来以来第二天就能到的,所以填的是家里的地址。这下可好,人都不在家了,咋办呢?</p>
<p>Tony 快速思索了一下,他想起了住一起的邻居 Wendy。Wendy 是一个小提琴老师,属于自由职业者,平时在艺术培训机构或到学生家里上上课,她在家的时间比较多。于是赶紧拿起手机呼叫 Wendy 帮忙:“你好,在家吗?能帮忙拿一下快速吗?”……</p>
<p>万幸的是 Wendy 正好在家,在她的帮助下终于顺利拿到快递,减了不少麻烦。</p>
</blockquote>
<p><img src="assets/33b267f0-d89b-11e7-9b5d-a5f4543ad36e.jpg" alt="enter image description here" /></p>
<h3>用程序来模拟生活</h3>
<p>在生活中,我们经常要找人帮一些忙:帮忙收快递,帮忙照看宠物狗。在程序中,有一种类似的设计,叫代理模式。在开始之前,我们先来模拟一下上面的故事案例。</p>
<p>源码示例:</p>
<pre><code class="language-python">class ReceiveParcel:
&quot;接收包裹&quot;
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
def receive(self, parcelContent):
pass
class TonyReception(ReceiveParcel):
&quot;Tony接收&quot;
def __init__(self, name, phoneNum):
super().__init__(name)
self.__phoneNum = phoneNum
def getPhoneNum(self):
return self.__phoneNum
def receive(self, parcelContent):
print(&quot;货物主人:&quot; + self.getName() + &quot; 手机号:&quot; + self.getPhoneNum())
print(&quot;接收到一个包裹,包裹内容:&quot; + parcelContent)
class WendyReception(ReceiveParcel):
&quot;Wendy接收&quot;
def __init__(self, name, receiver):
super().__init__(name)
self.__receiver = receiver
def receive(self, parcelContent):
print(&quot;我是&quot; + self.__receiver.getName() + &quot;的朋友, 我来帮他代收快递!&quot;)
if(self.__receiver is not None):
self.__receiver.receive(parcelContent)
print(&quot;代收人:&quot; + self.getName())
</code></pre>
<p>测试代码:</p>
<pre><code class="language-python">def testProxy():
tony = TonyReception(&quot;Tony&quot;, &quot;18512345678&quot;)
wendy = WendyReception(&quot;Wendy&quot;, tony)
wendy.receive(&quot;雪地靴&quot;)
</code></pre>
<p>输出结果:</p>
<pre><code>我是Tony的朋友 我来帮他代收快递!
货物主人Tony 手机号18512345678
接收到一个包裹,包裹内容:雪地靴
代收人Wendy
</code></pre>
<h3>从剧情中思考代理模式</h3>
<p>从上面的示例中我们可以发现,包裹实际上是 Tony 的,但是 Wendy 代替 Tony 的身份帮忙接收了包裹Wendy 需要使用 Tony 的身份并获得快递员的验证Tony 手机号)才能成功接收包裹。像这样,一个对象完成某项动作或任务,是通过对另一个对象的引用来完成,这种模式叫代理模式。</p>
<p><strong>代理模式Proxy Pattern</strong>:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做 Proxy 或 Surrogate它是一种对象结构型模式。</p>
<p>在某些情况下一个客户不想或者不能直接引用一个对象此时可以通过一个称之为“代理”的第三者来实现间接引用。如上面的示例中Tony 因为不在家,所以不能亲自接收包裹,但他可以叫 Wendy 来代他接收,这里 Wendy 就是代理,她代理了 Tony 的身份去接收快递。</p>
<h3>代理模式的模型抽象</h3>
<h4>代码框架</h4>
<p>代理模式有三个关键要素,它们分别是:</p>
<ol>
<li>主题Subject定义“操作/活动/任务”的接口类。</li>
<li>真实主题RealSubject真正完成“操作/活动/任务”的具体类。</li>
<li>代理主题ProxySubject代替真实主题完成“操作/活动/任务”的代理类。</li>
</ol>
<p>根据这三个要素,我们抽象出一个较为通用的代码框架。</p>
<pre><code class="language-python">class Subject:
&quot;主题&quot;
def request(self):
pass
class RealSubject(Subject):
&quot;代理主题&quot;
def request(self):
print(&quot;RealSubject todo something...&quot;)
class ProxySubject(Subject):
&quot;代理主题&quot;
def __init__(self, subject):
self.__realSubject = subject
def request(self):
self.preRequest()
if(self.__realSubject is not None):
self.__realSubject.request()
self.afterRequest()
def preRequest(self):
print(&quot;preRequest&quot;)
def afterRequest(self):
print(&quot;afterRequest&quot;)
def client():
&quot;客户端调用类&quot;
realObj = RealSubject()
proxyObj = ProxySubject(realObj)
proxyObj.request()
</code></pre>
<h4>类图</h4>
<p>上面的代码框架可用类图表示如下:</p>
<p><img src="assets/01a1acc0-d89c-11e7-9363-994f5e2a852c.jpg" alt="enter image description here" /></p>
<h4>基于框架的实现</h4>
<p>上面的示例代码中ReceiveParcel 其实就是 SubjectTonyReception 其实就是 RealSubjectWendyReception 其实就是 ProxySubject而 receive 其实就是 request。</p>
<p>我们可以按上面的框架对 WendyReception 进行稍微的改动,如下:</p>
<pre><code class="language-python">class WendyReception(ReceiveParcel):
&quot;Wendy接收&quot;
def __init__(self, name, receiver):
super().__init__(name)
self.__receiver = receiver
def receive(self, parcelContent):
self.preReceive()
if(self.__receiver is not None):
self.__receiver.receive(parcelContent)
self.afterReceive()
def preReceive(self):
print(&quot;我是&quot; + self.__receiver.getName() + &quot;的朋友, 我来帮他代收快递!&quot;)
def afterReceive(self):
print(&quot;代收人:&quot; + self.getName())
</code></pre>
<p>测试代码不用变。自己跑一下,会发现输出结果和之前的是一样的。</p>
<h4>模型说明</h4>
<p>代理对象可以在客户端和目标对象之间起到中间调和的作用,并且可以通过代理对象隐藏不希望被客户端看到的内容和服务,或者添加客户需要的额外服务。</p>
<p>在实现生活中能找到非常的代理模式的模型:火车票/机票的代售点;银行支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制;代表公司出席一些商务会议。</p>
<h5><strong>代理模式的优点</strong></h5>
<ol>
<li>代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。</li>
<li>可以灵活地隐藏被代理对象的部分功能和服务,也增加额外的功能和服务。</li>
</ol>
<h5><strong>代理模式的缺点</strong></h5>
<ol>
<li>由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。</li>
<li>实现代理模式需要额外的工作,有些代理模式的实现非常复杂。</li>
</ol>
<h3>应用场景</h3>
<p>1不想或者不能直接引用一个对象时</p>
<p>如在移动端加载网页信息时,因为下载真实大图比较耗费流量和性能,可以用一个小图代替进行渲染(用一个代理对象去下载小图),在真正点击图片时,才去下载大图,显示大图效果。还有 HTML 中的占位符,其实也是代理的思想。</p>
<p>2想对一个对象的功能进行加强时</p>
<p>如在字体Font渲染时对粗体BoldFont进行渲染时可使用字体 Font 对象进行代理,只要在对 Font 进行渲染后,进行一步加粗的操作即可。</p>
<p>3各种特殊用途的代理远程代理、虚拟代理、Copy-on-Write 代理、保护Protect or Access代理、Cache 代理、防火墙Firewall代理、同步化Synchronization代理、智能引用Smart Reference代理。这部分具体的运用可查阅相关<a href="https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/proxy.html">资料</a></p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/白话设计模式 28 讲(完)/06 中介模式:找房子问中介.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/白话设计模式 28 讲(完)/08 装饰模式:你想怎么穿就怎么穿.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997b89abd83cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>