* { color: #3e3e3e }
body { font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif; font-size: 15px }
p { line-height: 25.6px; text-align: justify; margin: 23.7px 0 }
blockquote { border-left: 2px solid rgba(128,128,128,0.075); background-color: rgba(128,128,128,0.05); padding: 10px -1px; margin: 0px }
blockquote p { color: #898989; margin: 0px }
strong { font-weight: 700; color: #3e3e3e }
pre { background-color: #f8f8f8; overflow: scroll; padding: 12px 13px; font-size: 13px; color: #898989 }
h1,h2,h3,h4,h5,h6 { text-align: left; font-weight: bold }
hr { border: 1px solid #ddd }
h1 { font-size: 170% }
h1:first-child { margin-top: 0; padding-top: .25em; border-top: none }
table { padding: 0; border-collapse: collapse }
table tr { border-top: 1px solid #cccccc; background-color: white; margin: 0; padding: 0 }
table tr:nth-child(2n) { background-color: #f8f8f8 }
table tr th { font-weight: bold; border: 1px solid #cccccc; margin: 0; padding: 6px 13px }
table tr td { border: 1px solid #cccccc; margin: 0; padding: 6px 13px }
table tr th :first-child,table tr td :first-child { margin-top: 0 }
table tr th :last-child,table tr td :last-child { margin-bottom: 0 }
a { color: #4183C4; text-decoration: none }
ul,ol { padding-left: 30px }
li { line-height: 24px }
hr { height: 2px; padding: 0; margin: 16px 0; background-color: #e7e7e7; border: 0 none; overflow: hidden; border-bottom: 1px solid #ddd }
pre { background: #f2f2f2; padding: 12px 13px }
code { padding: 2px 4px; color: #c7254e; background: #f9f2f4 }
code[class*="language-"],pre[class*="language-"] { color: black; background: none; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; text-align: left; white-space: pre; word-spacing: normal; line-height: 1.5 }
pre[class*="language-"] { position: relative; margin: .5em 0; border-left: 10px solid #358ccb; background-color: #fdfdfd; background-image: linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%); overflow: visible; padding: 0 }
code[class*="language"] { max-height: inherit; height: 100%; padding: 0 1em; display: block; overflow: auto }
:not(pre)>code[class*="language-"],pre[class*="language-"] { background-color: #fdfdfd; margin-bottom: 1em }
:not(pre)>code[class*="language-"] { position: relative; padding: .2em; color: #c92c2c; border: 1px solid rgba(0, 0, 0, 0.1); display: inline; white-space: normal }
pre[class*="language-"]::before,pre[class*="language-"]::after { content: ""; z-index: -2; display: block; position: absolute; bottom: 0.75em; left: 0.18em; width: 40%; height: 20%; max-height: 13em }
:not(pre)>code[class*="language-"]::after,pre[class*="language-"]::after { right: 0.75em; left: auto }
.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata { color: #7D8B99 }
.token.punctuation { color: #5F6364 }
.token.property,.token.tag,.token.boolean,.token.number,.token.function-name,.token.constant,.token.symbol,.token.deleted { color: #c92c2c }
.token.selector,.token.attr-name,.token.string,.token.char,.token.function,.token.builtin,.token.inserted { color: #2f9c0a }
.token.operator,.token.entity,.token.url,.token.variable { color: #a67f59; background: rgba(255, 255, 255, 0.5) }
.token.atrule,.token.attr-value,.token.keyword,.token.class-name { color: #1990b8 }
.token.regex,.token.important { color: #e90 }
.language-css .token.string,.style .token.string { color: #a67f59; background: rgba(255, 255, 255, 0.5) }
.token.important { font-weight: normal }
.token.bold { font-weight: bold }
.token.italic { font-style: italic }
.token.entity { cursor: help }
.namespace { opacity: .7 }
.token.tab:not(:empty)::before,.token.cr::before,.token.lf::before { color: #e0d7d1 }
pre[class*="language-"].line-numbers { padding-left: 0 }
pre[class*="language-"].line-numbers code { padding-left: 3.8em }
pre[class*="language-"].line-numbers .line-numbers-rows { left: 0 }
pre[class*="language-"][data-line] { padding-top: 0; padding-bottom: 0; padding-left: 0 }
pre[data-line] code { position: relative; padding-left: 4em }
pre .line-highlight { margin-top: 0 }
pre.line-numbers { position: relative; padding-left: 3.8em; counter-reset: linenumber }
pre.line-numbers>code { position: relative }
.line-numbers .line-numbers-rows { position: absolute; top: 0; font-size: 100%; left: -3.8em; width: 3em; letter-spacing: -1px; border-right: 1px solid #999 }
.line-numbers-rows>span { display: block; counter-increment: linenumber }
.line-numbers-rows>span::before { content: counter(linenumber); color: #999; display: block; padding-right: 0.8em; text-align: right }
前言
接上篇 设计模式,Let‘s “Go”! (上), 继续更新设计模式,今天介绍的设计模式有模板模式、迭代器模式、组合模式、状态模式、代理模式、桥接模式和建造者模式;
文章对设计模式的特点和使用场景进行了总结,每个设计模式分配的篇幅较少,给了解过设计模式的作为速查,帮不了解设计模式的入门, 当然具体实现才是重点,使用现实生活中的事物例子来帮助理解设计模式。
放上 Go 实现设计模式的源码地址:DesignPattern-枕边书-Github ,偶有更新,欢迎 star
。
文章经常被人爬,而且还不注明原地址,我在这里的更新和纠错没法同步,这里注明一下原文地址:http://www.cnblogs.com/zhenbianshu/p/7449868.html 以防误人子弟。
模板模式(Template)
介绍
模板模式:模板模式在抽象类或父类
中抽象出算法步骤
作为模板,模板的具体细节推迟到子类实现。
- 模板模式在父类或抽象类中定义一个
算法的骨架
,并在父类或抽象类中实现共同的部分,各个不同的步骤由不同的子类分别实现; - 模板板式在父类的算法步骤中定义
勾子(hook)
,在子类中判断并定义一些不是非通用步骤; - 模板模式与策略模式的不同之处在于,策略模式是针对多个不同的算法,而模板模式是针对一个算法的不同步骤,在模板模式中,只有一个算法;
场景
- 多个算法有多个共同之处,但某些步骤略微不同;
- 各子类步骤顺序一致,但步骤的具体实现有所不同时;
实现
- 有发邮件和发短信两种通讯方式;
- 他们都需要获取目标信息、格式化正文、填写发送方信息,但实现不同;
- 在信息类中抽象出三个步骤,具体的处理方式由两种通讯方式各自实现;
- 发送信息时调用信息类中的发送方法,发送方法会按照顺序自动调用对应的步骤;
迭代器模式(Iterator)
介绍
迭代器模式:迭代器模式允许调用者在不知道类内部实现
的情况下遍历类元素
。
- 迭代器接口常用方法有
length(),next(),previous(),remove()
等; - 各类在内部实现迭代器接口,用对应的方法操作元素;
- 调用者不考虑类内部实现,调用迭代器接口即可;
场景
- 类使用不同的数据结构存储数据;
- 需要对不同的数据类型进行遍历等操作;
实现
- 使用 slice 存储一列战马,使用 map 存储一列士兵;
- 战马和士兵结构都实现了迭代器接口;
- 获取战马数和士兵数,遍历战马和士兵,调用迭代器接口即可;
组合模式(Composite)
介绍
组合器模式:使用一种组件抽象
来同时表达集合与元素
,使用统一的接口来管理集合和元素。
- 组合模式通常为
树结构
,父结点和子节点具有同样的抽象和接口; - 在操作集合时,会
同时操作集合所属的具体元素
; - 通常给组合模式添加一个迭代器来完成组合结构的迭代;
场景
- 管理的多个对象构成树型层级结构;
- 操作高层级的对象时,需要同时其所属的下级对象,如界面窗口等;
实现
- 将军、队长、士兵构成树型层级结构,且他们都是战士,拥有战斗方法;
- 每位战士都保存着自己的下级名单,没有下级时忽略;
- 每个人在战斗时,都会率领着下级战斗;
状态模式(State)
介绍
状态模式:状态模式抽象出一个事物的状态
作为类,解耦事物和不同状态下的行为;
- 状态模式通过替换状态对象作为状态转换的方式;
- 状态对象实现根据状态动作的接口,可以根据不同的
动作做出对应
的反应; - 状态模式与策略模式的实现相似,但状态模式是对类内部状态作出改变,而策略模式是针对算法封装;
场景
- 事物有多种状态,且可以相互转换;
- 事物多种状态下对同一动作做出的行为不同;
实现
- 植物有 幼苗、开花和成熟 三种状态,且它们会通过浇水和收获的动作进行相互转换;
- 幼苗和开花时不能收获,只能浇水,成熟状态只能收获,不需要再浇水;
- 定义三种状态,和它们对不同动作时的行为,植物通过三种对象的替换来进行状态转换;
代理模式(Proxy)
介绍
代理模式:给对象提供一个代理,由代理对象控制对原对象的调用;
- 代理模式为一个对象(通常是大对象或无法复制的对象)
创建另外一个类作为其访问的接口
,所有对真实对象的请求都通过代理对象
完成; - 代理对象可以
控制用户对真实对象的访问权限
,也可以在访问真实对象时附加功能; - 代理模式可被用作:远程代理,虚拟代理,安全代理,指针引用,延迟加载;
场景
- 对象无法被直接访问时;
- 对象过大,初始化较慢;
- 对象不必要立刻初始化,可使用默认值代替;
实现
- 小明给暗恋对象写了一封信,在等回信;
- 邮递员是个非常忙的人,来不及去收取回信;
- 小明好声好气向邮递员要回信时,邮递员都推拖说自己要去取;
- 小明发怒了,邮递员终于抽时间去取了信给小明;
- 此信中邮递员就是代理模式中的代理,他实现了懒加载。
- 回信内容见源码:)
桥接模式(Bridge)
介绍
桥接模式:将事务的多个维度
都抽象出来以解耦抽象与实际
之间的绑定关系,使抽象和实际向着不同维度改变;
- 桥接模式通过对象的组合来解决事物的多维度变化问题,以替代多继承的不灵活;
- 桥接模式可以轻易在多维度上拓展,而不改变原有模式;
- 桥接模式与策略模式的不同之处:策略模式是针对一个不变的主题替换抽象算法,而桥接模式是策略模式的高维度状态,它的主题也可能会被替换;
场景
- 某事物在多个维度上都有变化;
- 无法使用多继承或使用多继承会很不灵活;
实现
- 作画时可以使用铅笔和圆珠笔等不同的笔,也可以在宣纸或普通A4纸;
- 抽象出笔和纸两种对象;
- 自由组合笔和纸进行作画;
建造者模式(Builder)
介绍
建造者模式:建造者模式分离创建复杂对象的过程和细节
,使得同样的创建过程能创建不同的对象。
- 建造者模式将创建对象部件的
一般过程抽象出接口
,而由不同的建造者类实现具体的接口,实现过程的步骤; - 通过建造者,调用者不用考虑对象创建过程的细节,且建造者也可以被灵活替换;
- 与模板模式的区别:建造者模式使用类的组合进行对象的创建,而模板模式使用类的继承实现对象的具体构造;
- 与工厂模式的区别:工厂模式会返回一个具体类,而建造者模式会建造出一个由多个类组装而成的完整类;
场景
- 对象的创建包含其他对象为类元素,创建过程复杂;
- 多个复杂对象的创建过程具有高度相似性;
实现
- 中国式建筑有金色屋顶和红色大门,而意式建筑有圆项和白色大门;
- 中国建筑师和意式建筑师分别擅长建造不同类型的建筑;
- 我们在盖不同类型的房子时先创建一个建筑师,再用建筑师去创建对应风格的房子;
小结
开发者对设计模式常陷入两个误区,要么极度推崇,要么弃如弊履。 极度推崇的人认为设计模式就是圣经,是程序设计的最高实现,以至于写代码时就往设计模式上凑,最终写出的代码冗余沉重,让人很难懂;而鄙弃设计模式的人实用至上,认为写出的代码能实现业务功能就好,对设计模式理也不理,结果代码杂乱不堪,稍有改动就引出一大堆 BUG,跟别人解释实现时,别人听得云里雾里。
我认为设计模式就是一些定义,就像冒泡排序、快速排序这些名字一样,便于开发者之间的交流,特别是在代码中,如果你提到使用了XX模式,如果阅读你代码的人也了解这个设计模式,那么他了解你的实现也就更加简单了。像之前在不了解设计模式的概念时,我就已经在很多地方应用模板模式和策略模式了,但在向别人介绍代码实现时,我需要说一堆代码设计,别人还不一定能理解,如果以后再跟人交流时,我不需要解释很多,只说我实现了模板模式就OK了。而且学会并深入了解了设计模式,那么以后遇到适用设计模式的场景,就可以不用苦逼地一遍遍修改代码,而直接朝着最美的方向设计了。
所以深入学习设计模式也是学习程序设计中很多问题的普遍解决方式,也是学习程序员之前交流的专业词汇,意义还是挺重大的。
关于本文有什么问题可以在下面留言交流,如果您觉得本文对您有帮助,可以点击下面的 推荐 支持一下我。博客一直在更新,欢迎 关注 。
设计模式,Let's “Go”! (中)