learn.lianglianglee.com/专栏/白话设计模式 28 讲(完)/11 组合模式:自己组装电脑.md.html
2022-05-11 19:04:14 +08:00

813 lines
25 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>11 组合模式:自己组装电脑.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 class="current-tab" 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>11 组合模式:自己组装电脑</h1>
<p>【故事剧情】</p>
<blockquote>
<p>Tony 用的笔记本电脑还是大学时候买的到现在已经用了5年虽然后面加过一次内存也换过一次硬盘但仍然跟不上 Tony 对性能的要求,改变不了它被淘汰的命运,是时候该换一台新的电脑了……</p>
<p>换什么电脑呢MacBookThinkPad还是台式机经过几番思考之后Tony 还是决定买台式机因为作为软件开发台式机性能会更高编译程序也会更快。确定台式机后一个新的问题又来了是买一个整机呢还是自己组装呢在反复纠结两天之后Tony 还是决定自己亲自动手组装。一来自己也了解一些硬件知识,正好趁这次机会对自己的知识做一个检验和实践;二来自己组装能便宜一大笔钱!</p>
<p>于是 Tony 在京东上浏览了各个配件,花了一个星期进行精心挑选(这可真是一个精细的活,需要考虑各种型号的性能,还要考虑不同硬件之间的兼容性,还需知道各个配件的尺寸确保能正常放进机箱,因为选的是小机箱),终于确定了各个子配件:</p>
<p>GIGABYTE Z170M M-ATX 的主板、Intel Core i5-6600K 的 CPU、Kingston Fury DDR4 的内存、Kingston V300 的 SSD 硬盘、Colorful iGame750 的显卡、DEEPCOOL 120T 水冷风扇、Antec VP 450P 的电源、AOC LV243XIP 的显示器、SAMA MATX 小板机箱……</p>
<p>周末Tony 花了一天的时间才把这些配件组装成一个完整的整机。一次点亮Tony 成就感十足!与购买相同性能的整机相比,不仅价格减了三成,而且加深了对各个硬件的了解。</p>
</blockquote>
<p><img src="assets/33723c10-e39b-11e7-8709-2542c9dceea6.jpg" alt="enter image description here" /></p>
<h3>用程序来模拟生活</h3>
<p>只要你对硬件稍微有一些了解,或者打开过机箱换过组件,一定知道 CPU、内存、显卡是插在主板上的而硬盘也是连在主板上的在机箱的后面有一排的插口可以连接鼠标、键盘、耳麦、摄像头等外接配件而显示器需要单独插电源才能工作。我们可以用代码来模拟台式电脑的组成这里假设每一个组件都有开始工作和结束工作两个功能还可以显示自己的信息和组成结构。</p>
<p>源码示例:</p>
<pre><code class="language-python">class Component:
&quot;组件,所有子配件的基类&quot;
def __init__(self, name):
self._name = name
def showInfo(self, indent = &quot;&quot;):
pass
def isComposite(self):
return False
def startup(self, indent = &quot;&quot;):
print(indent + self._name + &quot; 准备开始工作...&quot;)
def shutdown(self, indent = &quot;&quot;):
print(indent + self._name + &quot; 即将结束工作...&quot;)
class CPU(Component):
&quot;中央处理器&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent, end=&quot;&quot;)
print(&quot;CPU:&quot; + self._name + &quot;,可以进行高速计算。&quot;)
class MemoryCard(Component):
&quot;内存条&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent, end=&quot;&quot;)
print(&quot;内存:&quot; + self._name + &quot;,可以缓存数据,读写速度快。&quot;)
class HardDisk(Component):
&quot;硬盘&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent, end=&quot;&quot;)
print(&quot;硬盘:&quot; + self._name + &quot;,可以永久存储数据,容量大。&quot;)
class GraphicsCard(Component):
&quot;显卡&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent, end=&quot;&quot;)
print(&quot;显卡:&quot; + self._name + &quot;,可以高速计算和处理图形图像。&quot;)
class Battery(Component):
&quot;电源&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent, end=&quot;&quot;)
print(&quot;电源:&quot; + self._name + &quot;,可以持续给主板和外接配件供电。&quot;)
class Fan(Component):
&quot;风扇&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent, end=&quot;&quot;)
print(&quot;风扇:&quot; + self._name + &quot;辅助CPU散热。&quot;)
class Displayer(Component):
&quot;显示器&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent, end=&quot;&quot;)
print(&quot;显示器:&quot; + self._name + &quot;,负责内容的显示。&quot;)
class Composite(Component):
&quot;配件组合器&quot;
def __init__(self, name):
super().__init__(name)
self._components = []
def showInfo(self, indent):
print(self._name + &quot;,由以下部件组成:&quot;)
indent += &quot;\t&quot;
for element in self._components:
element.showInfo(indent)
def isComposite(self):
return True
def addComponent(self, component):
self._components.append(component)
def removeComponent(self, component):
self._components.remove(component)
def startup(self, indent):
super().startup(indent)
indent += &quot;\t&quot;
for element in self._components:
element.startup(indent)
def shutdown(self, indent):
super().startup(indent)
indent += &quot;\t&quot;
for element in self._components:
element.shutdown(indent)
class Mainboard(Composite):
&quot;主板&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent + &quot;主板:&quot;, end=&quot;&quot;)
super().showInfo(indent)
class ComputerCase(Composite):
&quot;机箱&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent + &quot;机箱:&quot;, end=&quot;&quot;)
super().showInfo(indent)
class Computer(Composite):
&quot;电脑&quot;
def __init__(self, name):
super().__init__(name)
def showInfo(self, indent):
print(indent + &quot;电脑:&quot;, end=&quot;&quot;)
super().showInfo(indent)
</code></pre>
<p>测试代码:</p>
<pre><code class="language-python">def testComputer():
cpu = CPU(&quot;Intel Core i5-6600K&quot;)
memoryCard = MemoryCard(&quot;Kingston Fury DDR4&quot;)
hardDisk = HardDisk(&quot;Kingston V300 &quot;)
graphicsCard = GraphicsCard(&quot;Colorful iGame750&quot;)
mainBoard = Mainboard(&quot;GIGABYTE Z170M M-ATX&quot;)
mainBoard.addComponent(cpu)
mainBoard.addComponent(memoryCard)
mainBoard.addComponent(hardDisk)
mainBoard.addComponent(graphicsCard)
battery = Battery(&quot;Antec VP 450P&quot;)
fan = Fan(&quot;DEEPCOOL 120T&quot;)
computerCase = ComputerCase(&quot;SAMA MATX&quot;)
computerCase.addComponent(battery)
computerCase.addComponent(mainBoard)
computerCase.addComponent(fan)
displayer = Displayer(&quot;AOC LV243XIP&quot;)
computer = Computer(&quot;Tony DIY电脑&quot;)
computer.addComponent(displayer)
computer.addComponent(computerCase)
computer.showInfo(&quot;&quot;)
print(&quot;\n开机过程:&quot;)
computer.startup(&quot;&quot;)
print(&quot;\n关机过程:&quot;)
computer.shutdown(&quot;&quot;)
</code></pre>
<p>输出结果:</p>
<pre><code>电脑:Tony DIY电脑,由以下部件组成:
显示器:AOC LV243XIP负责内容的显示。
机箱:SAMA MATX,由以下部件组成:
电源:Antec VP 450P,可以持续给主板和外接配件供电。
主板:GIGABYTE Z170M M-ATX,由以下部件组成:
CPU:Intel Core i5-6600K,可以进行高速计算。
内存:Kingston Fury DDR4,可以缓存数据,读写速度快。
硬盘:Kingston V300 ,可以永久存储数据,容量大。
显卡:Colorful iGame750,可以高速计算和处理图形图像。
风扇:DEEPCOOL 120T辅助CPU散热。
开机过程:
Tony DIY电脑 准备开始工作...
AOC LV243XIP 准备开始工作...
SAMA MATX 准备开始工作...
Antec VP 450P 准备开始工作...
GIGABYTE Z170M M-ATX 准备开始工作...
Intel Core i5-6600K 准备开始工作...
Kingston Fury DDR4 准备开始工作...
Kingston V300 准备开始工作...
Colorful iGame750 准备开始工作...
DEEPCOOL 120T 准备开始工作...
关机过程:
Tony DIY电脑 准备开始工作...
AOC LV243XIP 即将结束工作...
SAMA MATX 准备开始工作...
Antec VP 450P 即将结束工作...
GIGABYTE Z170M M-ATX 准备开始工作...
Intel Core i5-6600K 即将结束工作...
Kingston Fury DDR4 即将结束工作...
Kingston V300 即将结束工作...
Colorful iGame750 即将结束工作...
DEEPCOOL 120T 即将结束工作...
</code></pre>
<h3>从剧情中思考组合模式</h3>
<p>Tony 自己 DIY 组装的电脑是由各个配件组成的,在组装之前,就是一个个 CPU、硬盘、显卡等配件不能称之为电脑只有把它们按正确的方式组装在一起配合操作系统才能正常运行。一般人使用电脑并不会关注内部的组成结构只会关注一台整机。</p>
<p>这里有明显的部分与整体的关系,主板、电源等是电脑的一部分,而主板上又有 CPU、硬盘、显卡它们又可以认为是主板的一部分。像电脑一样把对象组合成树形结构以表示“部分-整体”的层次结构的程序设计模式就叫<strong>组合模式</strong>。组合模式使得用户对单个对象和组合对象的使用具有一致性,使用组合对象就像使用一般对象一样,不便关心内部的组织结构。</p>
<p>如上面的示例中,组合的电脑具有明显层次组合关系,如:</p>
<p><img src="assets/6c2db3e0-e39b-11e7-b359-dd1a130fd3aa.jpg" alt="enter image description here" /></p>
<p>我们将这种层次关系转换成对象的组合关系如下:</p>
<p><img src="assets/7929fb80-e39b-11e7-a626-77bb30a498d5.jpg" alt="enter image description here" /></p>
<h3>组合模式的模型抽象</h3>
<h4>类图</h4>
<p>根据上面组装电脑的示例,将组合模式抽象成一般化的类图关系如下:</p>
<p><img src="assets/8afe5810-e39b-11e7-8709-2542c9dceea6.jpg" alt="enter image description here" /></p>
<p>这里 Composite 就是组合对象,组合对象可以添加或删除组件,它本身也是一个组件,因此组合对象可以像一般对象一样被使用,因为它也实现了 Component的feature() 方法。</p>
<h4>模型说明</h4>
<p>组合模式是一个非常常用的模式,你可能在有意或无意间就已经用上了,比如公司(各个部门或各个子公司)的组织架构,学校各个学院-班级的关系。</p>
<p>如在图形绘制系统中图元GraphicUnit可以有多种不同的类型Text、Line、Rect、Ellipse 等还可以是矢量图vectorgraph。而矢量图本身又是由一个或多个 Text、Line、Rect、Ellipse 组成。但所有的图元都有一个共同的方法,那就是 draw()。这里就得用组合模式。</p>
<p><img src="assets/c1e18d70-e39b-11e7-9109-7f9bd2e50434.jpg" alt="enter image description here" /></p>
<h5><strong>组合模式的优点</strong></h5>
<ol>
<li>调用简单,组合对象可以像一般对象一样使用。</li>
<li>组合对象可以自由地增加、删除组件,可灵活地组合不同的对象。</li>
</ol>
<h5><strong>组合模式的缺点</strong></h5>
<p>在一些层次结构太深的场景中,组合结构会变得太庞杂。</p>
<h3>应用场景</h3>
<ol>
<li>对象之间具有明显的“部分-整体”的关系时,或者具有层次关系时。</li>
<li>组合对象与单个对象具有相同或类似行为(方法),用户希望统一地使用组合结构中的所有对象。</li>
</ol>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/白话设计模式 28 讲(完)/10 迭代模式:下一个就是你了.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/白话设计模式 28 讲(完)/12 构建模式:想要车还是庄园.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":"70997b930a893cfa","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>