TinyLab

Keep eyes on the star and feet on the ground.


  • 首页

  • 归档

  • 分类

  • 标签

每周一荐:代码编辑器sublime

发表于 2012-05-10   |   分类于 每周一荐

无意中发现了一个外观酷酷的代码编辑器:sublime,起初还以为就是一“花瓶”。网上随便搜了下,sublime原来还是比较强力的(据说想要干掉Mac下面的TextMate)。MacOS、Linux、Windows平台下都可以使用,支持各种插件。做为一个文本编辑软件,sublime给我的感觉是,它有许多非常有创意的操作,并且感觉很流畅,用着很舒服,界面也很简洁、美观。如:快捷的命令面板(Ctrl+Shift+P)、快速文件切换(Ctrl+P)、文件小地图、VIM模式、随心所欲地跳转和多重选择。

sublime

现在对于这种可以任意定制的软件越用越感觉爽了,起初就一及其简单的界面,岂不知背后蕴藏着无穷的技巧和可扩展性,foobar、sublime、vim、SlickEdit等都是越用越感觉好的软件。不过对于长期在Linux下做开发的人,最实用的代码编辑器还是要属vim或emacs了。

sublime

下面给几个参考链接,喜欢折腾的同学可以玩一下看:

  1. 官方网站:http://www.sublimetext.com/
  2. 官方文档:http://www.sublimetext.com/docs/2/
  3. 入门介绍:http://lucifr.com/139225/sublime-text-2-tricks-and-tips/

临时变量管理器

发表于 2012-05-08   |   分类于 重构

问题

有些变量,它们在特定的情况下才有意义。有些功能需要多步才能完成,结果就需要一些中间变量保存过程的状态,过程结束后变量就失去存在的价值。缺点:

  1. 浪费存储空间,虽然内存很廉价,但还是能省则省
  2. 中间变量变多的时候,所在类越来越大,越来越难以理解

解决方案

封装一个中间变量管理器:支持创建、删除、取值、设值这几个操作就行。

临时变量定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Player
{
public:
enum TempVariableType
{
kTempInvalid,
kTempTest,
kTempJumpVerification,
};

TempVariableManager<Player> tmpvars;
};

struct JumpVerification
{
JumpVerification(DWORD x_=0, DWORD y_=0, DWORD mapid_=0) :
x(x_), y(y_), mapid(mapid_) {}
DWORD x;
DWORD y;
DWORD mapid;
};

// 中间变量的定义
define_tempvariable(Player, kTempTest, DWORD);
define_tempvariable(Player, kTempJumpVerification, JumpVerification);

临时变量的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 新创建
pUser->tmpvars.create<SceneUser::kTempTest>(20);
pUser->tmpvars.create<SceneUser::kTempJumpVerification>(JumpVerification(10,10,101));

// 删除
pUser->tmpvars.release(SceneUser::kTempTest);
pUser->tmpvars.release(SceneUser::kTempJumpVerification);

// 取值
pUser->tmpvars.get<SceneUser::kTempTest>();
pUser->tmpvars.get<SceneUser::kTempJumpVerification>().x
pUser->tmpvars.get<SceneUser::kTempJumpVerification>().y

JumpVerification& jv = pUser->tmpvars.get<SceneUser::kTempJumpVerification>();
jv.x;
jv.y;

// 设值
pUser->tmpvars.get<SceneUser::kTempTest>() = 100;
pUser->tmpvars.get<SceneUser::kTempJumpVerification>().x = 100;
JumpVerification& jv = pUser->tmpvars.get<SceneUser::kTempJumpVerification>();
jv.x = 100;
jv.y = 100;

解析XML文件

发表于 2012-04-25   |   分类于 重构

动机

网游服务器端开发过程中,很多控制游戏的参数都不应该直接硬编码的。需要各种各样的配置和脚本文件,好处:

可以由策划或数值去随意修改,而不用动程序代码

