问题
有些变量,它们在特定的情况下才有意义。有些功能需要多步才能完成,结果就需要一些中间变量保存过程的状态,过程结束后变量就失去存在的价值。缺点:
- 浪费存储空间,虽然内存很廉价,但还是能省则省
- 中间变量变多的时候,所在类越来越大,越来越难以理解
解决方案
封装一个中间变量管理器:支持创建、删除、取值、设值这几个操作就行。
临时变量定义:
|
|
临时变量的使用:
|
|
Keep eyes on the star and feet on the ground.
有些变量,它们在特定的情况下才有意义。有些功能需要多步才能完成,结果就需要一些中间变量保存过程的状态,过程结束后变量就失去存在的价值。缺点:
封装一个中间变量管理器:支持创建、删除、取值、设值这几个操作就行。
临时变量定义:
|
|
临时变量的使用:
|
|
网游服务器端开发过程中,很多控制游戏的参数都不应该直接硬编码的。需要各种各样的配置和脚本文件,好处:
可以由策划或数值去随意修改,而不用动程序代码
配置可以动态加载,可以动态改变服务器运行中的参数,对已经发布的功能进行调整
一般,可采用:
XML(eXtensible Markup Language)是一种标记语言,用于说明数据是什么,以及携带数据信息。主要用于:
下面主要介绍一下对于xml文件作为服务器配置时候的解析方案。
解析下面的XML文件:
|
|
使用XMLPaser(用libxml2封装的一个解析器)来解析(TinyXML也类似,DOM方式的都大同小异):
|
|
坏味道分析
上面的代码,有几点不足之处,列举如下:
1> 代码重复
整个解析过程大同小异,一步一步遍历加载在内存中的节点树
节点或节点属性的名称、节点的层次结构不同的时候,就得写不同的代码,一般会采用复制代码的方式
使用不便
往往要写一个单件管理器,在服务器启动的时候加载该配置,然后在管理器里面把需要的数据结构都定义好
使用的时候,引用管理器里面的成员变量,代码既丑陋又容易出错
2> 不安全
节点名称、属性名称都是字符串,拼错了,运行时会发生逻辑错误
C++的结构与XML的对应树状结构对应起来,也就是数据绑定方案(Xml Data Binding)。自己曾经实现过一个Xml Data Binding库,名为xml_parser。具体用法如下:
step1: 编写一份描述XML结构的配置文件(也是一份XML文件,xml_parser.xml)
|
|
step2: 生成binding类
xmlpg -f xml_paser.xml -o xml_parser.h
*step3: 应用程序中使用
|
|
| 方式 | 特征 | 开源库 |
|---|---|---|
| DOM(Document Object Model) | 1. 文档对象模型,整个文档就是一个根节点及其子节点构成; 2. 树状,有子节点、父节点、兄弟节点; 3. 访问效率较低 | libxml2; Xerces-C++; TinyXML; SlimXML; RapidXML |
| SAX(Simple API for XML) | 基于事件解析XML | libxml2; Xerces-C++ |
| Data Binding | 1. C++的结构与XML的对应树状结构对应起来,使用起来比较容易; 2. 安全,C++的结构为静态的,不会因为写错节点或节点属性名称拼写错误而导致逻辑错误; 3. 代码简洁、清晰; 4. 访问效率高,对所为节点或节点属性的访问只是函数调用,而不像DOM方式去循环遍历整个子树的节点,做一系列字符串比较操作; 5. 不足之处,结构必须已知,DOM方式则不论程序里面对应的结构,先把整个节点树加载到内存中,程序根据自己的需要去读取自己想要的节点或节点属性 | CodeSynthesis XSD |
| 比较 | XML | Excel表格 |
|---|---|---|
| 结构 | 树状的层次结构 | MxN的二维数组 |
| 适用性 | 信息具有层次性; 结构复杂 | 有一个键值可以索引的关联数组结构; 结构简单 |
| 不足之处 | 配置起来不是那么方便,每个节点名、属性名都必须指定 | 添加新列的时候,不一定所有行都用到该列属性,容易导致空间的浪费 |
代码坏味道:是指在代码之中潜在问题的警示信号。并非所有的坏味道所指示的确实是问题,但是对于大多数坏味道,均很有必要加以查看,并作出相应的修改。
1. 重复的代码
如果你在一个以上的地点看到相同的程序结构,那么当可肯定:设法将它们合而为一,程序会变得更好。
1> 同一个class内的两个函数中含有重复的代码段
2> 两个兄弟class的成员函数中含有重复的代码段
3> 两个毫不相关的class内出现重复的代码段
注意:重复的代码是多数潜在BUG的温床!
2. 过长的函数
拥有短函数的对象会活的比较好、比较长。
1> 程序愈长就愈难理解
2> 函数过长阅读起来也不方便
3> 小函数的价值:解释能力、共享能力、选择能力
原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立的函数中。记着,起个好名字!
3. 过大类
如果想利用单一类做太多事情,其内往往就会出现太多的成员变量。
1> 提取完成同一任务的相关变量到一个新的类
2> 干太多事情的类,可以考虑把责任委托给其他类
注意:一个类如果拥有太多的代码,也是代码重复、混乱、死亡的绝佳滋生地点。
4. 过长的参数列表
太长的参数列表难以理解,太多参数会造成前后不一致、不易使用,而且你需要更多数据时,就不得不修改它。
原则:参数不超过3个!
5. 发散式变化
我们希望软件能够更容易被修改。一旦需要修改,我们希望能够跳到系统的某一点,只在该处做修改。如果不能做到这点,你就嗅出“坏味道:发散式变化”或“坏味道:霰弹式修改”。
发散式变化:一个类受多种变化的影响
数据库新加一个字段,同时修改三个函数:Load、Insert、Update
新加一个角色二进制,同时修改四处
原则:针对某一外界变化的所有相应修改,都只应该发生在单一类中
6. 霰弹式修改
如果每遇到某种变化,你都必须在许多不同的类内做出许多小修改以响应之。如果需要修改的代码散布四处,你不但难以找到它们,也很容易忘记某个重要的修改。
霰弹式修改:一种变化引起多个类相应的修改
7. 依恋情节
函数对某个类的兴趣高过对自己所处类的兴趣,就会产生对这个类的依恋情节,造成紧耦合。
原则:判断哪个类拥有最多被此函数使用的数据,然后将这个函数和那些数据摆在一起。
原则:将总是变化的东西放在一块。
8. 数据泥团
有些数据项,喜欢成群结队地待在一块。那就把它们绑起来放在一个新的类里面。这样就可以:
1> 缩短参数列表
2> 简化函数调用
9. 基本型别偏执
代码中有很多基本数据类型的数据。
原则:如果看到一些基本类型数据,尝试定义一种新的数据类型,符合它当前所代表的对象类型。
10. switch惊悚现身
面向对象程序的一个最明显特征就是:少用switch语句。从本质上说,switch语句的问题在于重复。
原则:看到switch你就应该考虑使用多态来替换它。
11. 冗赘类
你所创建的每一个类,都得有人去理解它、维护它,但一个类没有存在的必要时候,就让这个类庄严扑义吧!
原则:如果一个类的所得不值其身价,它就应该消失。
12. 夸夸其谈其未来性
对未来不可预知的变化考虑的过多,造成系统更难理解和维护。如果应对变化的代码都会被用到,那是值得的那么做;如果用不到,就不值得。
原则:代码应该满足当前的需求,并留有可扩展的余地。对于未来的变化,既不要考虑的太多,也不能一点都不考虑。
13. 令人迷惑的暂时成员变量
有时你会看到这样的对象:其内某个成员变量仅为某种特定的情形而设。这样的代码容易让人不解,因为你通常认为对象在所有时候都需要它的所有变量。
在变量未被使用的情况下猜测当初设置目的,会让你发疯。
14. 无用的中间人
过度使用委托。你也许会看到某个类的接口有一半的函数都委托给其他类,这样就过度运用了。所以,删除无用的中间人。
15. 狎昵关系
有时你会看到两个类过于亲密,花费太多时间去探究彼此的private成分。
原则:过分狎昵的类必须拆散。
16. 异曲同工的类
如果两个函数做同一件事情,却有着不同的签名式。
原则:删除一个,保留一个。
17. 不完美的程序库类
库的设计有时会不够好,不那么容易使用,可能还会有那么一点小缺陷。
工具:
1> Introduce Foreign Method
2> Introduce Local Extension
18. 过多的注释
过多注释的代码段,往往都是因为那段代码比较糟糕,散发着一股恶臭。
原则:当你感觉需要写注释时,请尝试重构,试着让所有注释都变得多余。
参考资料:
1> 《重构:改善既有代码的设计》
2> 《重构手册》
做网络游戏服务器端,最烦人的就是查后台日志。外面的玩家报BUG或者其他异常的时候,客服搞不定的时候,就需要服务器的程序去查日志。分析日志需要一些比较好的文本分析工具,grep和sed都是不错的文本分析工具,还有awk,再过复杂的日志分析估计就要写脚本了,推荐使用Python。
(其实我一直在纳闷,数据分析这些事情,怎么老是需要服务器程序去查,有专门的数据中心、运维和客服部门,人家才是专业的,工具比咱用的更“专业”吧。最后想了想,公司流程不完善,游戏后台日志分析自己这边还是做起来,不然搞的太被动了也不好。)
下面是sed的一个快速参考,更加详细的介绍,还有用法实例,可以参考《sed and awk》那本书。或者本文后面的那几个链接,都有很好的介绍。
有两种调用sed的方式:
sed [-n] [-e] 'command' file(s)
sed [-n] -f scriptfile file(s)
第一种方式允许在命令行指定一个编辑命令,用单引号括起来。第二种形式允许指定一个scriptfile,即包含sed命令的文件。两种形式可以一起使用,并且他们可以被多次使用。编辑的结构是将命令和脚本文件串联起来。
下面是可识别的选项:
-n
仅打印用p命令或s命令标记指定的行
-e cmd
下一个参数是编辑命令。当指定多个脚本时很有用。
-f file
下一个参数是一个包含编辑命令的文件。
如果脚本的第一行是”#n”,sed将按-n指定的方式工作。
频繁使用sed脚本通常是通过shell脚本来调用的。
sed命令的一般形式为:
[address[,address]][!]command[arguments]
sed将每个输入行拷贝到一个模式空间。sed指令由地址和编辑命令组成。如果命令的地址和模式空间中的行匹配,那么这个命令将被应用于匹配行。如果一个命令没有地址,那么它被应用于每个输入行。如果一个命令改变了模式空间的内容,后续的命令地址将被应用与模式空间中的当前行,而不是原始的输入行。
地址可以是一个行号或是由斜杠包含着的一个模式(/pattern/)。模式是用正则表达式表示的。另外,\n可以用来与模式空间(N命令的结果)的任意换行符匹配,但模式空间底部的换行符除外。
如果没有指定模式,相应的命令将被应用于所有的行。如果只指定一个地址,那么相应的命令将被应用于和这个地址匹配的行。如果指定了两个用逗号分隔的地址,这个命令将被应用于位于第一个和第二个地址范围之间的所有行。一些命令只接收一个地址,包括a,i,r,q和=。
在地址后面的”!”操作符使sed将相应的命令作用于所有与该地址不匹配的行。
大括号({})被sed用于地址的嵌套或对同一个地址应用多个命令。
|
|
左大括号必须在一行的末尾,而右括号必须单独在一行。确保大括号后面没有空格。
.
匹配除换行符以为的任意单个字符
*
匹配任意个(包括0个)在它前面的字符(包括由正则表达式指定的字符)
[…]
匹配广括号中的字符中的任意一个字符。其它所有的元字符被指定为其中成员时都会失去它们原来的含义。如果方括号第一个字符为脱字符(^)则匹配除了换行符和列出的那些字符以为的所有字符。连字符(-)用来表示字符的范围。如果其中第一个字符为右方括号(]),则表示它是这个类的成员。
{n,m}
它前面的某个范围内单个字符出现的次数(保护正则表达式指定的字符)。{n}将匹配n次出现,{n,}匹配大于等于n次出现的。
^
定位位于行起始位置后面的正则表达式。只有当^符号出现在正则表达式的起始位置时才是特殊的。
$
定位位于行末尾的正则表达式,只有当$符号出现在正则表达式的末尾时才是特殊的。
\
转义随后的特殊字符
( )
将包含在””和””之间的模式保存到一个特殊的保持空间。用这种方法在一行中可以最多保存9个模式。用转义序列”\1″ 到 “\9″ 可以重新使用它们。
\n
匹配前面””和””保存的第n个模式,这里n是一个从1到9的数字,前面保存的模式从行的左边开始编号。
&
当在替换字符串中使用时打印整个被匹配的文本
: :lable
在脚本中标记一行,用于实现由b或t的控制转移。label最多可以包含7个字符。(GNU sed允许标签的长度任意)
= [address]=
将所寻址的行编号写到标准输出。
a [address]a\ .. text
在与address匹配的每行后面追加text。如果text多于一行,必须用反斜杠将这些行前面的换行符“隐藏”起来。text将被没有用这种方法隐藏的第一个换行符结束。text在模式空间中不是可用的并且后续命令不能应用于它。当编辑命令的列表用完时这个命令的结果将被输送到标准输出,而不管在模式空间中的当前行发生了什么。
c [address1[,address2]]c \ .. text
用text代替(改变)由地址选定的行。当指定的是一个行范围时,将所有的这些行为作为一个组由text的一个副本来代替。每个text行后面的换行符必须用反斜杠将其转义,但最后一行除外。实际上,模式空间的内容被删除,因此后续的命令不能应用于它(或应用于text)
d [address1[,address2]]d
从模式空间中删除行。因此行没有被传递到标准输出。一个新的输入行被读取,并用脚本的第一个命令来编辑。
D [address1[,address2]]D
删除由命令N穿件的多行模式空间中的第一部分(直接嵌入的换行符),并且用脚本的第一条命令恢复编辑。如果这个命令使模式空间为空,那么将读取一个新的输入行,和执行了d命令。
g [address1[,address2]]g
将保持空间中的内容复制到模式空间。如果保持空间为空,则将换行符添加到模式空间。
G [address1[,address2]]G
将保持空间中的内容追加到模式空间。如果保持空间为空,则将换行符添加到模式空间。
h [address1[,address2]]h
将模式空间的内容复制到保持空间,即一个特殊的临时缓冲区。保持空间的当前内容被清楚。
H [address1[,address2]]H
将换行符和模式空间的内容追加到保持空间中,即使保持空间为空,这个命令也追加换行符。
i [address]i\ .. text
将text插入到每个和address匹配的行的前面。
l [address1[,address2]]l
列出模式空间的内容,将不可打印的字符表示为ASCII码。长的行被折行。
n [address1[,address2]]n
读取下一个输入行到模式空间。当前行被送到标准输出。新行成为当前行并递增计数器。将控制转到n后面的命令,而不是恢复到脚本的顶部。
N [address1[,address2]]N
将下一个输入行追加到模式空的内容之后;新添加的行与模式空间的当前内容用换行符分隔(这个命令用于实现两行的模式匹配。利用\n来匹配嵌入的换行符,则可以实现多行模式匹配)
p [address1[,address2]]p
打印所寻址的行。注意这将导致输出的重复,除非默认的输出用”#n”或”-n”命令行选项限制。常用于改变流控制(d,n,b)的命令之前并可能阻止当前行被输出。
P [address1[,address2]]P
打印由N命令创建的多行模式空间的地一部分(直接嵌入的换行符)。如果没有将N应用于某一行则和p相同。
q [address]q
当遇到address时退出。寻址的行首先被写到输出(如果没有限制默认输出),包括用前面的a或r命令为它追加的文本。
r [address]r file
读取file的内容并追加到模式空间内容的后面。必须在r和文件名file之间保留一个空格。
s [address1[,address2]]s/pattern/replacement/[flags]
用replacement代替每个寻址行的pattern。如果使用了模式地址,那么模式//表示最后指定的模式地址。可指定下面的标志。
n
替代每个寻址的行的地n个/pattern/。n是1到512之间的任意数值,并默认致为1。
g
替代每个寻址的行的所有/pattern/,而不只是第一个。
p
如果替换成功则打印这一行。如果成功进行了多次替换,将打印这个行的多个副本。
w file
如果发生一次替换则将这行写入file。最多可以打开10个不同的file。
t [address1[,address2]]t [label]
测试在寻址的行范围内是否成功执行了替换,如果是,则转移到有lable标志的行。如果没有给出lable,控制转移到脚本的底部。
w [address1[,address2]]w file
将模式空间的内容追加到file。这个动作是在遇到命令时发生而不是在输出模式空间内容时发生。必须在w和这个文件名之间保留一个空格。在脚本中可以打开的最大文件数是10。如果文件不存在,这个命令将创建一个文件。如果文件存在,则每次执行脚步时将修改其内容,多重写入命令直接将输出写入到同一个文件并追加到这个文件的末端。
x [address1[,address2]]x
交换模式空间和保持空间的内容。
y [address1[,address2]]y/abc/xyz/
安位置将字符串abc中的字符转换成字符串xyz中的相应该字符。
《生命之树》由泰伦斯·马力克导演。故事开始于一个1950年代,生活在美国中西部的家庭的杰克在童年时收到双亲两种矛盾的教育。成年后的杰克渐渐迷失了自己,发现他在现代社会失去了灵魂,他寻觅生命的原初以及意义的答案,拷问信仰的存在,最终明白了生命的真谛。
故事讲述生长在美国中西部的11岁少年杰克的成长变迁。他的家庭由父母和三兄弟组成。在一个孩子眼中,起初一切都是新奇的。他从母亲身上看到了慈爱,而他的父亲告诉他,这个世界的生存法则就是把自己放在首位。在成长过程中,杰克竭力想要调和双亲相悖的人生观。

