learn.lianglianglee.com/专栏/白话设计模式 28 讲(完)/18 外观模式:学妹别慌,学长帮你.md.html
2022-05-11 19:04:14 +08:00

812 lines
31 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>18 外观模式:学妹别慌,学长帮你.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 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 class="current-tab" 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>18 外观模式:学妹别慌,学长帮你</h1>
<p>【故事剧情】</p>
<blockquote>
<p>Tony 有个爱好喜欢跑步。因为住的离北体北京体育大学比较近便经常去北体跑步校园里环境优雅、场地开阔。正直金色九月的一天Tony 一如往常的来到北体的开放田径场,但与往常不同的是 Tony 看到了成群的学生穿着蓝色的军服在参加军训。看着这群活力四射的新生迈着整齐的步伐忽然有一种熟悉的感觉……是的Tony 想起了自己的大学生活,想起了自己参加过的军训,更想起了自己刚踏入大学校园的那一天!</p>
<p>2010 年 9 月 10 日Tony 拖着一个行旅箱,背着一个背包,独自一人坐上了一辆前往南昌的大巴,开始了自己的大学生涯。路上遇到堵车,一路兜兜转转,到站时已经很晚了,还好赶上了学校在汽车站的最后一趟迎新接送班车,感觉如释重负!到达学校时已是下午六点多了,天色已渐入黄昏!一路舟车劳顿,身心具备的 Tony 一下车有种不知所措的感觉……</p>
<p>正当 Tony 四处张望寻找该去哪儿报到时,一位热情的志愿者走过来问:“你好!我是新生报到的志愿者,你是报道的新生吧!哪个学院的呢?”</p>
<p>Tony 有点蒙:“什么...学院?”</p>
<p>志愿者:“你录取通知书上写的是什么专业?”</p>
<p>Tony“哦软件工程</p>
<p>志愿者:“那就是软件学院,正好我也是这个专业的,我叫 Frank是你学长哈哈</p>
<p>Tony“学长好</p>
<p>志愿者:“你是一个人来的吗?一路坐车累了吧!我帮你拿行李吧!这边走,我带你去报到……”</p>
<p>在 Frank 的帮助下Tony 先到活动中心完成了报到登记,然后去缴费窗口缴完学费,之后又到生活中心领了生活用品,最后再到宿舍完成入住。这一系列流程走完,差不多花了一个小时,还是在 Frank 的热心帮助下!如果是 Tony 一个人面对这陌生的环境和场所所花的时间更是难以想象。报道流程结束后Frank 还带 Tony 到食堂,请他吃了顿饭,带他到校园走了半圈……</p>
<p>Tony 读大二、大三时每一年新生入学时作为老鸟的他也毅然决然地成为了迎新志愿者的一员迎接新一届的学弟学妹加入志愿者后Tony 发现这里真是有不少“假”志愿者!因为要是学妹来了,一群学长都围过去,抡着去帮忙;虽然学弟也不拒绝,但明显就没了抢的姿势,在理工类学院,学姐抢学弟的事是绝对不可能发生的!</p>
</blockquote>
<h3>用程序来模拟生活</h3>
<p>9 月是所有大学的入学季,新生入学报道是学校的一项大工程,每一个学校都有自己的报道流程和方式,但都少不了志愿者这一重要角色!一来,学长学姐带学弟学妹是尊师重教的一种优良传统;二来,轻车熟路的学长学姐作为志愿者为入学新生服务,能给刚入学的新生减少了诸多不必要的麻烦。下面我们用程序来模拟一下新生报到的整个流程。</p>
<p>源码示例:</p>
<pre><code class="language-python">class Register:
&quot;入学报到&quot;
def register(self, name):
print(&quot;活动中心:&quot; + name + &quot;同学报到成功!&quot;)
class Payment:
&quot;缴费&quot;
def pay(self, name, money):
print(&quot;缴费中心:&quot; + &quot;收到&quot; + name + &quot;同学&quot; + str(money) + &quot;元付款,缴费成功!&quot;)
class DormitoryManagementCenter:
&quot;宿舍管理中心(生活中心)&quot;
def provideLivingGoods(self, name):
print(&quot;生活中心:&quot; + name + &quot;同学的生活用品已发放。&quot;)
class Dormitory:
&quot;宿舍&quot;
def meetRoommate(self, name):
print(&quot;宿 舍:&quot; + &quot;大家好!这是刚来的&quot; + name + &quot;同学,是你们未来需要共度四年的室友!相互认识一下……&quot;)
class Volunteer:
&quot;迎新志愿者&quot;
def __init__(self, name):
self.__name = name
self.__register = Register()
self.__payment = Payment()
self.__lifeCenter = DormitoryManagementCenter()
self.__dormintory = Dormitory()
def welcomeFreshmen(self, name):
print(&quot;你好,&quot; + name + &quot;同学! 我是新生报到的志愿者&quot; + self.__name
+ &quot;,我将带你完成整个报到流程。&quot;)
self.__register.register(name)
self.__payment.pay(name, 10000)
self.__lifeCenter.provideLivingGoods(name)
self.__dormintory.meetRoommate(name)
</code></pre>
<p>测试代码:</p>
<pre><code class="language-python">def testRegister():
volunteer = Volunteer(&quot;Frank&quot;)
volunteer.welcomeFreshmen(&quot;Tony&quot;)
</code></pre>
<p>输出结果:</p>
<pre><code>你好,Tony同学! 我是新生报到的志愿者Frank我将带你完成整个报到流程。
活动中心:Tony同学报到成功
缴费中心:收到Tony同学10000元付款缴费成功
生活中心:Tony同学的生活用品已发放。
宿 舍:大家好这是刚来的Tony同学是你们未来需要共度四年的室友相互认识一下……
</code></pre>
<h3>从剧情中思考外观模式</h3>
<p>在上面的示例中,迎新志愿者陪同并帮助入学新生完成报到登记、缴纳学费、领日用品、入住宿舍等一系列的报到流程。新生不用知道具体的报到流程,不用去寻找各个场地;只要跟着志愿者走,到指定的地点,根据志愿者的指导,完成指定的任务即可。志愿者虽然不是直接提供这些报到服务,但也相当于间接提供了报到登记、缴纳学费、领日用品、入住宿舍等一条龙的服务,帮新生减轻了不少麻烦和负担。</p>
<p>在这里志愿者就相当于一个对接人,将复杂的业务通过一个对接人来提供一整套统一的(一条龙式的)服务,让用户不用关心内部复杂的运行机制。这种方式在程序中叫<strong>外观模式</strong>,也是门面模式。</p>
<h4>外观模式</h4>
<blockquote>
<p>Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.</p>
<p>为子系统中的一组接口提供一个一致的界面称为<strong>外观模式</strong>,外观模式定义了一个高层接口,这个接口使得这一子系统更容易使用。</p>
</blockquote>
<p>外观模式的核心思想:用一个简单的接口来封装一个复杂的系统,使这个系统更容易使用。</p>
<h3>外观模式的模型抽象</h3>
<h4>类图</h4>
<p>外观模式的类图表示如下:</p>
<p><img src="assets/90d44460-8351-11e8-a90b-215c3565b75a.jpg" alt="enter image description here" /></p>
<p>Facade 封装了子系统的复杂实现,给外部提供了一个统一的接口,用户只需要通过 Facade 来访问子系统。</p>
<p>外观模式虽然很简单,但却是非常常用的一种模式,它测为一个复杂的系统提供一个简单可用的的调用接口。如有一个运行多年的老项目 A现在要开发的新项目 B 要用到项目 A 的部分功能,但由于项目 A 维护的时间太长了(真实的场景很可能是原来的开发人员都离职了,后期的维护人员在原来的系统上随便修修改改),类的结构和关系非常庞杂,调用关系也比较复杂,重新开发一套成本又比较高。这个时候就需要对系统 A 进行封装,提供一个简单可用的接口,方便项目 B 的开发者进行调用。</p>
<p>在软件的层次化结构设计中,可以使用外观模式来定义每一层系统的调用接口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。这时就会有如下这样的层次结构图:</p>
<p><img src="assets/c0c47410-8351-11e8-9f90-95294e517933.jpg" alt="img" /></p>
<p>我曾经开发过的一个电子书阅读器就采用了这样一种层次结构分明的软件结构设计。如下图:</p>
<p><img src="assets/226f3330-8352-11e8-9f90-95294e517933.jpg" alt="enter image description here" /></p>
<h4>模型说明</h4>
<h5><strong>设计要点</strong></h5>
<p>外观模式是最简单的设计模式之一,只有两个角色。</p>
<p><strong>外观角色Facade</strong> 为子系统封装统一的对外接口,如同子系统的一个门面。这个类一般不负责具体的业务逻辑,只是一个委托类,具体的业务逻辑由子系统完成。</p>
<p><strong>子系统SubSystem</strong> 由多个类组成的具有某一特定功能的子系统。可以是第三方库,也可以是自己的基础库,还可能是一个子服务,为整个系统提供特定的功能或服务。</p>
<h5><strong>优缺点</strong></h5>
<p>优点:</p>
<ul>
<li>实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端。</li>
<li>简化了客户端对子系统的使用难度,客户端(用户)无须关心子系统的具体实现方式,而只需要和外观进行交互即可。</li>
<li>为不同的用户提供了统一的调用接口,方便了系统的管理和维护。</li>
</ul>
<p>缺点:因为统一了调用的接口,降低了系统功能的灵活性。</p>
<h3>实战应用</h3>
<p>在互联网世界中文件的压缩与解压是一项非常重要的功能它不仅能降低文件的存储空间还能减少网络带宽现在最常用的压缩文件格式有ZIP、RAR、7Z 。从压缩率看ZIP &lt; RAR &lt; 7Z即 7Z 的压缩比最高从压缩时间看ZIP &lt; RAR &lt; 7Z即 ZIP 的压缩速度最快。从普及率上看ZIP 应该是应用最广泛的,因为出现的时间最早,格式开放且免费;而 7Z 因为其极高的压缩比和开放性,大有赶超之势。</p>
<p>假设我们有一个压缩与解压缩系统专门处理文件的压缩和解压,这个系统有三个模块 ZIPModel、RARModel、ZModel 分别处理 ZIP、RAR、7Z 三种文件格式的压缩与解压。现在这一系统要提供给上层应用程序使用。</p>
<p>为了让这一系统更方便使用,这时就可以用外观模式进行封装,定义一套统一的调用接口,我们用代码实现一下。</p>
<p><strong>源码示例:</strong></p>
<pre><code class="language-python">from os import path
import logging
class ZIPModel:
&quot;ZIP模块负责ZIP文件的压缩与解压&quot;
def compress(self, srcFilePath, dstFilePath):
print(&quot;ZIP模块正在进行 '&quot; + srcFilePath + &quot;' 文件的压缩......&quot;)
print(&quot;文件压缩成功,已保存至 '&quot; + dstFilePath + &quot;'&quot;)
def decompress(self, srcFilePath, dstFilePath):
print(&quot;ZIP模块正在进行 '&quot; + srcFilePath + &quot;' 文件的解压......&quot;)
print(&quot;文件解压成功,已保存至 '&quot; + dstFilePath+ &quot;'&quot;)
class RARModel:
&quot;RAR模块负责ZIP文件的压缩与解压&quot;
def compress(self, srcFilePath, dstFilePath):
print(&quot;RAR模块正在进行 '&quot; + srcFilePath + &quot;' 文件的压缩......&quot;)
print(&quot;文件压缩成功,已保存至 '&quot; + dstFilePath + &quot;'&quot;)
def decompress(self, srcFilePath, dstFilePath):
print(&quot;RAR模块正在进行 '&quot; + srcFilePath + &quot;' 文件的解压......&quot;)
print(&quot;文件解压成功,已保存至 '&quot; + dstFilePath + &quot;'&quot;)
class ZModel:
&quot;7Z模块负责7Z文件的压缩与解压&quot;
def compress(self, srcFilePath, dstFilePath):
print(&quot;7Z模块正在进行 '&quot; + srcFilePath + &quot;' 文件的压缩......&quot;)
print(&quot;文件压缩成功,已保存至 '&quot; + dstFilePath + &quot;'&quot;)
def decompress(self, srcFilePath, dstFilePath):
print(&quot;7Z模块正在进行 '&quot; + srcFilePath + &quot;' 文件的解压......&quot;)
print(&quot;文件解压成功,已保存至 '&quot; + dstFilePath + &quot;'&quot;)
class CompressionFacade:
&quot;压缩系统的外观类&quot;
def __init__(self):
self.__zipModel = ZIPModel()
self.__rarModel = RARModel()
self.__zModel = ZModel()
def compress(self, srcFilePath, dstFilePath, type):
&quot;根据不同的压缩类型,压缩成不同的格式&quot;
# 获取新的文件名
extName = &quot;.&quot; + type
fullName = dstFilePath + extName
if (type.lower() == &quot;zip&quot;) :
self.__zipModel.compress(srcFilePath, fullName)
elif(type.lower() == &quot;rar&quot;):
self.__rarModel.compress(srcFilePath, fullName)
elif(type.lower() == &quot;7z&quot;):
self.__zModel.compress(srcFilePath, fullName)
else:
logging.error(&quot;Not support this format:&quot; + str(type))
return False
return True
def decompress(self, srcFilePath, dstFilePath):
&quot;从srcFilePath中获取后缀根据不同的后缀名(拓展名),进行不同格式的解压&quot;
baseName = path.basename(srcFilePath)
extName = baseName.split(&quot;.&quot;)[1]
if (extName.lower() == &quot;zip&quot;) :
self.__zipModel.decompress(srcFilePath, dstFilePath)
elif(extName.lower() == &quot;rar&quot;):
self.__rarModel.decompress(srcFilePath, dstFilePath)
elif(extName.lower() == &quot;7z&quot;):
self.__zModel.decompress(srcFilePath, dstFilePath)
else:
logging.error(&quot;Not support this format:&quot; + str(extName))
return False
return True
</code></pre>
<p><strong>测试代码:</strong></p>
<pre><code class="language-python">def testCompression():
facade = CompressionFacade()
facade.compress(&quot;E:\生活中的设计模式\生活中的外观模式——学妹别慌,学长帮你.md&quot;,
&quot;E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你&quot;, &quot;zip&quot;)
facade.decompress(&quot;E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.zip&quot;,
&quot;E:\解析文件\生活中的外观模式——学妹别慌,学长帮你.md&quot;)
print()
facade.compress(&quot;E:\生活中的设计模式\生活中的外观模式——学妹别慌,学长帮你.md&quot;,
&quot;E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你&quot;, &quot;rar&quot;)
facade.decompress(&quot;E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.rar&quot;,
&quot;E:\解析文件\生活中的外观模式——学妹别慌,学长帮你.md&quot;)
print()
facade.compress(&quot;E:\生活中的设计模式\生活中的外观模式——学妹别慌,学长帮你.md&quot;,
&quot;E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你&quot;, &quot;7z&quot;)
facade.decompress(&quot;E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.7z&quot;,
&quot;E:\解析文件\生活中的外观模式——学妹别慌,学长帮你.md&quot;)
print()
</code></pre>
<p><strong>输出结果:</strong></p>
<pre><code>ZIP模块正在进行 'E:\生活中的设计模式\生活中的外观模式——学妹别慌,学长帮你.md' 文件的压缩......
文件压缩成功,已保存至 'E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.zip'
ZIP模块正在进行 'E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.zip' 文件的解压......
文件解压成功,已保存至 'E:\解析文件\生活中的外观模式——学妹别慌,学长帮你.md'
RAR模块正在进行 'E:\生活中的设计模式\生活中的外观模式——学妹别慌,学长帮你.md' 文件的压缩......
文件压缩成功,已保存至 'E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.rar'
RAR模块正在进行 'E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.rar' 文件的解压......
文件解压成功,已保存至 'E:\解析文件\生活中的外观模式——学妹别慌,学长帮你.md'
7Z模块正在进行 'E:\生活中的设计模式\生活中的外观模式——学妹别慌,学长帮你.md' 文件的压缩......
文件压缩成功,已保存至 'E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.7z'
7Z模块正在进行 'E:\压缩文件\生活中的外观模式——学妹别慌,学长帮你.7z' 文件的解压......
文件解压成功,已保存至 'E:\解析文件\生活中的外观模式——学妹别慌,学长帮你.md'
</code></pre>
<p>在上面的例子中,为了简单起见,我们通过后缀名(拓展名)来区分不同的文件格式,不同的文件格式采用不同的解压方式来进行解压。在实际的项目开发中,不应该通过文件后缀名来区分文件格式,因为用户可能将一个 RAR 格式的文件改成 .zip 的后缀,这会造成解压的错误;而应该通过文件的魔数来判断,每一种格式的文件,在二进制文件的开头都会有一个魔数来说明该文件的类型(可通过二进制文件工具查看,如 WinHex如 ZIP 的魔数是 PK50 4B 03 04RAR 的魔数是 Rar52 61 727z 的魔数是 7z37 7A</p>
<h3>应用场景</h3>
<ul>
<li>当要为一个复杂子系统提供一个简单接口时;</li>
<li>客户程序与多个子系统之间存在很大的依赖性,引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性;</li>
<li>在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。</li>
</ul>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/白话设计模式 28 讲(完)/17 享元模式:颜料很贵必须充分利用.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/白话设计模式 28 讲(完)/19 访问模式:一千个读者一千个哈姆雷特.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":"70997ba32f933cfa","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>