配置可以动态加载,可以动态改变服务器运行中的参数,对已经发布的功能进行调整
一般,可采用:

  1. ini配置,一般用于window下的软件,游戏客户端有时会用到。比较简单,功能有限。
  2. Excel表格,数值策划特别喜欢用这个,可以做很多运算,生成数值,可以用VBA做更多的事情。
  3. xml配置,对于层次比较深、结构比较复杂的数据,应该算最佳选择了。

XML(eXtensible Markup Language)是一种标记语言,用于说明数据是什么,以及携带数据信息。主要用于:

  1. 丰富文件(Rich Documents):自定文件描述并使其更丰富
  2. 元数据(Metadata):描述其它文件
  3. 配置文件(Configuration Files):设定应用程序的参数

下面主要介绍一下对于xml文件作为服务器配置时候的解析方案。

问题

解析下面的XML文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<config>
<node1 prop1="100" prop2="i am string", prop3="2012-01-02 23:00:00"/>

<node2 id="1" prop1="100" prop2="string1"/>
<node2 id="2" prop1="100" prop2="string1"/>
<node2 id="3" prop1="100" prop2="string1"/>
<node2 id="4" prop1="100" prop2="string1"/>

<node3 prop1="100" prop2="string1"/>
<node3 prop1="100" prop2="string1"/>
<node3 prop1="100" prop2="string1"/>
<node3 prop1="100" prop2="string1"/>
</config>
  • node1 – 整个xml文件里面只有一个该节点
  • node2 – 有多个并且id属性可以作为它的键值,称之为节点map
  • node3 – 有多个名为node3的节点,但没有键值,称之为节点vector

一般的解决方案

使用XMLPaser(用libxml2封装的一个解析器)来解析(TinyXML也类似,DOM方式的都大同小异):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

XMLPaser xml;
if (xml.initFile("xxx.xml"))
{
xmlNodePtr root = xml.getRootNode("config");
if (root)
{
// 解析node1的prop1和prop2属性
struct NodeConfig{
int prop1;
string prop2;
} config;

xmlNodePtr node1 = root->getChildNode(root, "node1");
if (node1)
{

node1->getNodePropNum(node1, "prop1", &config.prop1, sizeof(config.prop1));
node2->getNodePropStr(node1, "prop2", config.prop2);
}

// 解析node2节点map
struct NodeConfig{
int prop1;
string prop2;
};
std::map<int, NodeConfig> nodemap;

xmlNodePtr node2 = root->getChildNode(root, "node2");
while (node2)
{
int id;
NodeConfig config;

node2->getNodePropNum(node2, "id", &id, sizeof(id));
node2->getNodePropNum(node2, "prop1", &config.prop1, sizeof(config.prop1));
node2->getNodePropStr(node2, "prop2", config.prop2);

nodemap[id] = config;

node2 = node2->getNextNode(node2, "node2");
}

// 解析node3节点vector
.....
}
}

坏味道分析

上面的代码,有几点不足之处,列举如下:

1> 代码重复

整个解析过程大同小异,一步一步遍历加载在内存中的节点树
节点或节点属性的名称、节点的层次结构不同的时候,就得写不同的代码,一般会采用复制代码的方式
使用不便

往往要写一个单件管理器,在服务器启动的时候加载该配置,然后在管理器里面把需要的数据结构都定义好
使用的时候,引用管理器里面的成员变量,代码既丑陋又容易出错

2> 不安全

节点名称、属性名称都是字符串,拼错了,运行时会发生逻辑错误

更好的解决方案

C++的结构与XML的对应树状结构对应起来,也就是数据绑定方案(Xml Data Binding)。自己曾经实现过一个Xml Data Binding库,名为xml_parser。具体用法如下:

step1: 编写一份描述XML结构的配置文件(也是一份XML文件,xml_parser.xml)

1
2
3
4
5
6

<config>
<node1 prop1="int" prop2="string" prop3="t_Date"/>
<node2 id="int" prop1="int" prop2="string" container_="map" key_="id"/>
<node3 prop1="int" prop2="string" container_="vector" />
</config>

step2: 生成binding类

xmlpg -f xml_paser.xml -o xml_parser.h

*step3: 应用程序中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