故事开始于一个50年代,生活在美国中西部的家庭,电影跟随着大儿子杰克的生命展开,穿过其无辜天真的童年到幻灭的成人时代。
西恩·潘饰演的杰克出生于美国中西部,父亲奥布莱恩是一个颇为守旧的农民,经常强调要以自己为中心,去做一个纯爷们真汉子,而母亲则是温柔又感性。杰克就在双亲不同的教育中长大成人,渐渐疲惫了现代都市生活的杰克突然发现自己已经迷失了。最终是父母的教育让他明白了, 生命之树 电影生命的真谛就在于家庭之中。
影片中母亲便说:“人生分为两种活法,一种是自然的,一种是圣洁的,要看你自己选择哪一种。”影片预告片最后一句仍是母亲的教导:“只要爱过,人生就不算白过。”
There are two ways in life: the way of nature, and the way of Grace. You must choose which one you will follow.
《水上音乐》,又称《水乐》、《船乐》。亨德尔作于1717年,是一部管弦乐组曲。传说是在英国伦敦泰晤士河上为新即位的英皇乔治一世演奏的,故有“水上音乐”的美名。全部组曲由二十首小曲组成,开始是一首法国式的前奏曲,其后是布莱舞曲、小步舞曲等各种形式的舞曲,同时也有缓慢乐章。乐器使用了小提琴、低音提琴、日耳曼横笛、法兰西横笛、双簧管、圆号、小号等。

