每周分享第1期-常用表情包收录

每周分享,分享看到的一些有意思的文章和项目 1 中国表情包大集合 https://zhaoolee.github.io/ChineseBQB/ 表情包目录(共收录2298张表情包)Emoticon package directory (commonly included 2298 emoticon pack) 2 谷歌产品替代品 一批谷歌产品的替代方案 https://nomoregoogle.com/ 3 Redis作者:开源维护者的挣扎和无奈 英文作者是著名开源项目 Redis 的开发者 antirez。截至 2019-06-04 为止,Redis 项目在 GitHub 将近有 37,000 Star,Fork 数达 14000。 https://mp.weixin.qq.com/s/6C7-4Fp46rxfn0J34ebKyg 4 一个分享 GitHub 上 有趣、入门级的开源项目网站 https://hellogithub.com 5 Saber - 新一代静态网站生成系统 https://saber.land 6 star-history: 帮助用户查看 github 项目 star 数目的历史 star-history 帮助用户查看 github 项目 star 数目的历史, 判断项目发展情况,项目地址:https://github.com/timqian/star-history 7 一个分类收集 GitHub 开源项目的网站,并对项目的热度和活跃度进行分析 项目地址:https://www.ctolib.com/ 8 git 提交信息规范检测工具 git-commit-msg-linter 项目地址:https://www.npmjs.com/package/git-commit-msg-linter 9 编码一时爽,重写火葬场?这些公司都重写了软件,结局却不同。 生存,还是死亡,这是一个问题。重写,还是不重写,这是导致生存或死亡的另一个问题。 https://mp.weixin.qq.com/s/SqxPoIDPuvKlrlqZmubpXA ...

2019-06-07 · 1 min · 78 words

为什么json 不能使用 int64类型

json 简介 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。 JSON支持两种数据结构存在: 对象(object):一个对象包含一系列非排序的名称/值对(pair),一个对象以{开始,并以}结束。每个名称/值对之间使用 : 分割。 数组 (array):一个数组是一个值(value)的集合,一个数组以 [ 开始,并以]结束。数组成员之间使用 , 分割。 具体的格式如下: [value1, value2, value3] 名称/值(pair):名称和值之间使用 : 隔开,格式如下: {name:value} 名称必须是字符串类型; 值(value)必须是可以是字符串(string),数值(number),对象(object),有序列表(array),或者 false, null, true 的其中一种。 JSON的格式描述可以参考RFC 4627。 为什么JSON不支持 int64 类型? 通过上面的介绍有两个关键点: JSON 是基于 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集 JSON 支持number 类型 Javascript的数字存储使用了IEEE 754中规定的双精度浮点数数据类型,而这一数据类型能够安全存储 -(2^53-1) 到 2^53-1 之间的数值(包含边界值)。JSON 是Javascript 的一个子集,所以它也遵守这个规则。 ...

2019-06-03 · 1 min · 191 words

PostgreSQL jsonb 使用入门