xml_config<xml_paser> xml;
if (xml.load("xxx.xml"))
{
// node1的prop1和prop2属性
int prop1 = xml.node1.prop1();
string prop2 = xml.node1.prop2();
t_Date date = xml.node1.prop3();

// node2节点map
for (xml_paser::Node2MapIter it = xml.node2.begin(); it != xml.node2.end(); ++ it)
{
int id = it->first;
int prop1 = it->second.prop1();
string prop2 = it->second.prop2();
}

// node3节点vector
for (size_t i = 0; i < xml.node3.size(); i ++)
{
int prop1 = xml.node3[i].prop1();
string prop2 = xml.node3[i].prop2();
}
}

更多解决方案


























方式 特征 开源库
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表格做配置的比较


























比较 XML Excel表格
结构 树状的层次结构 MxN的二维数组
适用性 信息具有层次性; 结构复杂 有一个键值可以索引的关联数组结构; 结构简单
不足之处 配置起来不是那么方便,每个节点名、属性名都必须指定 添加新列的时候,不一定所有行都用到该列属性,容易导致空间的浪费

代码的坏味道

发表于 2012-04-24   |   分类于 重构

代码坏味道:是指在代码之中潜在问题的警示信号。并非所有的坏味道所指示的确实是问题,但是对于大多数坏味道,均很有必要加以查看,并作出相应的修改。

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> 《重构手册》

每周一荐:流编辑器sed

发表于 2012-04-20   |   分类于 每周一荐

做网络游戏服务器端,最烦人的就是查后台日志。外面的玩家报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命令语法

sed命令的一般形式为:

[address[,address]][!]command[arguments]

sed将每个输入行拷贝到一个模式空间。sed指令由地址和编辑命令组成。如果命令的地址和模式空间中的行匹配,那么这个命令将被应用于匹配行。如果一个命令没有地址,那么它被应用于每个输入行。如果一个命令改变了模式空间的内容,后续的命令地址将被应用与模式空间中的当前行,而不是原始的输入行。

模式寻址

地址可以是一个行号或是由斜杠包含着的一个模式(/pattern/)。模式是用正则表达式表示的。另外,\n可以用来与模式空间(N命令的结果)的任意换行符匹配,但模式空间底部的换行符除外。
如果没有指定模式,相应的命令将被应用于所有的行。如果只指定一个地址,那么相应的命令将被应用于和这个地址匹配的行。如果指定了两个用逗号分隔的地址,这个命令将被应用于位于第一个和第二个地址范围之间的所有行。一些命令只接收一个地址,包括a,i,r,q和=。

在地址后面的”!”操作符使sed将相应的命令作用于所有与该地址不匹配的行。

大括号({})被sed用于地址的嵌套或对同一个地址应用多个命令。

1
2
3
4
[/pattern/[,/pattern/]] {
command1
command2
}

左大括号必须在一行的末尾,而右括号必须单独在一行。确保大括号后面没有空格。

sed中的正则表达式元字符

.
匹配除换行符以为的任意单个字符

*
匹配任意个(包括0个)在它前面的字符(包括由正则表达式指定的字符)

[…]
匹配广括号中的字符中的任意一个字符。其它所有的元字符被指定为其中成员时都会失去它们原来的含义。如果方括号第一个字符为脱字符(^)则匹配除了换行符和列出的那些字符以为的所有字符。连字符(-)用来表示字符的范围。如果其中第一个字符为右方括号(]),则表示它是这个类的成员。

{n,m}
它前面的某个范围内单个字符出现的次数(保护正则表达式指定的字符)。{n}将匹配n次出现,{n,}匹配大于等于n次出现的。

^
定位位于行起始位置后面的正则表达式。只有当^符号出现在正则表达式的起始位置时才是特殊的。

$
定位位于行末尾的正则表达式,只有当$符号出现在正则表达式的末尾时才是特殊的。

\
转义随后的特殊字符

( )
将包含在””和””之间的模式保存到一个特殊的保持空间。用这种方法在一行中可以最多保存9个模式。用转义序列”\1″ 到 “\9″ 可以重新使用它们。