现在我们演奏和听到的《水上音乐》已经不是亨德尔的原作,而是后来英国曼彻斯特的哈莱乐队指挥哈蒂(harty)爵士为近代乐队所改编的乐曲,共有六个乐章: 快板、布莱舞曲、小步舞曲、号角舞曲(一种古代的三拍子舞曲)、行板、坚决的快板。由于旋律优美动听,节奏轻巧而流传后世。这里我们选录了六个乐章中的第一乐章、第二乐章和第六乐章。
第一乐章为庄严的序曲,乐曲气氛活泼热烈,开始由圆号与弦乐器共同奏出轻盈的同音反复和华美的颤音,相互对答。
第二乐章为舞曲般的旋律,气氛轻松舒展。这里选录的第二主题为小调,抒情性很强。
第六乐章为坚决的快板,威武雄壮。这一部分是全曲最为精彩的篇章。
亨德尔的《水上音乐》,乃亨德尔访问伦敦时,为伦敦的王室而作,作于1715年。当时,在泰晤士河上夜游,是皇室喜爱的夏季活动。乘坐挂着彩灯的游船,周围有成群结队簇拥着的大小帆船,国王乘坐的船旁有音乐家的船演奏音乐。亨德尔一共参加了三次这样的夜游活动(1715年8月22日,1717年7月17日,1736年4月26日),他的《水上音乐》最早的原始手稿其实已丢失,最细致的复原本乃莱德利希所复制。这部作品根据亨德尔的三次夜游,编为三组。
用更加简洁、健壮、可移植、可维护和内聚的面向对象接口,封装已存在的非面向对象API的函数和相关数据。
使用已存在的非面向对象的API提供的服务或机制,开发可维护和进化的应用。
结构:

行为:

实现:
已知应用
优势:
不足:
使用vim的wikimedia.vim插件,可以高亮wiki语法关键字,自动补齐等功能。对于喜欢用vim编辑器的人来说,用此方式编辑wiki再好不过了。可以自动识别的文件类型为*.wiki,或者set filetype=mediawiki。

该MediaWiki扩展,使得用户可以以WYSIWYG方式编辑WIKI,使用了CKEditor。
配置MediaWiki的LocalSettings.php
require_once(“$IP/extensions/WYSIWYG/WYSIWYG.php”);
设置可以使用者的权限
所有人可用:$wgGroupPermissions['*']['wysiwyg']=true;
注册用户可以:$wgGroupPermissions['registered_users']['wysiwyg']=true;

可以将MS Word 2007/2012的文档直接保存为MediaWiki语法格式的文件。
下载WORD插件:http://www.microsoft.com/download/en/details.aspx?id=12298
运行插件安装程序
迄今为止,人们提出的软件开发模式有不少是关于分布式计算的,但人们始终无法以完整的视角了解分布式计算中各种模式是如何协同工作、取长补短的。构建复杂的分布式系统似乎成为了永远也无法精通的一门手艺。本书的出版改变了这一切。
本书是经典的POSA系列的第4卷,介绍了一种模式设计语言,将分布式系统开发中的114个模式联系起来。书中首先介绍了一些分布式系统和模式语言的概念,然后通过一个仓库管理流程控制系统的例子,介绍如何使用模式语言设计分布式系统,最后介绍模式语言本身。