json 类型 说明 根据RFC 7159中的说明,JSON 数据类型是用来存储 JSON(JavaScript Object Notation)数据的。这种数据也可以被存储为text,但是 JSON 数据类型的优势在于能强制要求每个被存储的值符合 JSON 规则。也有很多 JSON 相关的函数和操作符可以用于存储在这些数据类型中的数据 PostgreSQL支持两种 JSON 数据类型:json 和 jsonb。它们几乎接受完全相同的值集合作为输入。两者最大的区别是效率。json数据类型存储输入文本的精准拷贝,处理函数必须在每 次执行时必须重新解析该数据。而jsonb数据被存储在一种分解好的二进制格式中,因为需要做附加的转换,它在输入时要稍慢一些。但是 jsonb在处理时要快很多,因为不需要重新解析。 重点:jsonb支持索引 由于json类型存储的是输入文本的准确拷贝,存储时会空格和JSON 对象内部的键的顺序。如果一个值中的 JSON 对象包含同一个键超过一次,所有的键/值对都会被保留(** 处理函数会把最后的值当作有效值**)。 jsonb不保留空格、不保留对象键的顺序并且不保留重复的对象键。如果在输入中指定了重复的键,只有最后一个值会被保留。 推荐把JSON 数据存储为jsonb 在把文本 JSON 输入转换成jsonb时,JSON的基本类型(RFC 7159 )会被映射到原生的 PostgreSQL类型。因此,jsonb数据有一些次要额外约束。 比如:jsonb将拒绝除 PostgreSQL numeric数据类型范围之外的数字,而json则不会。 JSON 基本类型和相应的PostgreSQL类型 JSON 基本类型 PostgreSQL类型 注释 string text 不允许\u0000,如果数据库编码不是 UTF8,非 ASCII Unicode 转义也是这样 number numeric 不允许NaN 和 infinity值 boolean boolean 只接受小写true和false拼写 null (无) SQL NULL是一个不同的概念 json 输入输出语法 -- 简单标量/基本值 -- 基本值可以是数字、带引号的字符串、true、false或者null SELECT '5'::json; -- 有零个或者更多元素的数组(元素不需要为同一类型) SELECT '[1, 2, "foo", null]'::json; -- 包含键值对的对象 -- 注意对象键必须总是带引号的字符串 SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json; -- 数组和对象可以被任意嵌套 SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json; -- "->" 通过键获得 JSON 对象域 结果为json对象 select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->'nickname' as nickname; nickname ------------- "goodspeed" -- "->>" 通过键获得 JSON 对象域 结果为text select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->>'nickname' as nickname; nickname ----------- goodspeed -- "->" 通过键获得 JSON 对象域 结果为json对象 select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->'nickname' as nickname; nickname ------------- "goodspeed" -- "->>" 通过键获得 JSON 对象域 结果为text select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->>'nickname' as nickname; nickname ----------- goodspeed 当一个 JSON 值被输入并且接着不做任何附加处理就输出时, json会输出和输入完全相同的文本,而jsonb 则不会保留语义上没有意义的细节 ...

2019-05-30 · 10 min · 1984 words

创建高质量的代码--软件构建中的设计