\n
匹配前面””和””保存的第n个模式,这里n是一个从1到9的数字,前面保存的模式从行的左边开始编号。

&
当在替换字符串中使用时打印整个被匹配的文本

sed命令参考

: :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中的相应该字符。

每周一荐:《生命之树》、《水上音乐》

发表于 2012-04-13   |   分类于 每周一荐

电影:《生命之树》

《生命之树》由泰伦斯·马力克导演。故事开始于一个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日),他的《水上音乐》最早的原始手稿其实已丢失,最细致的复原本乃莱德利希所复制。这部作品根据亨德尔的三次夜游,编为三组。

POSA2:Wrapper Facade模式

发表于 2012-04-11   |   分类于 工具

意图

用更加简洁、健壮、可移植、可维护和内聚的面向对象接口,封装已存在的非面向对象API的函数和相关数据。

背景

使用已存在的非面向对象的API提供的服务或机制,开发可维护和进化的应用。

问题

  1. 简洁的代码比复杂的代码更加健壮,因为它易于理解和维护
  2. 可移植的软件可以容易的在不同的操作系统、编译器和硬件平台之间移植
  3. 提高软件可维护性,会降低开发生命周期的开销
  4. 高内聚的组件,易于学习、维护和增强

解决方案

结构:

POSA

行为:

POSA

实现:

  1. 识别已有的过程式API中内聚的抽象和关系
  2. 把内聚的函数放入包装外观类中(Wrapper Facade Class)
    2.1. 创建内聚类
    2.2. 把多个独立的函数放入其中
    2.3. 自动创建和销毁,可以考虑使用构造函数和析构函数
    2.4. 选择间接的等级
    2.5. 检查依赖与特定平台变化的代码
  3. 考虑以可控地访问实现细节(Escape-Hatch)
  4. 开发一套错误处理机制
  5. 定义相关的帮助类(可选)

已知应用

  • MFC
  • ACE
  • 效果

优势:

  1. 内聚的高层面向对象接口
  2. 易于移植和维护
  3. 模块化、可重用、可配置

不足:

  1. 功能缩水(Escape-Hatch可以解决)
  2. 性能降低(类函数内联可以解决)
  3. 编程语言和编译器的限制

MediaWiki编辑工具

发表于 2012-04-07   |   分类于 工具

1. vim+wikimedia.vim

简介:

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

vim

安装:

  1. 下载VIM插件:http://www.vim.org/scripts/script.php?script_id=1787
  2. 解压缩到$HOME/.vim/或$VIMDIR/vimfile/目录下面
  3. 确认:$HOME/.vim/syntax/mediawiki.vim
  4. 确认:$HOME/.vim/ftdetect/mediawiki.vim

2. WYSIWYG(CKEditor)

简介:

该MediaWiki扩展,使得用户可以以WYSIWYG方式编辑WIKI,使用了CKEditor。

安装:

  1. 下载WYSIWYG包:http://www.smwplus.com/index.php/Help:WYSIWYG_extension_1.6.0
  2. 解压缩
  3. 复制wysiwyg-1.6.0_0\extensions\WYSIWYG 到 ..\htdocs\mediawiki\extensions
  4. 配置MediaWiki的LocalSettings.php

    require_once(“$IP/extensions/WYSIWYG/WYSIWYG.php”);

  5. 设置可以使用者的权限

所有人可用:$wgGroupPermissions['*']['wysiwyg']=true;

注册用户可以:$wgGroupPermissions['registered_users']['wysiwyg']=true;

CKEditor

Microsoft Office Word Add-in For MediaWiki

简介:

可以将MS Word 2007/2012的文档直接保存为MediaWiki语法格式的文件。

安装:

下载WORD插件:http://www.microsoft.com/download/en/details.aspx?id=12298

运行插件安装程序

每周一荐:分布式计算的模式语言

发表于 2012-04-06   |   分类于 每周一荐

书籍:《面向模式的软件架构IV:分布式计算的模式语言》

简介

迄今为止,人们提出的软件开发模式有不少是关于分布式计算的,但人们始终无法以完整的视角了解分布式计算中各种模式是如何协同工作、取长补短的。构建复杂的分布式系统似乎成为了永远也无法精通的一门手艺。本书的出版改变了这一切。