使用这一模式语言,人们可以有效地解决许多与分布式系统开发相关的技术问题,如
★ 对象交互
★ 接口与组件划分
★ 应用控制
★ 资源管理
★ 并发与同步
本书从实用角度展示了如何从现有的主要模式中整合出一门全面的模式语言,用于开发分布式计算中间件及应用程序。作为该领域在市场上唯一统揽全局的书,它将给读者带来醍醐灌顶的感觉!
以前总是困惑自己正在开发的游戏网络通信框架是前人怎么想出来的,看着底层那些乱七八糟的代码,即膜拜又感到不解。看到本书讲的一个模式:Half-Sync/Half-Async 顿时感觉清楚了许多。这本书对于很多模式都讲的不是很细致,只是粗略总结一下,详细的用法和介绍还是要去参考一些《面向模式的软件架构I》、《面向模式的软件架构II》、《设计模式:可复用面向对象基础》。对于网络框架的架构这本书和《面向模式的软件架构II》必备。
下面把书中提及到的分布式相关的模式列举出来:
从混沌到结构(From Mud To Structure )
分布式架构
事件分派(Event Demultiplexing and Dispatching)
接口划分(Interface Partitioning )
组件划分(Component Partitioning)
应用控制(Application Control)
并发(Concurrency)
同步(Synchronization)
对象交互(Object Interaction)
适配和扩展(Adaptation and Extension)
模态行为(Modal Behavior)
资源管理(Resource Management)
数据库访问(Database Access)
以上模式详细内容还需要更加深入的应用才能很好的掌握。继续学习…
Martin Fowler和《重构:改善既有代码的设计》(中文版)另几位作者清楚揭示了重构过程,他们为面向对象软件开发所做的贡献,难以衡量。《重构:改善既有代码的设计》(中文版)解释重构的原理(principles)和最佳实践方式(best practices),并指出何时何地你应该开始挖掘你的代码以求改善。《重构:改善既有代码的设计》(中文版)的核心是一份完整的重构名录(catalog of refactoring),其中每一项都介绍一种经过实证的代码变换手法(code transformation)的动机和技术。某些项目如Extract Method和Move Field看起来可能很浅显,但不要掉以轻心,因为理解这类技术正是有条不紊地进行重构的关键。