《代码大全》读书笔记 太长不看版 软件构建中的设计 软件设计是一项明确的活动 设计中的挑战 软件设计一词意味着去构思、创造或发明一套方案,把一份计算机软件的规格说明书要求转变为可实际运行的软件。 设计就是把需求分析和编码调试连接在一起的活动。 好的高层词设计能够提供一个可以稳妥容纳多个较低层次设计的结构。 设计是一个险恶的问题 险恶(wicked)的问题就是那种只有通过解决或部分解决才能被明确的问题。 Tacoma Narrows 大桥是一个险恶问题的好例子,因为直到这座桥坍塌,工程师才知道不应该只考虑桥的负荷,还需要充分的考虑空气动力学因素(只有建造大桥,才能从中学到需要考虑额外的环节)。 设计是一个了无章法的过程(即使它能得处清爽的成果) 是因为在设计的过程中可能会采用很多错误的步骤,多次出错 因为设计的优劣差异往往非常微妙 因为不能判断设计是否足够好 设计就是确定取舍和调整顺序的过程 现实世界中,设计者工作的一个关键内容就是衡量彼此冲突的各项设计特性,并尽力在其中寻求平衡。响应速度优先和开发时间短优先得出的设计结果可能是不同的。 设计受到诸多限制 设计的要点一部分是在创造可能发生的事情,另一部分是在限制可能发生的事情。 如果一个人有无限空间和资源来建造房子,可能会建造出无法控制的建筑。正是因为有了限制,才得出了简单的结果。软件设计也是一样。 设计是不确定的 每个人设计的结果可能是不同的,并且可能用起来都不错。设计没有标准答案。 设计是一个启发式的过程 设计过程中充满了不确定性,因此设计技术也趋于具有探索性–“经验法则”或者“试试没准能行”–而不是保证能产生预期结果的可重复的过程。 设计是自然而然形成的 设计不是在谁的头脑中直接跳出来的,它是在不断的设计评估、非正式讨论、写试经验以及修改试验代码中演化和完善的。 关键的设计概念 软件的首要技术使命:管理复杂度 本质的难题和偶然的难题 偶然的难题可以理解为bug,编程语言笨拙的语法,等易于发现容易解决的问题。 本质的难题则比较复杂,本质上说,软件开发就是不断去发掘错综复杂,相互关连的整套概念的所有细节。本质困难就是: 要面对复杂、无序的现实世界; 精确而完成的识别出各种依赖关系和外部情况 设计出完全正确而不是大致正确的解决方案 。。。 管理复杂度的重要性 一个失败的项目如果是由于技术原因而失败,通常都是因为软件复杂度失控了。如果复杂度失控,那么软件就会变得极端复杂,没有人知道它能做什么,它出了问题如何解决。 管理复杂度是软件开发中最为重要的技术话题。 在软件架构层次上,可以通过把大的系统分解为多个子系统来降低问题的复杂度,多个简单的问题比一个复杂的大问题更容易理解。 子系统相互间应该减少依赖; 子系统的关注点应该是相互分离的。 如何应对复杂度 高代价、低效率的设计源于下面三种根源: 用复杂的方法解决简单的问题 用简单但错误的方法解决复杂的问题 用不恰当的复杂的方法解决复杂的问题 用下面的方法管理复杂度 把任何人在同一时间需要处理的本质复杂度降到最低 不要让偶然性的复杂度无谓的增长 理想的设计特征 最小的复杂度 易于维护 松散耦合 可扩展性 可重用性 高扇入:让大量的类使用某个给定的类。(意味着设计出的系统很好的利用了在较低层次上的工具类 低扇出:让一个类少量或始终的使用其他类。(高扇出(7个)意味着一个类过多的使用了其他类,可能会变得过于复杂 可移植性 精简性:没有多余的部分 层次性:比如一个新系统会用到很多设计不佳的旧系统,这时就应该为新系统编写一个负责同就代码交互的层(代理模式) 层次性能把低劣的代码紧闭起来 如果能最终抛弃或重构旧代码,旧不必修改处交互层之外的任何新代码。 标准技术:用到的外来的、古怪的东西越多,也越难理解。 设计的层次 1. 软件系统(Software System) 2. 分解为子系统或包(Division into Subsystems or Packages) 这一层的主要目的是确定如何把程序分为主要的子系统,并定义清楚允许各子系统如何使用其他子系统。 ...

2019-05-29 · 2 min · 305 words

并发模型:线程与锁

并发&并行 并发程序含有多个逻辑上的独立执行块,他们可以独立的并行执行,也可以串行执行。 并行程序解决问题的速度比串行程序快的多,因为其可以同时执行整个任务的多个部分。并行程序可能有多个独立执行块,也可能只有一个。 引用Rob Pike的经典描述就是: 并发是同一时间应对多件事情的能力; 并行是同一时间动手做多件事情的能力。 常见的并发模型有: 线程与锁 函数式编程 actor模型和通信顺序是进行(Communicating Sequential Processes, CSP) 数据级并行 lambda 架构 分离标识与状态模型 这篇主要介绍线程与锁模型 线程与锁模型 线程与锁模型是对底层硬件运行过程的形式化,非常简单直接,几乎所有的编程语言都对其提供了支持,且不对其使用方法加以限制(易出错)。 这篇文章主要使用python语言来演示线程与锁模型。文章结构来自《七周七并发模型》 互斥和内存模型 创建线程 from threading import Thread def hello_world(): print("Hello from new thread") def main(): my_thread = Thread(target=hello_world) my_thread.start() print("Hello from main thread") my_thread.join() main() 这段代码创建并启动了一个Thread实例,首先从start() 开始,my_thread.start() main()函数的余下部分一起并发执行。最后调用join() 来等待my_thread线程结束。 运行这段代码输出结果有几种: Hello from new thread Hello from main thread 或者 Hello from main thread Hello from new thread 或者 ...

2019-05-19 · 6 min · 1178 words

JWT RefreshToken 实践

Json web token (JWT), 根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。 详细介绍可以查看这篇文章 理解JWT(JSON Web Token)认证及实践 JWT 特点 优点 体积小,因而传输速度快 传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输 严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。 支持跨域验证,可以应用于单点登录。 存在的问题 JWT 自身(在 payload 中)就包含了所有与用户相关的验证消息,所以通常情况下不需要保存。这种设计存在几个问题: Token不能撤销–客户端重置密码后之前的JWT依然可以使用(JWT 并没有过期或者失效 不支持refresh token,JWT过期后需要执行登录授权的完整流程 无法知道用户签发了几个JWT 针对第一个问题,可能的解决方法有: 保存JWT到数据库(或Redis),这样可以针对每个JWT单独校验 在重置密码等需要作废之前全部JWT时,把操作时间点记录到数据库(或Redis),校验JWT时同时判断此JWT创建之后有没有过重置密码等类似操作,如果有校验不通过 当然,这种解决方法都会多一次数据库请求,JWT自身可校验的优势会有所减少,同时也会影响认证效率。 这篇文章主要介绍解决第二个问题(不支持refresh token)的思路。 refresh token refresh token是OAuth2 认证中的一个概念,和OAuth2 的access token 一起生成,表示更新令牌,过期所需时间比access toen 要长,可以用来获取下一次的access token。 如果JWT 需要添加 refresh token支持,refresh token需要满足的条件有一下几项: 和JWT一起生成返回给客户端 有实效时间,有效时间比JWT要长 只能用来换取下一次JWT,不能用于访问认证 不能重复使用(可选) refresh token 获取流程 refresh token 使用流程 代码示例 import jwt import time # 使用 sanic 作为restful api 框架 def create_token(account_id, username): payload = { "iss": "gusibi.mobi", "iat": int(time.time()), "exp": int(time.time()) + 86400 * 7, "aud": "www.gusibi.mobi", "sub": account_id, "username": username, "scopes": ['open'] } token = jwt.encode(payload, 'secret', algorithm='HS256') payload['grant_type'] = "refresh" refresh_token = jwt.encode(payload, 'secret', algorithm='HS256') return True, { 'access_token': token, 'account_id': account_id, "refresh_token": refresh_token } # 验证refresh token 出否有效 def verify_refresh_token(token): payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256']) # 校验token 是否有效,以及是否是refresh token,验证通过后生成新的token 以及 refresh_token if payload and payload.get('grant_type') == 'refresh': # 如果需要标记此token 已经使用,需要借助redis 或者数据库(推荐redis) return True, payload return False, None # 验证token 是否有效 def verify_bearer_token(token): # 如果在生成token的时候使用了aud参数,那么校验的时候也需要添加此参数 payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256']) # 校验token 是否有效,以及不能是refresh token if payload and not payload.get('grant_type') == 'refresh': return True, payload return False, None 参考链接 理解JWT(JSON Web Token)认证及实践 理解OAuth 2.0[1] References [1] 理解OAuth 2.0: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html ...

2019-04-29 · 1 min · 204 words

Golang mod 入门

go modules 是 golang 1.11 新加的特性。现在1.12 已经发布了,是时候用起来了。Modules官方定义为: 模块是相关Go包的集合。modules是源代码交换和版本控制的单元。 go命令直接支持使用modules,包括记录和解析对其他模块的依赖性。modules替换旧的基于GOPATH的方法来指定在给定构建中使用哪些源文件。 如何使用 Modules ? 把 golang 升级到 1.11(现在1.12 已经发布了,建议使用1.12) 设置 GO111MODULE GO111MODULE GO111MODULE 有三个值:off, on和auto(默认值)。 GO111MODULE=off,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。 GO111MODULE=on,go命令行会使用modules,而一点也不会去GOPATH目录下查找。 GO111MODULE=auto,默认值,go命令行将会根据当前目录来决定是否启用module功能。这种情况下可以分为两种情形: 当前目录在GOPATH/src之外且该目录包含go.mod文件 当前文件在包含go.mod文件的目录下面。 当modules 功能启用时,依赖包的存放位置变更为$GOPATH/pkg,允许同一个package多个版本并存,且多个项目可以共享缓存的 module。 go mod golang 提供了 go mod命令来管理包。 go mod 有以下命令: 命令 说明 download download modules to local cache(下载依赖包) edit edit go.mod from tools or scripts(编辑go.mod graph print module requirement graph (打印模块依赖图) init initialize new module in current directory(在当前目录初始化mod) tidy add missing and remove unused modules(拉取缺少的模块,移除不用的模块) vendor make vendored copy of dependencies(将依赖复制到vendor下) verify verify dependencies have expected content (验证依赖是否正确) why explain why packages or modules are needed(解释为什么需要依赖) 如何在项目中使用 示例一:创建一个新项目 在GOPATH 目录之外新建一个目录,并使用go mod init 初始化生成go.mod 文件 ➜ ~ mkdir hello ➜ ~ cd hello ➜ hello go mod init hello go: creating new go.mod: module hello ➜ hello ls go.mod ➜ hello cat go.mod module hello go 1.12 go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。 ...

2019-03-16 · 4 min · 689 words

python设计模式-状态模式

问题:有一个糖果公司需要设计一个糖果售卖机,控制流程如下图,需要怎么实现? 这是一个状态图,每个圆圈都是一种状态。很明显,有有25分钱、 没有25分钱、 售出糖果、 糖果售罄四个状态,同时也对应四个动作:投入25分钱,退回25分钱,转动曲柄和发放糖果。 那如何从状态图得到真正的代码呢? 简单代码实现如下: #! -*- coding: utf-8 -*- class GumballMachine: # 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值 STATE_SOLD_OUT = 0 STATE_NO_QUARTER = 1 STATE_HAS_QUARTER = 2 STATE_SOLD = 3 state = STATE_SOLD_OUT def __init__(self, count=0): self.count = count if count > 0: self.state = self.STATE_NO_QUARTER def __str__(self): return "Gumball machine current state: %s" % self.state def insert_quarter(self): # 投入25分钱 if self.state == self.STATE_HAS_QUARTER: # 如果已经投过 print("You can't insert another quarter") elif self.state == self.STATE_NO_QUARTER: # 如果没有投过 self.state = self.STATE_HAS_QUARTER print("You inserted a quarter") elif self.state == self.STATE_SOLD_OUT: # 如果已经售罄 print("You can't insert a quarter, the machine is sold out") elif self.state == self.STATE_SOLD: # 如果刚刚买了糖果 print("Please wait, we're already giving you a gumball") def eject_quarter(self): # 退回25分 if self.state == self.STATE_HAS_QUARTER: print("Quarter returned") self.state = self.STATE_NO_QUARTER elif self.state == self.STATE_NO_QUARTER: print("You haven't inserted a quarter") elif self.state == self.STATE_SOLD: print("Sorry, you alread turned the crank") elif self.state == self.SOLD_OUT: print("You can't eject, you haven't inserted") def turn_crank(self): # 转动曲柄 if self.state == self.STATE_SOLD: print("Turning twice doesn't get you another gumball") elif self.state == self.STATE_NO_QUARTER: print("You turned but there's no quarter") elif self.state == self.STATE_SOLD_OUT: print("You turned, but there are no gumballs") elif self.state == self.STATE_HAS_QUARTER: print("You turned...") self.state = self.STATE_SOLD self.dispense() def dispense(self): # 发放糖果 if self.state == self.STATE_SOLD: print("A gumball comes rolling out the slot") self.count -= 1 if self.count == 0: self.state = self.STATE_SOLD_OUT else: self.state = self.STATE_NO_QUARTER elif self.state == self.STATE_NO_QUARTER: print("You need to pay first") elif self.state == self.STATE_SOLD_OUT: print("No gumball dispensed") elif self.state == self.STATE_HAS_QUARTER: print("No gumball dispensed") if __name__ == "__main__": # 以下是代码测试 gumball_machine = GumballMachine(5) # 装入5 个糖果 print(gumball_machine) gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 print(gumball_machine) gumball_machine.insert_quarter() #投入25分钱 gumball_machine.eject_quarter() # 退钱 gumball_machine.turn_crank() # 转动曲柄 print(gumball_machine) gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 gumball_machine.insert_quarter() # 投入25分钱 gumball_machine.turn_crank() # 转动曲柄 gumball_machine.eject_quarter() # 退钱 print(gumball_machine) 这段代码有几个问题: ...

2018-12-31 · 4 min · 802 words

python设计模式-模板方法模式

首先先介绍一下咖啡和茶的冲泡方法: 茶 1. 把水煮沸 2. 用沸水浸泡茶叶 3. 把茶放到杯子里 咖啡 1. 把水煮沸 2. 用沸水冲泡咖啡 3. 把咖啡倒进杯子 4. 加糖和牛奶 用python代码实现冲泡方法大概是这个样子: # 茶的制作方法 class Tea: def prepare_recipe(self): # 在下边实现具体步骤 self.boil_water() self.brew_tea_bag() self.pour_in_cup() def boil_water(self): print("Boiling water") def brew_tea_bag(self): print("Steeping the tea") def pour_in_cup(self): print("Pouring into cup") # 咖啡的制作方法 class Coffee: def prepare_recipe(self): # 在下边实现具体步骤 self.boil_water() self.brew_coffee_grinds() self.pour_in_cup() self.add_sugar_and_milk() def boil_water(self): print("Boiling water") def brew_coffee_grinds(self): print("Dripping Coffee through filter") def pour_in_cup(self): print("Pouring into cup") def add_sugar_and_milk(self): print("Adding Sugar and Milk") 仔细看上边两端代码会发现,茶和咖啡的实现方式基本类似,都有prepare_recipe,boil_water,pour_in_cup 这三个方法。 ...

2018-12-02 · 2 min · 261 words

python设计模式-外观模式

上一篇《python设计模式-适配器模式》介绍了如何将一个类的接口转换成另一个符合期望的接口。这一篇将要介绍需要一个为了简化接口而改变接口的新模式-外观模式(Facade-Pattern)。 问题 问题:如果你组装了一套家庭影院,内含播放器、投影机、自动屏幕、立体声音响、爆米花机等。如何设计一个遥控器,可以简单的操作这个系统中的各个组件呢? 首先来看一下最笨方式观赏电影的步骤: 打开爆米花机 开始爆米花 将灯光调暗 放下屏幕 打开投影仪 将投影机的输入切换到播放器 将投影及设置在宽屏模式 打开功放 将功放的输入设置为播放器 将攻防设置为环绕立体声 将攻防音量调到适中 打开播放器 播放电影 写成类和方法的调用大概是以下的样子: # 打开爆米花机,开始爆米花 poper.on() poper.pop() # 灯光调暗 lights.dim(10) # 放下屏幕 screen.down() # 打开投影仪,设置为宽屏模式 projector.on() projector.setInput(dvd) projector.wideScreenMode() # 打开功放 设置为DVD 调整成环绕立体声模式,音量调到5 amp.on() amp.setDvd(dvd) amp.setSurroundSound() amp.setVolume(5) # 打开dvd 播放器 dvd.on() dvd.play(movie) 可以看到代码中涉及到6个不同的类,而且电影看完后还需要回退,一切都要再反着重来一遍。怎样简化一下操作呢? 现在,外观模式就可以大展身手了。 使用外观模式,可以通过实现一个提供更合理的接口的外观类,将子系统变得更容易使用。当然,原来的接口还在。 解决方法 先来看一下外观模式如何运作 这里为家庭影院系统创建了一个新的外观类HomeTheaterFacade,这个类暴露出来几个简单的方法,比如watchMovie,endMovie。 这个外观类将家庭影院的多个组件看作一个子系统,通过调用这个子系统来实现watchMovie方法。 外观只提供了一个更直接的操作方式,并没有将原来的子系统隔离,子系统的功能还可以使用 注意: 可以有多个外观 外观提供简化的接口,但不隔离子系统 外观将实现从子系统中解耦,比如:现在有个子系统的组件需要升级换代,只需要把外观代码做相应的修改就可以实现 外观和适配器都可以包装多个类,但是外观的意图时简化接口的调用,而适配器的意图是将接口转换成不同的接口。 示例 class HomeTheaterFacade(object): #先声明需要用的子组件 amp = Amplifier() tuner = Tuner() dvd = DvdPlayer() cd = CdPlayer() projector = Projector() lights = TheaterLights() screen = Screen() popper = PopcornPopper() def watchMovie(self, movie): # watchMovie 将之前需要手动处理的任务批量处理 print("Get ready to watch a movie...") # 打开爆米花机,开始爆米花 self.poper.on() self.poper.pop() # 灯光调暗 self.lights.dim(10) # 放下屏幕 self.screen.down() # 打开投影仪,设置为宽屏模式 self.projector.on() self.projector.setInput(dvd) self.projector.wideScreenMode() # 打开功放 设置为DVD 调整成环绕立体声模式,音量调到5 self.amp.on() self.amp.setDvd(dvd) self.amp.setSurroundSound() self.amp.setVolume(5) # 打开dvd 播放器 self.dvd.on() self.dvd.play(movie) def endMovie(self): # endMovie 负责关闭一切,由子系统中的组件完成 print("Shutting movie theater down...") self.popper.off() self.lights.on() self.screen.up() self.projector.off() self.amp.off() self.dvd.stop() self.dvd.eject() self.dvd.off() 代码使用 def main(): home_theater = HomeTheaterFacade() # 实例化外观 home_theater.watchMovice() # 使用简化方法开启 关闭电影ß home_theater.endMovice() 定义 定义:外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。 ...

2018-11-25 · 1 min · 196 words