本书是经典的POSA系列的第4卷,介绍了一种模式设计语言,将分布式系统开发中的114个模式联系起来。书中首先介绍了一些分布式系统和模式语言的概念,然后通过一个仓库管理流程控制系统的例子,介绍如何使用模式语言设计分布式系统,最后介绍模式语言本身。

分布式计算的模式语言

使用这一模式语言,人们可以有效地解决许多与分布式系统开发相关的技术问题,如

★ 对象交互

★ 接口与组件划分

★ 应用控制

★ 资源管理

★ 并发与同步

本书从实用角度展示了如何从现有的主要模式中整合出一门全面的模式语言,用于开发分布式计算中间件及应用程序。作为该领域在市场上唯一统揽全局的书,它将给读者带来醍醐灌顶的感觉!

笔记

以前总是困惑自己正在开发的游戏网络通信框架是前人怎么想出来的,看着底层那些乱七八糟的代码,即膜拜又感到不解。看到本书讲的一个模式:Half-Sync/Half-Async 顿时感觉清楚了许多。这本书对于很多模式都讲的不是很细致,只是粗略总结一下,详细的用法和介绍还是要去参考一些《面向模式的软件架构I》、《面向模式的软件架构II》、《设计模式:可复用面向对象基础》。对于网络框架的架构这本书和《面向模式的软件架构II》必备。

下面把书中提及到的分布式相关的模式列举出来:

从混沌到结构(From Mud To Structure )

  1. 领域模型(Domain Model)
  2. 分层(Layers)
  3. MVC模式(Model-View-Controller)
  4. PAC模式(Presentation-Abastraction-Control)
  5. 微内核(Microkernel)
  6. 反射(Reflection)
  7. 管道和过滤器(Pipes and Filters)
  8. 共享仓库(Shared Repository)
  9. 黑板(Blackboard)
  10. 领域对象(Domain Object)

分布式架构

  1. 消息机制(Messaging)
  2. 消息通道(Message Channel)
  3. 消息端点(Message Endpoint)
  4. 消息转换器(Message Translator)
  5. 消息路由(Message Router)
  6. 发布-订阅者(Publisher-Subscriber)
  7. 代理者(Broker)
  8. 客户端代理(Client Proxy)
  9. 请求者(Requestor)
  10. 调用者(Invoker)
  11. 客户端请求处理(Client Request Handler)
  12. 服务端请求处理(Server Request Handler)

事件分派(Event Demultiplexing and Dispatching)

  1. Reator
  2. Proator
  3. Acceptor-Connector
  4. Asynchronous Completion Token

接口划分(Interface Partitioning )

  1. Explicit Interface
  2. Extension Interface
  3. Introspective Interface
  4. 动态调用接口(Dynamic Invocation Interface)
  5. 代理(Proxy)
  6. 业务委托(Business Delegate)
  7. 外观模式(Facade)
  8. 复合方法(Combined Method)
  9. 迭代器(Iterator)
  10. 枚举方法(Enumeration Method)
  11. 批处理方法(Batch Method)

组件划分(Component Partitioning)

  1. 封装实现(Encapsulated Implementation)
  2. 整体-部分(Whole-Part)
  3. 组合模式(Composite)
  4. 主从模式(Master-Slave)
  5. Half-Object plus Protocol
  6. Replicated Component Group

应用控制(Application Control)

  1. 页控制器(Page Controller)
  2. 前端控制器(Front Controller)
  3. 应用控制器(Application Controller)
  4. 命令处理器(Command Processor)
  5. 模板视图(Template View)
  6. 转换视图(Transform View)
  7. 防火墙代理(Firewall Proxy)
  8. 授权(Authorition)

并发(Concurrency)

  1. Half-Sync/Half-Async
  2. Leader/Followers
  3. Active Object
  4. Monitor Object