Extract Method: 你有一段代码可以被组织在一起并独立起来,将其放入一个独立的函数,并让函数名称解释该函数的用途
Inline Method: 一个函数,其本体和名称同样清楚易懂,在函数调用点插入函数本体,然后移除该函数
Inline Temp: 你有一个临时变量,只被一个简单表达式赋值一次,将所有对该变量的引用,替换为对它赋值的那个表达式自身
Replace Temp With Query: 你的程序以一个临时变量保存某一个表达式的运算结果,将这个表达式提炼到一个独立的函数中,将这个临时变量的所有被引用点替换为对新函数的调用。新函数也可以被其它函数使用。
Introduce Explaining Variable: 你有个复杂的表达式,将该复杂的表达式或其中的一部分的结果放进一个临时变量,以此变量名称来解释表达式的用途
Split Temporary Variable: 你的程序有某个临时变量被赋值超过一次,它既不是循环变量,也不是一个集用临时变量。针对每次赋值,创造一个独立的、对应的临时变量。
Remove Assignments to Parameters: 你的代码对一个参数进行赋值动作,以一个临时变量取代该参数的位置
Replace Method With Method Object: 你有一个大型函数,其中对局部变量的使用,使你无法使用Extract Method。将这个函数放入一个单独的对象,如此一来局部变量就变成了对象内的值域,然后你尅在同一个对象中将这个大型函数分解为数个小型函数。
Substitute Algorithm: 你想要把某个算法替换为另一个更清楚的算法,将函数本体替换为另外一个算法。
Move Method: 你的程序中,有个函数与其所驻class之外的另外一个class进行更多交流(调用或者,或被后者调用)。在该函数最长引用的class中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是将旧函数完全删除。
Move Field: 你的程序中,某个值域被其所驻class之外的另一个classs更多的用到。在target class建立一个new field,修改source field的所有用户,令它们改用 new field。
Extract Class: 某个class做了应该由两个classes做的事。建立一个新class,将相关的值域和函数从旧class搬移到新class。
Inline Class: 你的某个class没有做太多事情(没有承担足够责任)。将class的所有特性搬移到另外一个class中,然后移除原class。
Hide Delgate: 客户直接调用server object(服务对象)的delegate class。在server端(某个class)建立客户所需的所有函数,用以隐藏委托关系。
Remove Middle Man: 某个class做了过多的简单委托动作。让客户端直接调用delegate(受托类)。
Introduce Foreign Method: 你所使用的server class 需要一个额外的函数,但你无法修改这个class。在client class中建立一个函数,并以一个server class实体作为第一个参数。
Introduce Local Extension: 你所使用的server class需要一些额外的函数,但你无法修改这个class。建立一个新class,使它包含这些额外函数。让这个扩展品成为source class的subclass(子类)或 wrapper(外覆类)。
Self Encapsluate: 你直接访问一个值域,但与值域直接的耦合关系逐渐变得笨拙。为这个值域建getting/setting methods,并且以这些函数来访问值域。
Replace Data Value with Object: 你有一比数据项,需要额外的数据和行为。你将这笔数据变成一个对象。
Change Value to Reference: 你有一个class,衍生出许多相等实体,你希望将它们替换为单一对象。将这个value object变成一个reference object。
Change Reference to Value: 你有个reference object,很小且不可变,而且不易管理。将它鞭策一个value object。
Replace Array with Object: 你有一个数组,其中的元素各自代表不同的东西。以对象替换数组,对于数组中的每个元素,以一个值域表示。
Duplicate Observed Object: 你有一些domain data置于GUI控件中,而domain Method 需要访问之。将该数据copy 到一个domain object中,建立一个Observer模式,用以对domain object和GUI object内的重复数据进行同步控制。
Change Unidirectional Association to Bidirectional : 两个classes都需要对方的特性,但期间只有一条单向连接。添加一个反向指针,并使修改函数能够同时更新两条连接。
Change Bidirectional Association to Unidirectional: 两个classes直接有双向关联,但其中一个class如今不再需要另一个class的特性。去除不必要的关联。
Replace Magic Number with Symbolic Constant: 你有一个字面数值,带有特别的含义。创造一个常量,根据其意义为它命名,并将上述的字符数值替换为这个常量。
Encapsulate Filed: 你在class中一个public值域,将它声明为private,并提供相应得分访问函数。
Encapsulate Collection: 有个函数返回一个群集,让这个函数返回该群集的一个只读映像,并在这个class中提供添加/删除群集元素的函数。
Replace Record with Data class: 你需要面对传统编程环境中的record struct。为该record创建一个哑数据对象。
Replace Type Code with Class : class之中有一个数值型别码,但它并不影响class的行为。以一个新class替换该数值型别码。
Replace Type Code with Subclass: 你有一个不可变的type code,它会影响class的行为。以一个subclass取代这个type code。
Replace Type Code with State/Stratgery: 你有 一个type code,它会影响class的行为,但你无法使用subclassing。以state object取代type code。
Replace Subclass with Fields: 你的各个subclass的唯一差别只在返回常量数据的函数身上。修改这些函数,使它们返回superclass中的某个值域,然后销毁subclass。
Decompose Condtional: 你有一个复杂的条件语句,从if、then、else三个段落中分别提取出独立函数。
Consolidate Condtional Expression: 你有一系列条件测试,都得到相同的结果。将这些测试合并为一个条件式,并将这个条件式提炼成为一个独立函数。
Consolidate Duplicate Condtional Fragment: 在条件式的每个分支上有着相同的一段代码。将这段代码搬移到条件式之外。
Remove Control Flag: 在一系列布尔表达式中,某个变量带有控制标记的作用。以break语句或return语句取代控制标记。
Replace Nested Condtional wiht Guard Clauses: 函数中的条件逻辑使人难以看清楚正常的执行路径。使用卫语句表现所以特殊情况。
Replace Conditional with Polymorphism: 你手上有个条件式,它根据对象型别的不同而选择不同的行为。将这个条件表达式的每个分支放进一个subclass内的覆写函数中,然后将原函数声明为抽象函数。
Introduce Null Object: 你需要再三检查某物是否为null value,将null value替换为null object。
Introduce Assertion: 某一段代码需要对程序状态作出某种假设,以断言明确表现这种假设。
Rename Method: 函数的名称未能揭示函数的用途,修改函数的名称。
Add Parameter : 某个函数需要从调用端得到更多信息,为此函数添加一个对象参数,让该对象带进函数所需信息。
Remove Parameter: 函数本体不再需要某个参数,将该参数删除。
Seperate Query from Modifier: 某个函数既返回对象状态值,又修改对象状态,建立两个不同的函数,其中一个负责查下,另一个负责修改。
Parameterize Method: 若干函数做了类似的工作,但在函数本体中却包含了不同的值,建立一个单一函数,以参数表达那些不同的值。
Replace Parameter with Explict Methods: 你有一个函数,其内完全取决于参数采取不同的反应。针对该参数的每一个可能值,建立一个对立函数。
Preserver Whole Object :你从某个对象中取出若干值,将他们昨晚某一次函数调用时的参数。改使用传递整个对象。
Replace Parameter with Methods: 对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接收该参数的函数也可以调用前一个函数。让参数接受者去除该项参数,并直接调用前一个函数。
Introduce Parameter Object : 某些参数总是很自然的同时出现,以一个对象取代这些参数。
Remove Setting Method: 你的clas中的某个值域,应该在对象初始时被设值,然后就不再改变。去掉该值域的设值函数。
Hide Method: 有一个函数,从来没有被其它任何class用到。将这个函数修改为private。
Replace Constructor with Factory Method: 你希望在创建对象时不仅仅是对它做简单的建构动作。将构造函数替换为工厂函数。
Encapsulate Downcast: 某个函数返回的对象,需要由函数调用者执行向下转型动作。将down-cast动作移动到函数中。
Replace Error Code with Exception: 某一个函数返回一个特定的代码,用以表示某种错误情况。改用异常。
Replace Exception with Test: 面对一个调用者可预先加以检查的条件,你抛出了一个异常。修改调用者,使它在调用函数之前先做检查。
Pull Up Field: 两个subclass拥有相同的值域,将此一值域移至superclass
Pull Up Method: 有些函数,在各个subclass中产生完全相同的结果,将该函数移至superclass
Pull Up Constructor Body: 你在各个subclass中拥有一些构造函数,他们的本体代码几乎完全一致。在superclass中新建一个构造函数,并在subclass构造函数中调用它
Push Down Method: superclass中的某个函数只与部分而非全部subclass有关。将这个函数移到相关的那些subclass去
Push Down Field: superclass中的某个值域只被部分而非全部subclass用到。将这个值域移到需要它的那些subclass去
Extract Subclass: class中的某些特性只被某些而非全部实体用到。新建一个subclass,将上面所说的那一部分特性移到subclass中
Extract Superclass: 两个class有些相似特性。为这两个classes建立一个superclass,将相同特性移到superclass
Extract Interface: 若干客户使用class接口中的同一子集;或者,两个classes的接口有部分相同。将相同的子集提炼到一个独立的接口中。
Collapse Hierarchy: superclass和subclass直接无太大区别,将它们合为一体。
Form Template Method: 你有一些subclasses,其中相应的某些函数以相同的顺序执行类似的措施,但各措施实际上有所不同。将各个措施分别放进独立函数中,并保持它们都有相同的签名式,于是原函数也就变得相同了。然后将原函数移到superclass。
Replace Inheritence with Delegation: 某个subclass只使用superclass接口中的一部分,或是根本不需要继承而来的数据。在subclass中新建一个值域用以保存superclass;调整subclass函数,令他改而委托superclass;然后去掉两者直接的继承关系。
Replace Delegation with Inheritence: 你在两个classes之间使用委托关系,并经常为整个接口编写许多及其简单的请托函数(delegating method),让请托class继承受托class。
Tease Apart Inheritance: 某个继承体系同时承担两项责任。建立两个继承体系,并通过委托关系让其中一个可以调用另一个。
Convert Procedural Design to Objects: 你手上有一些代码,以传统的过程化风格写就。将数据记录变成对象,将行为分开,并将行为移入对象之中。
Seperate Domain from Presentation: 某些GUI class之中包含了domain logic。将domain logic分离出来,为它们建立独立的domain classes。
Extract Hierarchy: 你有某些class做了太多工作,其中一部分工作是以大量条件式完成的。建立继承体系,以一个subclass表示一种特殊情况。
利用这本通过示例“说话”的实例手册,可以充分发挥重构的强大功能,改善现有的软件。
身为程序员,你必须具备的一个基本功就是能够找出并改善有问题的代码,使程序能够在软件的整个生命周期中正常运转。重构可谓是安全地改善既有代码设计的一门艺术,由此可以提供高效而可靠的系统,使纷杂凌乱归于平稳有序,并能最大限度地抑制异常的出现!重构可能很难掌握,但是在专业顾问William C.Wake所撰写的这本书中,经由作者娓娓道来,有关内容得以通过一种易于学习的方式展现出来,不仅使学习之旅颇具实效,而且充满乐趣。

