mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-26 21:26:41 +08:00
1159 lines
26 KiB
HTML
1159 lines
26 KiB
HTML
<!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>08 装饰模式:你想怎么穿就怎么穿.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 class="current-tab" 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>08 装饰模式:你想怎么穿就怎么穿</h1>
|
||
|
||
<p>故事剧情</p>
|
||
|
||
<blockquote>
|
||
|
||
<p>工作两年后,Tony 因为换工作而搬了一次家!这是一个4室1厅1卫1厨的户型,住了4户人家。恰巧这里住的都是年轻人,有男孩也有女孩,而 Tony 就是在这里遇上了自己喜欢的人,她叫 Jenny。Tony 和 Jenny 每天都低头不见抬头见,但 Tony 是一个程序猿,天生不善言辞、不懂着装,老被 Jenny 嫌弃:满脸猥琐,一副屌丝样!</p>
|
||
|
||
<p>被嫌弃后,Tony 痛定思痛:一定要改善一下自己的形象,摆脱屌丝样!于是叫上自己的死党 Henry 去了五彩城……</p>
|
||
|
||
<p>Tony 在这个大商城中兜兜转转,被各个商家教化着该怎样搭配衣服:衬衫要套在腰带里面,风衣不要系纽扣,领子要立起来……</p>
|
||
|
||
<p>在反复试穿了一个晚上的衣服之后,终于找到一套还算凑合的行装:下面是一条卡其色休闲裤配一双深色休闲皮鞋,加一条银色针扣头的黑色腰带;上面是一件紫红色针织毛衣,内套一件白色衬衫;头上带一副方形黑框眼镜。整体行装虽不潮流,却透露出一种工作人士的成熟、稳健和大气!</p>
|
||
|
||
</blockquote>
|
||
|
||
<p><img src="assets/c09637c0-71f0-11e8-b1ea-d92fd075c722.jpg" alt="img" /></p>
|
||
|
||
<p>(图片来自网络)</p>
|
||
|
||
<h3>用程序来模拟生活</h3>
|
||
|
||
<p>Tony 是一个程序员,给自己搭配了一套着装:一条卡其色休闲裤、一双深色休闲皮鞋、一条银色针扣头的黑色腰带、一件紫红色针织毛衣、一件白色衬衫、一副方形黑框眼镜。但类似的着装也可以穿在其他的人身上,比如一个老师也可以这样穿:一双深色休闲皮鞋、一件白色衬衫、一副方形黑框眼镜。</p>
|
||
|
||
<p>我们就用程序来模拟这样一个情景。</p>
|
||
|
||
<p>源码示例:</p>
|
||
|
||
<pre><code class="language-python">class Person:
|
||
|
||
"人"
|
||
|
||
|
||
|
||
def __init__(self, name):
|
||
|
||
self.__name = name
|
||
|
||
|
||
|
||
def getName(self):
|
||
|
||
return self.__name
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
print("我的着装是:")
|
||
|
||
|
||
|
||
class Engineer(Person):
|
||
|
||
"工程师"
|
||
|
||
|
||
|
||
def __init__(self, name, skill):
|
||
|
||
super().__init__(name)
|
||
|
||
self.__skill = skill
|
||
|
||
|
||
|
||
def getSkill(self):
|
||
|
||
return self.__skill
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
print("我是" + self.getSkill() + "工程师" + self.getName())
|
||
|
||
super().wear()
|
||
|
||
|
||
|
||
class Teacher(Person):
|
||
|
||
"教师"
|
||
|
||
|
||
|
||
def __init__(self, name, title):
|
||
|
||
super().__init__(name)
|
||
|
||
self.__title = title
|
||
|
||
|
||
|
||
def getTitle(self):
|
||
|
||
return self.__title
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
print("我是" + self.getName() + self.getTitle())
|
||
|
||
super().wear()
|
||
|
||
|
||
|
||
class ClothingDecorator(Person):
|
||
|
||
"服装装饰器"
|
||
|
||
|
||
|
||
def __init__(self, person):
|
||
|
||
self._decorated = person
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
self._decorated.wear()
|
||
|
||
|
||
|
||
class CasualPantDecorator(ClothingDecorator):
|
||
|
||
"休闲裤"
|
||
|
||
|
||
|
||
def __init__(self, person):
|
||
|
||
super().__init__(person)
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
super().wear()
|
||
|
||
print("一条卡其色休闲裤")
|
||
|
||
|
||
|
||
class BeltDecorator(ClothingDecorator):
|
||
|
||
"腰带"
|
||
|
||
|
||
|
||
def __init__(self, person):
|
||
|
||
super().__init__(person)
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
super().wear()
|
||
|
||
print("一条银色针扣头的黑色腰带")
|
||
|
||
|
||
|
||
class LeatherShoesDecorator(ClothingDecorator):
|
||
|
||
"皮鞋"
|
||
|
||
|
||
|
||
def __init__(self, person):
|
||
|
||
super().__init__(person)
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
super().wear()
|
||
|
||
print("一双深色休闲皮鞋")
|
||
|
||
|
||
|
||
class KnittedSweaterDecorator(ClothingDecorator):
|
||
|
||
"针织毛衣"
|
||
|
||
|
||
|
||
def __init__(self, person):
|
||
|
||
super().__init__(person)
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
super().wear()
|
||
|
||
print("一件紫红色针织毛衣")
|
||
|
||
|
||
|
||
class WhiteShirtDecorator(ClothingDecorator):
|
||
|
||
"白色衬衫"
|
||
|
||
|
||
|
||
def __init__(self, person):
|
||
|
||
super().__init__(person)
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
super().wear()
|
||
|
||
print("一件白色衬衫")
|
||
|
||
|
||
|
||
class GlassesDecorator(ClothingDecorator):
|
||
|
||
"眼镜"
|
||
|
||
|
||
|
||
def __init__(self, person):
|
||
|
||
super().__init__(person)
|
||
|
||
|
||
|
||
def wear(self):
|
||
|
||
super().wear()
|
||
|
||
print("一副方形黑框眼镜")
|
||
|
||
|
||
|
||
</code></pre>
|
||
|
||
<p>测试代码:</p>
|
||
|
||
<pre><code class="language-python">def testDecorator():
|
||
|
||
tony = Engineer("Tony", "客户端")
|
||
|
||
pant = CasualPantDecorator(tony)
|
||
|
||
belt = BeltDecorator(pant)
|
||
|
||
shoes = LeatherShoesDecorator(belt)
|
||
|
||
shirt = WhiteShirtDecorator(shoes)
|
||
|
||
sweater = KnittedSweaterDecorator(shirt)
|
||
|
||
glasses = GlassesDecorator(sweater)
|
||
|
||
glasses.wear()
|
||
|
||
|
||
|
||
print()
|
||
|
||
decorateTeacher = GlassesDecorator(WhiteShirtDecorator(LeatherShoesDecorator(Teacher("wells", "教授"))))
|
||
|
||
decorateTeacher.wear()
|
||
|
||
|
||
|
||
</code></pre>
|
||
|
||
<p>上面的测试代码中:</p>
|
||
|
||
<pre><code>decorateTeacher = GlassesDecorator(WhiteShirtDecorator(LeatherShoesDecorator(Teacher("wells", "教授"))))
|
||
|
||
|
||
|
||
</code></pre>
|
||
|
||
<p>这个写法,大家不要觉得奇怪,它其实就是将多个对象的创建过程合在了一起,其实是一种优雅的写法(是不是少了好几行代码?)。创建的 Teacher 对象又通过参数传给 LeatherShoesDecorator 的构造函数,而创建的 LeatherShoesDecorator 对象又通过参数传给 WhiteShirtDecorator 的构造函数,以此类推……</p>
|
||
|
||
<p>输出结果:</p>
|
||
|
||
<pre><code>我是客户端工程师Tony
|
||
|
||
我的着装是:
|
||
|
||
一条卡其色休闲裤
|
||
|
||
一条银色针扣头的黑色腰带
|
||
|
||
一双深色休闲皮鞋
|
||
|
||
一件白色衬衫
|
||
|
||
一件紫红色针织毛衣
|
||
|
||
一副方形黑框眼镜
|
||
|
||
|
||
|
||
我是wells教授
|
||
|
||
我的着装是:
|
||
|
||
一双深色休闲皮鞋
|
||
|
||
一件白色衬衫
|
||
|
||
一副方形黑框眼镜
|
||
|
||
|
||
|
||
</code></pre>
|
||
|
||
<h3>从剧情中思考装饰模式</h3>
|
||
|
||
<p>上面的示例中,Tony 为了改善自己的形象,整体换了个着装,改变了自己的气质,使自己看起来不再是那么猥琐的屌丝样。俗话说一个人帅不帅,得看三分长相七分打扮。同一个人,不一样的着装,给人一种完全不一样的感觉。我们可以任意搭配不同的衣服、围巾、裤子、鞋子、眼镜、帽子以达到不同的效果。不同的搭配,形成不同的风格,透露不同的气质。在这个追求个性与自由的时代,穿着的风格可谓是开放到了极致,真是你想怎么穿就怎么穿!如果你是去参加一个正式会议或演讲,可以穿一套标配西服;如果你是去大草原,想骑着骏马驰骋天地,便该穿上一套马服马裤马鞋;如果你是漫迷,去参加动漫节,亦可穿上 Cosplay 的衣服,让自己成为那个内心向往的主角;如果你是……</p>
|
||
|
||
<p>这样一个时时刻刻发现在我们生活中的着装问题,就是程序中装饰模式的典型样例。</p>
|
||
|
||
<h4>装饰模式</h4>
|
||
|
||
<p><strong>装饰模式</strong>(Decorator Pattern):动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。</p>
|
||
|
||
<p>就故事中这个示例来说,由结构庞大的子类继承关系转换成了结构紧凑的装饰关系:</p>
|
||
|
||
<p><img src="assets/a01f99d0-71f2-11e8-b1ea-d92fd075c722.jpg" alt="enter image description here" /></p>
|
||
|
||
<p>继承关系:</p>
|
||
|
||
<p><img src="assets/cc4f8d30-71f2-11e8-9e76-835574e7e257.jpg" alt="img" /></p>
|
||
|
||
<p>装饰关系</p>
|
||
|
||
<h4>装饰模式的特点</h4>
|
||
|
||
<ol>
|
||
|
||
<li><strong>可灵活地给一个对象增加职责或拓展功能</strong></li>
|
||
|
||
</ol>
|
||
|
||
<p>如上面的示例中,可任意地穿上自己想穿的衣服。不管穿上什么衣服,你还是那个你,但穿上不同的衣服你就会有不同的外表。</p>
|
||
|
||
<ol>
|
||
|
||
<li><strong>可增加任意多个装饰</strong></li>
|
||
|
||
</ol>
|
||
|
||
<p>你可以只穿一件衣服,也可以只穿一条裤子,也可以衣服和裤子各种搭配的穿,全随你意!</p>
|
||
|
||
<ol>
|
||
|
||
<li><strong>装饰的顺序不同,可能产生不同的效果</strong></li>
|
||
|
||
</ol>
|
||
|
||
<p>在上面的示例中,Tony 是针织毛衣穿在外面,白色衬衫穿在里面。当然,如果你愿意(或因为怕冷),也可以针织毛衣穿在里面,白色衬衫穿在外面。但两种着装穿出来的效果,给人的感觉肯定是完全不一样的,自己脑补一下,哈哈!</p>
|
||
|
||
<p>使用装饰模式的方式,想要改变装饰的顺序,也是非常简单的。只要把测试代码稍微改动一下即可,如下:</p>
|
||
|
||
<pre><code class="language-python">def testDecorator2():
|
||
|
||
tony = Engineer("Tony", "客户端")
|
||
|
||
pant = CasualPantDecorator(tony)
|
||
|
||
belt = BeltDecorator(pant)
|
||
|
||
shoes = LeatherShoesDecorator(belt)
|
||
|
||
sweater = KnittedSweaterDecorator(shoes)
|
||
|
||
shirt = WhiteShirtDecorator(sweater)
|
||
|
||
glasses = GlassesDecorator(shirt)
|
||
|
||
glasses.wear()
|
||
|
||
|
||
|
||
</code></pre>
|
||
|
||
<p>结果如下:</p>
|
||
|
||
<pre><code>我是客户端工程师Tony
|
||
|
||
我的着装是:
|
||
|
||
一条卡其色休闲裤
|
||
|
||
一条银色针扣头的黑色腰带
|
||
|
||
一双深色休闲皮鞋
|
||
|
||
一件紫红色针织毛衣
|
||
|
||
一件白色衬衫
|
||
|
||
一副方形黑框眼镜
|
||
|
||
|
||
|
||
</code></pre>
|
||
|
||
<h3>装饰模式的模型抽象</h3>
|
||
|
||
<h4>类图</h4>
|
||
|
||
<p>通过上面的示例代码,我们知道了装饰模式的一个典型实现。我们再将其抽象成一个一般化的类图结构,如下:</p>
|
||
|
||
<p><img src="assets/67662860-71f3-11e8-be4d-f5aac07d9486.jpg" alt="img" /></p>
|
||
|
||
<p>上图中的 Component 是一个抽象类,代表具有某中功能(function)的组件,ComponentImplA 和 ComponentImplB 分别是其具体的实现子类。Decorator 是 Component 装饰器,里面有一个 Component 的对象 decorated,这就是被装饰的对象,装饰器可为被装饰对象添加额外的功能或行为(addBehavior)。DecoratorImplA 和 DecoratorImplB 分别是两个具体的装饰器(实现子类)。</p>
|
||
|
||
<p>这样一种模式很好地将装饰器与被装饰的对象进行解耦。</p>
|
||
|
||
<h4>模型说明</h4>
|
||
|
||
<p><strong>装饰模式的优点:</strong></p>
|
||
|
||
<ol>
|
||
|
||
<li>使用装饰模式来实现扩展比继承更加灵活,它可以在不需要创造更多子类的情况下,将对象的功能加以扩展。</li>
|
||
|
||
<li>可以动态地给一个对象附加更多的功能。</li>
|
||
|
||
<li>可以用不同的装饰器进行多重装饰,装饰的顺序不同,可能产生不同的效果。</li>
|
||
|
||
<li>装饰类和被装饰类可以独立发展,不会相互耦合;装饰模式相当于是继承的一个替代模式。</li>
|
||
|
||
</ol>
|
||
|
||
<p><strong>装饰模式的缺点:</strong></p>
|
||
|
||
<ol>
|
||
|
||
<li>与继承相比,用装饰的方式拓展功能更加容易出错,排错也更困难。对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。</li>
|
||
|
||
</ol>
|
||
|
||
<h3>应用场景</h3>
|
||
|
||
<ol>
|
||
|
||
<li>有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。</li>
|
||
|
||
<li>需要动态地增加或撤销功能时。</li>
|
||
|
||
<li>不能采用生成子类的方法进行扩充时,如类定义不能用于生成子类。</li>
|
||
|
||
</ol>
|
||
|
||
<p>装饰模式的应用场景非常广泛。如在实际项目开发中经常看到的过滤器,便可用装饰模式的方式去实现。如果你是 Java 程序员,对 IO 中的 FilterInputStream 和 FilterOutputStream 一定不陌生,它的实现其实就是一个装饰模式。FilterInputStream(FilterOutputStream) 就是一个装饰器,而 InputStream(OutputStream) 就是被装饰的对象。</p>
|
||
|
||
<p>我们看一下创建对象过程:</p>
|
||
|
||
<pre><code class="language-java">DataInputStream dataInputStream = new DataInputStream(new FileInputStream("C:/text.txt"));
|
||
|
||
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("C:/text.txt"));
|
||
|
||
|
||
|
||
</code></pre>
|
||
|
||
<p>这个写法与上面 Demo 中的</p>
|
||
|
||
<pre><code>decorateTeacher = GlassesDecorator(WhiteShirtDecorator(LeatherShoesDecorator(Teacher("wells", "教授"))))
|
||
|
||
|
||
|
||
</code></pre>
|
||
|
||
<p>是不是很相似?都是一个对象套一个对象的方式进行创建。</p>
|
||
|
||
<p>还有一个场景,如果你对图形图像处理有一定了解,就会知道对图像的处理其实就是对一个二维坐标像素数据的处理,如图像的灰度化、梯度化(锐化)、边缘化、二值化。这些操作顺序不同就会造成不同的效果,也是适合用装饰模式来进行封装。</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/白话设计模式 28 讲(完)/07 代理模式:帮我拿一下快递.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/白话设计模式 28 讲(完)/09 工厂模式:你要拿铁还是摩卡.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":"70997b8c09a03cfa","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>
|
||
|