设计模式,Let's “Go”! (上)
code[class*="language-"],
pre[class*="language-"] {
background-color: #fdfdfd;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin-bottom: 1em;
}
:not(pre) > code[class*="language-"] {
position: relative;
padding: .2em;
-webkit-border-radius: 0.3em;
-moz-border-radius: 0.3em;
-ms-border-radius: 0.3em;
-o-border-radius: 0.3em;
border-radius: 0.3em;
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;
-webkit-box-shadow: 0px 13px 8px #979797;
-moz-box-shadow: 0px 13px 8px #979797;
box-shadow: 0px 13px 8px #979797;
-webkit-transform: rotate(-2deg);
-moz-transform: rotate(-2deg);
-ms-transform: rotate(-2deg);
-o-transform: rotate(-2deg);
transform: rotate(-2deg);
}
:not(pre) > code[class*="language-"]:after,
pre[class*="language-"]:after {
right: 0.75em;
left: auto;
-webkit-transform: rotate(2deg);
-moz-transform: rotate(2deg);
-ms-transform: rotate(2deg);
-o-transform: rotate(2deg);
transform: rotate(2deg);
}
.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;
}
@media screen and (max-width: 767px) {
pre[class*="language-"]:before,
pre[class*="language-"]:after {
bottom: 14px;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
.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;
pointer-events: none;
top: 0;
font-size: 100%;
left: -3.8em;
width: 3em; /* works for line-numbers below 1000 lines */
letter-spacing: -1px;
border-right: 1px solid #999;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.line-numbers-rows > span {
pointer-events: none;
display: block;
counter-increment: linenumber;
}
.line-numbers-rows > span:before {
content: counter(linenumber);
color: #999;
display: block;
padding-right: 0.8em;
text-align: right;
}
-->
前言
最近读了《Head First 设计模式》,每天早上看一章,学习一个设计模式,做些笔记,然后晚上抽空用刚学习的 Go 语言实现一下。半个月下来书读完了,留下了一些笔记,写博客总结一下。
书中的例子都是 Java 写的,但几乎没使用 Java 语言的特性,很容易看懂。对于我来说,就是一个 PHP 开发工程师,读了一本 用 Java 语言实现设计模式的书,然后用 Go 写了一遍。。。=_=
本文为每个设计模式只分配了一小节,总结一下设计模式的概念和特点以及适用场景,并介绍了自己用代码实现时的列举的例子,用现实生活中的事物对比加深一下记忆 ( 代码实现中所有的例子都是自己原创的,有牵强的部分,不要介意)。每个模式篇幅不大,可以给新人入门,给了解过设计模式的人作为速查。
文章经常被人爬,而且还不注明原地址,我在这里的更新和纠错没法同步,这里注明一下原文地址:http://www.cnblogs.com/zhenbianshu/p/7406572.html 以防误人子弟。
放上 Go 实现设计模式的 源码地址:DesignPattern-枕边书-Github ,偶有更新,欢迎 star。OK,正文开始。
策略模式(Strategy)
介绍
策略模式: 将算法或操作抽象成实现共同接口、可以被替换的类,实现逻辑和具体算法的解耦。
- 将各种行为
抽象成算法,封装算法为对象; - 算法实现
共同接口,调用者调用时不考虑算法具体实现,调用接口方法即可; - 调用者可随时替换此算法对象;
场景
- 多个方法择一使用,且他们会被随时替换;
- 方法没有共性,使用继承会有大量重写,使用接口会有大量重复使用;
实现
- 两个算法: 冒泡排序和快速排序;
- 抽象冒泡排序和快速排序为算法对象,实现算法接口,拥有 used() 被使用方法;
- 计算器计算时不用理会是什么算法,调用 used() 即可;
观察者模式(Observer)
介绍
观察者模式:主题主动向观察者推送变化,解决观察者对主题对象的依赖。
- 观察者实现
被通知接口,并在主题上注册,主题只保存观察者的引用,不关心观察者的实现; - 在主题有变化时调用观察者的通知接口来通知已注册的观察者;
- 通知方式有
推(主题变化时将变化数据推送给观察者)和拉(主题只告知变化,观察者主动来拉取数据);
场景
- 一个主题,多个观察者,主题的任何变动,观察者都要第一时刻得到;
- 观察者获取主题变化困难,定时不及时,轮询消耗大;
- 观察者可以随时停止关注某主题;
实现
- 张三和李四是记者,他们需要及时了解城市发生的新闻;
- 张三和李四在电视台注册了自己的信息;
- 城市发生了新闻,电视台遍历注册信息,通知了张三和李四;
- 李四退休了,在电视台注销了自己的信息;
- 城市又发生了新闻,电视台只通知了张三;
装饰者模式(Decorator)
介绍
装饰者模式:包装一个对象,在被装饰对象的基础上添加功能;
- 装饰者与被装饰对象拥有同一个超类,
装饰者拥有被装饰对象的所有外部接口,可被调用,外界无法感知调用的是装饰者还是被装饰者; - 装饰者需要被装饰者作为参数传入,并在装饰者内部,在
被装饰者实现的基础上添加或修改某些功能后,提供同被装饰者一样的接口; - 装饰者也可被另一个装饰者装饰,即嵌套装饰;
- 装饰者是一群包装类,由于装饰的复杂性,会多出很多个装饰者小类;
场景
- 对象需要动态地添加和修改功能;
- 功能改变后不影响原对象的使用;
实现
- 在商店内,花作为被装饰者对象、红丝带和盒子作为花的装饰者;
- 花、红丝带、盒子有共同的超类“商品”,他们都能被卖掉;
- 我们可以在红丝带装饰过花后,再用盒子再包装一次;
- 包装后的花,顾客买时也不会受到任何影响;
工厂模式(Factory)
介绍
工厂模式: 顾名思义,工厂模式是对象的生产器,解耦用户对具体对象的依赖。
- 实现
依赖倒置,让用户通过一个产品工厂
依赖产品的抽象,而不是一个具体的产品; - 简单工厂模式:接收参数并根据参数创建对应类,将对象的实例化和具体使用解耦;
- 抽象工厂模式:将工厂
抽象出多个生产接口,不同类型的工厂调用生产接口时,生产不同类型的对象; - 简单工厂常配合抽象工厂一起使用;
场景
- 根据不同条件需求不同的对象;
- 对象实例化的代码经常需要修改;
实现
- 简单工厂:向鞋厂内传入不同的类型(布制),鞋厂会生产出不同类型的鞋子(布鞋);
- 抽象工厂:有两座鞋厂:李宁鞋厂、Adidas鞋厂,他们能生产对应各自品牌的鞋子;
- 搭配使用:向不同的抽象工厂(李宁)传入不同的类型(运动类型),会生产出对应品牌对应类型的鞋子(李宁运动鞋);
单例模式(Singleton)
介绍
单例模式:保证同一个类全局只有一个实例对象;
- 在第一次实例化后会使用
静态变量保存实例,后续全局使用此静态变量; - 一般将构造方法私有化,构造方法添加 final 关键字无法被重写,添加一个类静态方法用于返回此实例;
- 在多线程时应该考虑
并发问题,防止两次调用都被判定为实例未初始化而重复初始化对象;
场景
- 全局共享同一个实例对象(数据库连接等);
- 某一处对此对象的更新全局可见;
实现
- 利用 Go 中包的可见性规则来隐藏对象的实例化权限;
- 使用包变量保存实例对象,获取实例时判断是否已实例化,如为nil,实例化对象并返回,如有值,直接返回值;
- 待用锁实现 Go routine 并发时的问题;
命令模式(Command)
介绍
命令模式:将一个命令封装成对象,解耦命令的发起者和执行者。
- 命令对象实现
命令接口(excute[、undo]),命令发起者实例化命令对象,并传递此对象,并不关心此对象由谁执行; - 命令执行者只负责调用命令对象的执行方法即可,不关心对象是由谁生成的;
- 与策略模式不同之处:策略模式是通过不同的算法做同一件事情(例如排序),而命令模式则是通过不同的命令做不同的事情;
场景
- 命令发起者与执行者无法直接接触;
- 命令需要撤销功能,却不易保存命令执行状态信息时;
实现
- 指挥官创建了一个“从树下跑到草地上”的命令;
- 命令被分配给张三执行,张三作为军人,接到命令后不管命令的具体内容,而是直接调用命令的执行接口执行;
- 指挥官发布了撤销指令,张三又从草地上跑到了树下;
适配器模式(Adapter)
介绍
适配器模式:包装对象提供一个接口,以适配调用者。
- 适配器通过一个中间对象,
封装目标接口以适应调用者调用; - 调用者调用此适配器,以达到调用目标接口的目的;
- 适配器模式与装饰者模式的不同之处:适配器模式不改变接口的功能,而装饰者会添加或修改原接口功能;
场景
- 提供的接口与调用者调用的其他的接口都不一致;
- 为一个特殊接口修改调用者的调用方式得不偿失;
实现
- 张三是个正常人,他能通过说话直接地表达自己;
- 李四是个聋哑人,他没法直接表达自己,但他会写字;
- 笔记本作为一个适配器,用笔记本“包装”了李四之后,当李四需要表达自己想法时,调用笔记本的“表达”功能,笔记本再调用李四“写字”的方法;
外观模式(Facade)
介绍
外观模式:通过封装多个复杂的接口来提供一个简化接口来实现一个复杂功能。
- 外观模式是通过封装多个接口来将接口
简单化; - 外观模式不会改变原有的多个复杂的单一接口,这些接口依然能被单独调用,只是提供了一个额外的接口;
- 外观模式与适配器模式的不同之处:外观模式是
整合多个接口并添加一个简化接口,适配器是适配一个接口;
场景
- 实现某一功能需要调用多个复杂接口;
- 经常需要实现此功能;
实现
- 正常的冲咖啡步骤是:磨咖啡豆、烧开水、倒开水搅拌咖啡。
- 我们经常需要直接冲咖啡,而不是使用单一步骤,每次喝咖啡时调用三个方法很麻烦;
- 封装三个接口,额外提供一个 “冲咖啡” 的方法,需要喝咖啡时只需要调用一次冲咖啡方法即可;
小结
《Head First 设计模式》这书真心不错,例子很轻松,给人很多时间和空间来思考,同时介绍模式时使用结合故事,层层深入的方法,让人印象很深刻,推荐。
书中详细介绍了 14 个基础设计模式,还有 9 个简化版,就自己查资料结合自己的理解来总结了。
本系统准备写三篇,敬请期待中下篇。
关于本文有什么问题可以在下面留言交流,如果您觉得本文对您有帮助,可以点击下面的 推荐 支持一下我。博客一直在更新,欢迎 关注 。
设计模式,Let's “Go”! (上)的更多相关文章
- Java 设计模式学习总结(上)
在编写和维护公司的现有代码的时候,我经常思考的问题是:怎样的系统算一个好的系统?如何设计具有扩展.可维护.复用的系统,它能最大限度的应对产品经理不断变化的需求.答案似乎是:设计模式. Remember ...
- Java与设计模式之单例模式(上)六种实现方式
阎宏博士在<JAVA与模式>中是这样描述单例模式的:作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. ...
- 12天,这本《重学Java设计模式》PDF书籍下载量9k,新增粉丝1400人,Github上全球推荐榜!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言
- 程序设计模式浅析(plain framework商业版设计模式)
程序设计其实对程序开发者来说十分重要,但是在工作中往往我们却忽略了这一块,因为我们所用的都是现有的模式.一个设计模式的好坏,往往能够体现出程序的专业性,还有整个项目的可持续性.这就是为什么有些公司,在 ...
- 写给Python初学者的设计模式入门
有没有想过设计模式到底是什么?通过本文可以看到设计模式为什么这么重要,通过几个Python的示例展示为什么需要设计模式,以及如何使用. 设计模式是什么? 设计模式是经过总结.优化的,对我们经常会碰到的 ...
- Java设计模式学习资源汇总
本文记录了Java设计模式学习书籍.教程资源.此分享会持续更新: 1. 设计模式书籍 在豆瓣上搜索了一把,发现设计模式贯穿了人类生活的方方面面.还是回到Java与程序设计来吧. 打算先归类,再浏览,从 ...
- 二十三种设计模式及其python实现
本文源码寄方于github:https://github.com/w392807287/Design_pattern_of_python 参考文献: <大话设计模式>——吴强 <Py ...
- ASP.NET 设计模式:设计模式和原则简述
设计模式的概念 设计模式是高层次的.抽象的解决方案模板.可以将这些模式视为解决方案的蓝本而不是解决方案本身.通常是通过重构自己的代码并将问题泛化来实现设计模式. 软件设计中常见的模式大体分为三类: 创 ...
- Java设计模式-责任链模式
提出问题: 最初接触责任链模式就是在struts2中,在当时学的时候看了一眼,大概知道了原理,最近在复习,模拟struts2,说是模拟只是大体模拟了struts2的工作流程,很多东西都是写死的,只是为 ...
- Python的设计模式
设计模式是什么? 设计模式是经过总结.优化的,对我们经常会碰到的一些编程问题的可重用解决方案.一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码.反之,设计模式更为高级,它是一种必须在特定情 ...
随机推荐
- canvas 2.0 图片绘制
绘制图片drawImage 2013.02.21 by 十年灯·一条评论 本文属于<html5 Canvas画图系列教程> 这里的绘制图片是指把一张现成的图片,绘制到Canvas上面. 有 ...
- centos实现永久修改hostname
前言 介绍一下centos的两种修改hostname的方式. 查看hostname [root@slave02 ~]# hostname slave02 临时性修改 [root@slave02 ~]# ...
- ABAP-表中数据的横向累加
GLT0 等表中数据的横向累加 ——塞依SAP培训 在 ABAP 的 GLT0.FAGLFLEXT 等余额表中,用诸如 TSL01.TSL02 …… TSL16 等字段记录了 16 个期间的发生额 ...
- TensorFlow Android Camera Demo 使用android studio编译安装和解决Execution failed for task ':buildNativeBazel'报错
可以参考官网:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/android#android-stud ...
- springboot整合zookeeper
在springboot中所有的整合都是以bean的形式注入对象,从数据库coon.redis conn.再到整合的zookeeper,依然是依照bean注入连接对象,通过zookeeper api对z ...
- cdnbest独立主控用户如何开通日志分析
1.cdn独立主控用户开通日志分析,先用授权的帐号在官网平台登陆,然后购买日志套餐 2.在自已的主控平台还要做两步操作: 1.增加个日志套餐,内容随便,因为设置是无效的,只是需要一个套餐 2. 给用户 ...
- window.innerWidth和document.body.clientWidth的区别
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- mysql数据库字段内容替换
UPDATE 表名 SET 字段名= replace(字段名, '查找内容', '替换成内容') ; UPDATE car_articles SET article_title = replace(a ...
- FortiGate上架前准备
1.收集信息 1.网络拓扑信息(了解网络拓扑信息有助于网络方案的规划) 2.环境信息(了解部署位置.部署模式.最大吞吐.最大用户数有助于对设备性能的评估) 3.客户需求,对FortiGate部署的功能 ...
- 存储过程传入datatable
存储过程传入一般的参数都很简单,今天要说一下存储过程传入datatable 类型 首先要自定义一个 表类型 CREATE TYPE [dbo].[servicedatableType] AS TABL ...