对于许多人来说,学习重构的最大障碍是如何找出代码的“坏味道(smell)”,即可能存在问题之处。本书并非让你流水帐式地通读这些坏味道,而是确保你对这些坏味道有切实的理解。在此奉上了一系列精心组织的问题,通过这些问题的解决,你将会茅塞顿开,不仅会在更深层次上了解重构,而且还将获得你自己的一些心得体会。Wake采用了实例手册的方式来组织全书,以帮助你了解最为重要的重构技术并将其应用于代码之中。这是一种强调学习的方法,要求你必须充分应用本书所提供的诸多技术。除此之外,这种方法还有一个附带的好处,即尽管当前你所作的工作也许并非重构,利用本书也将有助于你更多地考虑如何创建优质的代码。
####简介
本书开创性地深入揭示了重构与模式这两种软件开发关键技术之间的联系,说明了通过重构实现模式改善既有的设计,往往优于在新的设计早期使用模式。本书不仅展示了一种应用模式和重构的创新方法,而且有助于读者结合实战深入理解重构和模式。书中讲述了27种重构方式。

2012/03/09 09:07 于上海
这本书结合设计实作例从面向对象的设计中精选出23个设计模式,总结了面向对象设计中最有价值的经验,并且用简洁可复用的形式表达出来。书中分类描述了一组设计良好、表达清楚的软件设计模式,这些模式在实用环境下特别有用。