同步(Synchronization)

  1. 守护挂起(Guarded Suspension)
  2. Future
  3. 线程安全接口(Thread-Safe Interface)
  4. 双检查锁(Double-Checked Locking)
  5. 策略锁定(Strategized Locking)
  6. 范围锁定(Scoped Locking)
  7. 线程指定存储(Thread-Specific Storage)
  8. 复制值(Copied Value)
  9. 常量值(Immutable Value)

对象交互(Object Interaction)

  1. 观察者(Observer)
  2. 双分配(Double Dispatch)
  3. 中间者(Mediator)
  4. 命令模式(Command)
  5. 备忘录模式(Memento)
  6. 环境对象(Context Object)
  7. 数据传输对象(Data Transfer Object)
  8. 消息(Message)

适配和扩展(Adaptation and Extension)

  1. 桥接模式(Bridge)
  2. 对象适配器(Object Adapter)
  3. 责任链(Chain of Responsibility)
  4. 解释器(Interpreter)
  5. 插入器(Interceptor)
  6. 访问者(Visitor)
  7. 修饰模式(Decorator)
  8. Execute-Around Object
  9. 模板方法(Template Method)
  10. 策略模式(Strategy)
  11. 空对象(NULL Object)
  12. 封装外观(Wrapper Facade)
  13. Declarative Component Configuration

模态行为(Modal Behavior)

  1. 状态对象(Objects for States)
  2. 状态方法(Methods for States)
  3. 状态集合(Collections for States)

资源管理(Resource Management)

  1. 容器(Container)
  2. 组件配置(Component Configurator)
  3. 对象管理器(Object Manager)
  4. 查找(Lookup)
  5. 虚拟代理(Virtual Proxy)
  6. 生命周期回调(Lifecyce Callback)
  7. 任务协调器(Task Coordinator)
  8. 资源池(Resource Pool)
  9. 资源缓冲(Resource Cache)
  10. Layzy Acquisition
  11. Eager Acquisition
  12. Partial Acquisition
  13. Activator
  14. Evictor
  15. Leasing
  16. 自动垃圾回收(Automated Garbage Collection)
  17. 计数句柄(Counting Handle)
  18. 抽象工厂(Abstract Factory)
  19. 构建者(Builder)
  20. 工厂方法(Factory Method)
  21. Disposal Method

数据库访问(Database Access)

  1. 数据库访问层(Database Access Layer)
  2. 数据映射(Data Mapper)
  3. 行数据网关(Row Data Gateway)
  4. 表格数据网关闭(Table Data Gateway)
  5. Active Record

以上模式详细内容还需要更加深入的应用才能很好的掌握。继续学习…

每周一荐:重构

发表于 2012-03-09   |   分类于 每周一荐

书籍:《重构:改善既有代码的设计》

简介

Martin Fowler和《重构:改善既有代码的设计》(中文版)另几位作者清楚揭示了重构过程,他们为面向对象软件开发所做的贡献,难以衡量。《重构:改善既有代码的设计》(中文版)解释重构的原理(principles)和最佳实践方式(best practices),并指出何时何地你应该开始挖掘你的代码以求改善。《重构:改善既有代码的设计》(中文版)的核心是一份完整的重构名录(catalog of refactoring),其中每一项都介绍一种经过实证的代码变换手法(code transformation)的动机和技术。某些项目如Extract Method和Move Field看起来可能很浅显,但不要掉以轻心,因为理解这类技术正是有条不紊地进行重构的关键。

重构

笔记

Composing Methods

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: 你想要把某个算法替换为另一个更清楚的算法,将函数本体替换为另外一个算法。

Moving Feature Between Object

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(外覆类)。

Organizing Data

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。

Simplifing Conditional Expression

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: 某一段代码需要对程序状态作出某种假设,以断言明确表现这种假设。

Making Method Calls Simpler

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: 面对一个调用者可预先加以检查的条件,你抛出了一个异常。修改调用者,使它在调用函数之前先做检查。

Dealing with Generalization

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。

Big Refactoring

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 于上海

1…789…13
David++

David++

123 日志
25 分类
65 标签
RSS
GitHub 知乎 微博 豆瓣
© 2007 - 2021 David++
由 Hexo 强力驱动
主题 - NexT.Pisces