23个设计最基础的设计模式,每每读起如醍醐灌顶!不愧为经典之作!
创建型
Abstract Factory:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类
Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
Factory Method:定义一个用于创建对象的接口,让子类决定实例化那一个类。Factory Method使得一个类的实例化延迟到其子类
Prototype:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
Singleton:保证一个类仅有一个实例,并提供一个访问它的全局访问点
结构型
Adapter:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作
Bridge:将抽象部分与它的实现部分分离,使它们都可以独立地变化
Composite:将对象组合成树形结构以表示”部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性
Decorator:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式比生成子类给为灵活
Facade:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得子系统更加容易使用
Flyweight:运行共享技术有效地支持大量细粒度的对象
Proxy:为其它对象提供一种代理以控制对这个对象的访问
行为型
Chain of Responsibility:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
Command:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作
Interpreter:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子
Iterator:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示
Mediator:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
Memento:在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
Observer:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
State:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类
Strategy:定义一系列的算法,把他们一个个封装起来,并且使它们可互相替换。本模式使得算法可独立与使用它的客户而变化
Template Method:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
Visitor:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作
本书以作者自身学习、使用模式和多年来为软件开发人员(包括面向对象技术老兵和新手)讲授模式的经验为基础撰写而成。首先概述了模式的基础知识,以及面向对象分析和设计在当代软件开发中的重要性,随后使用易懂的示例代码阐明了12个最常用的模式,包括它们的基础概念、优点、权衡取舍、实现技术以及需要避免的缺陷,使读者能够理解模式背后的基本原则和动机,理解为什么它们会这样运作。

对象、封装、面向对象设计不同视角的定义
视角对象封装面向对象设计概念具有责任的一个实体任何形式的隐藏:实现细节、派生类、设计细节、实例化规则共性分析规约接口集合共性分析、变性分析实现数据和方法集合数据隐藏变性分析
优秀代码的目标:
面向对象的一些原则和方法
内聚性(cohesion):例程中操作之间联系的紧密程度
耦合性(coupling):两个例程之间联系的紧密程度
软件开发的目标:高内聚、松耦合
发现变化并将其封装
寻找变化,并将它封装在一个单独的类中
将这个类包含在另一个类
当一个类处理越来越多不同变化时,代码的内聚性就会变得很差。即:它处理的特殊情况越多,可理解性就越差。
设计的两步法:
1> 抽象类(共性)—— 需要什么接口来出来这个类的所有责任?
2> 派生类(可变性)——对于这个给定的特定实现,应该怎样根据给定的规约来实现它?
处理新需求的选择:
1> 分析瘫痪;
2> 放任自流;
3> 考虑变化的设计
考虑变化的设计
原则:针对接口编程,而不是针对实现编程
原则:优先使用对象组合,而不是类继承
原则:考虑设计中什么应该是可变的
防止“分支蔓延”
一条规则,实现一次
组合起来:用模式思考
Alexander: 设计应该从问题的一个简单陈述开始,然后通过在这个陈述中加入信息,使它更加详细(也更加复杂)。一种基于模式的方法(《建筑的永恒之道》)
从整体的概念性理解开始,以理解需要实现的目标
找到整体中出现的模式
从为其它模式创造背景的那些模式开始
从背景向内:应用这些模式,找到新的模式,并重复
最后,通过每次应用一个模式,改进设计,并在所创建的背景中予以实现
用模式思考的过程:
考虑背景时候的一条规则:先考虑系统中需要什么,然后再去关注如何创建它们
背景和被使用的关系:当一个模式使用另一个模式时,被使用的模式就处于使用模式的背景中
设计模式的策略:
从背景设计
在类中封装变化
设计模式的原则:
开闭原则——模块、方法和类应该对扩展开放,对修改封闭。换言之,软件应该设计成不加修改缘由代码就能扩展功能。
依赖倒置原则——其背后的理念是在设计细节之前先创建总体概念。高层模块不应该依赖底层模块。相反,它们都应该依赖于抽象。
理性怀疑原则——小心过分依赖模式。概念层次的模式和模型都是真理的抽象。它们是已往经验和教训的结晶。使用它们来帮助我们思考摆在前面的问题。
共性与可变性分析(CVA):
先寻找共性
从这些共性创建抽象
从共性的变化寻找派生
看共性之间的关系如何
分析矩阵:
找到某种特定情况中最重要的特性,并用矩阵将它们组织起来。用特性所表示的概念为每个特性标记。
继续处理其它情况,安需要扩展这个矩阵。处理每一情况时应该独立于其它情况。
用新的概念扩展该分析矩阵
用行发现规则
用列发现实现
从这种分析中确定模式
得到高层设计
对象创建和管理规则:对象应该要么构造和/或管理其它对象,要么使用对象,而不应该兼而有之。
常见的设计模式:
Facade
Adapter
Strategy
Bridge
Abstract Factory
Decorator
Observer
Template Method
Singleton 和 Double-Checked Locking
Object Pool
Factory Method