12月 28, 2012

这篇文章要介绍的,是我作为专业程序员这些年来学到的能真正提高我的代码质量和整体工作效率的10件事情。

1. 永远不要复制代码

不惜任何代价避免重复的代码。如果一个常用的代码片段出现在了程序中的几个不同地方,重构它,把它放到一个自己的函数里。重复的代码会导致你的同事在读你的代码时产生困惑。而重复的代码如果在一个地方修改,在另外一个地方忘记修改,就会产生到处是bug,它还会使你的代码体积变得臃肿。现代的编程语言提供了很好的方法来解决这些问题,例如,下面这个问题在以前很难解决,而如今使用lambdas却很好实现:

///

/// 一些函数含有部分重复代码

///

void OriginalA()

{

DoThingsA();

// unique code

DoThingsB();

}

///

/// 另外一个含有部分重复代码的函数

///

void OriginalB()

{

DoThingsA();

// 没有重复的代码

DoThingsB();

}

现在我们重构含有部分相同代码的函数,用delegate模式重写它们:

///

/// Encapsulate shared functionality

///

/// User defined action

void UniqueWrapper(Action action)

{

DoThingsA();

action();

DoThingsB();

}

///

/// New implmentation of A

///

void NewA()

{

UniqueWrapper(() =>

{

// unique code

});

}

///

/// New implementation of B

///

void NewB()

{

UniqueWrapper(() =>

{

// unique code

});

}

2. 留意你开始分心的时候

当你发现自己在浏览facebook或微博、而不是在解决问题,这通常是一种你需要短暂休息的信号。离开办公桌,去喝一杯咖啡,或去跟同事聊5分钟。尽管这样做看起来有点反直觉,但长久去看,它会提高你的工作效率。

3. 不要匆忙赶任务而放弃原则

当带着压力去解决一个问题或修改一个bug,你很容易失去自制,发现自己匆匆忙忙,甚至完全忘了一直坚持的重要的测试过程。这通常会导致更多的问题,会让你在老板或同事眼里显得很不专业。

4. 测试你完成的代码

你知道你的代码能做什么,而且试了一下,它确实好用,但你实际上需要充分的验证它。分析所有可能的边界情况,测试在所有可能的条件下它都能如期的工作。如果有参数,传递一些预期范围外的值。传递一个null值。如果可能,让同事看看你的代码,问他们能否弄坏它。单元测试是到达这种目的的常规方法。

5. 代码审查

提交你的代码之前,找个同事一起坐下来,向他解释你做了哪些修改。通常,这样做的过程中你就能发现代码中的错误,而不需要同事说一句话。这比自己审查自己的代码要有效的多得多。

6. 让代码更少

如果你发现写了大量的代码来解决一个简单的问题,你很可能做错了。下面的boolean用法是一个很好的例子:

if (numMines > 0)

{

enabled=true;

}

else

{

enabled=false;

}

这时你应该写成这样:

enabled = numMines > 0;

代码越少越好。这会使bug更少,重构可能性更小,出错的几率更小。要适度。可读性同等重要,你可不能这样做而使代码丧失可读性。

7. 为优雅的代码而努力

优雅的代码非常的易读,只用手边很少的代码、让机器做很少的运算就能解决问题。在各种环境中都做到代码优雅是很难的,但经过一段时间的编程,你会对优雅的代码是个什么样子有个初步的感觉。优雅的代码不会通过重构来获得。当你看到优雅的代码是会很高兴。你会为它自豪。例如,下面就是一个我认为是优雅的方式来计算多边形面积的方法:

static public double GetConvexPolygonArea(Vector2[] vertices)

{

double area = 0;

for (int i = 0; i < vertices.Length; i++)

{

Vector2 P0 = vertices[i];

Vector2 P1 = vertices[(i + 1) % vertices.Length];

area += P0.Wedge(P1);

}

return area / 2;

}

8. 编写不言自明的代码

勿庸置疑,注释是编程中很重要的一部分,但能够不言自明的代码跟胜一筹,因为它能让你在看代码时就能理解它。函数名变量名要慎重选择,好的变量/方法名字放到语言语义环境中时,不懂编程的人都能看懂。例如:

void DamagePlayer(Player player, int damageAmount)

{

if (!player.m_IsInvincible && !player.m_IsDead)

{

player.InflictDamage( damageAmount );

}

}

能自我说明的代码不能代替注释。注释是用来解释“为什么”的,而自我说明的代码是来描述“是什么”的。

9. 不要使用纯数字

直接把数字嵌入代码中是一种恶习,因为无法说明它们是代表什么的。当有重复时更糟糕——相同的数字在代码的多个地方出现。如果只修改了一个,而忘记了其它的。这就导致bug。一定要用一个命名常量来代表你要表达的数字,即使它在代码里只出现一次。

10. 不要做手工劳动

当做一系列动作时,人类总是喜欢犯错误。如果你在做部署工作,并且不是一步能完成的,那你就是在做错事。尽量的让工作能自动化的完成,减少人为错误。当做工作量很大的任务时,这尤其重要。

11. 避免过早优化

当你要去优化一个已经好用的功能代码时,你很有可能会改坏它。优化只能发生在有性能分析报告指示需要优化的时候,通常是在一个项目开发的最后阶段。性能分析之前的优化活动纯属浪费时间,并且会导致bug出现。

好吧,我说是10个,但你却得到了额外赠送的一个!

这些就是我要说的,我希望它们能帮助你改进编程开发过程。

下次再见!祝快乐!

Cheers, Paul.

Tags: ,,,.
12月 27, 2012

“别活得太认真,你逃不出你的命运。” – Van Wilder

这句流行的名言,对于如今的20几岁的青年,看起来就是让他们没有任何目标的自由生活,尽可能的为所欲为,不接受任何指引。作为一个工作狂、一个创业者,从我自学校毕业,这句话就没有和我产生过共鸣,不仅如此,我甚至还十分慎重的过好每一分钟,因为我知道,20几岁,这是一生中一段非常独特的时间,是人生的其它时间代替不了的。

一个朋友最近推荐我一本书,《The Definining Decade: Why your twenties matter – and how to make the most of them now》.读完之后,我前所未有的认识到了我20几岁这段时间的重要性,它改变了我如何度过20几岁余下这最后几年的人生计划。

出自一位临床心理学家之手,这本书触及到了我们生活的各个关键点:工作,爱情和我们的身体。里面有很多的思想都是非常宝贵的,但最触动我的是下面这三个:

1) 你的20几岁为你之后余下的人生事业打下了成功的基础。

无论你做多长时间的酒吧招待或餐厅服务员,用多长时间在你家后院想你的“创业想法”,你都是在浪费生命,阻挡自己朝着未来更成功、更幸福的生活前进。

也许在星巴克当服务员的日子很快乐,但你的收入能养活你未来的孩子吗?更重要的,你真的打算今后10年仍然干这个吗?如果不是,那你目前的工作是否有助于你迈进你想要的工作的门槛吗?如果你不是Facebook的创始人,也许你应该考虑到一家创业公司开创自己的职业道路,而不是做一个永远都飞不起来的雏鸟。

人都是一步一步走出来的。从刚开始去做那些辛苦的低收入的,能力上绰绰有余的工作,慢慢得到越来越多的机会。我有硕士学位,仍然需要抽出时间来去实习,才能进入科技界的大门。实习工作让我在一家创业公司找到了全职工作,公司的创始人向我引荐了Hiten Shah,他成为了我的导师,现在,我给他的公司开发产品KISSmetrics。他是我见到的最成功的经营自己事业的人,他总是从小事做起。

经验:不要拖延开展自己的事业。越早进入一个行业或成为你希望的角色,越早你能获得你满意的职业生涯。

2) 据统计,女人应该在35岁之前生孩子。

根据作者所说,女人怀孕的能力在35岁左右的时候开始急剧下降。更糟糕的是,超过35岁,四分之一的女性会流产。这是一个令人震惊的数据,让我想象到了一副好不容易受孕却最终流产的场景。没有人想要这样的结果。

20几岁的时候成立家庭的想法和离我很遥远。我把事业放到了其它所有事情之前。但我知道,我最终是要组织一个家庭的。当知道了这些统计数据后,我们更加认清了我做的选择意味这什么、我现在所做的是为了什么。这正好说明了为什么这么多的创业者会在30左右开始建立家庭。

经验:如果组建一个家庭是你的人生目标,那你的时间会比你想象的要少。如果你是一个男的,想娶一个和你年纪相当的女人,时间的脚步走催促着你,同样也在催促着她。

3) 你的大脑中20多岁时成熟定型。

我一直以为人的大脑在孩子时就已经定型,从10几岁到三四十岁之间不会有多少变化,40四十以后开始萎缩。但事实证明,人的前大脑皮层会在20几岁时发生巨大的变化,30几岁时趋于稳定。维基百科上说:

额叶(frontal lobes)职能涉及到从当前的活动中认识将来的后果,影响你在好的活动和不好的活动之间做出的选择,忽视或制止不可接受的社会反应,在一些事情和事件间辨别它们的相似或不同。

作为一个有志向开创一个大型公司的人,我认识到现在就需要培养大量的用来领导这样一个公司的各种技能,因为再过几年你就很难或者根本不可能像现在这样学习知识。我听到很多的创业者在公司做到一定规模后就没有能力(或不想)管理这个公司,我现在正面临着提高自己和培养自己的巨大挑战。

经验: 不管你有什么样的人生或事业目标,你需要认识到,20多岁时获得的技能和人格特性将会决定着你余生的众多能力。

很少有书能让我像读这本书这样做如此多的人生反省。如果这些事情你也感兴趣,我高度推荐你看一看。

Tags: ,,,.
12月 26, 2012

我的新书的内容被我砍掉了两章,因为它们太让我觉得不堪。

我的员工发动暴动要除掉我,赶我走,他们败坏了公司的文化,使公司变成了权益为先的腐败温床,只关注他们自己的利益,而不是把客户放在第一位。那两章内容就是在发泄我对这些恐怖的员工的不满的细节。

多年之后,我仍然对这些邪恶的小子们的所作所为感到愤怒。就像所有感觉被做为牺牲品或被错怪的人一样,我需要渲泄——说出站在我的立场上的故事。或者是我认为的故事。

你想知道我砍掉这两种内容的真正原因吗?

我认识到,这都是我的错。

是我,让公司的文化变得腐烂。

是我,置若罔闻,没能防患于未然。

是我,疏离群体,没有认真管理和培训中层干部。

是我,用不停变换的想法命令让他们困惑不堪,没能让他们参与决策的过程。

是我,只是发号施令,以为他们会照命令完成,没有跟进去检查完成情况。

是我,用人失误,没有在这方面用心。

(我还可以列出另外20条,但你应该知道我的意思了。)

认识到这完全是我的错,这种感觉真好!

这是比宽恕更好的结果。如果你宽恕他们,说明你仍然认为自己是牺牲者,你认为仍然是他们的错,而你在慷慨的宽恕他们的恶行。

但当认定这是自己的错时,这种感觉很神奇!现在自己不是被错怪。他们只是在你创造的形势下做他们该做的事。他们只是在你编织的故事里添加高潮部分。

太强大了!现在我觉得自己变成了一个新的英雄,一下子发现了自己的强大之处。我就是那个强有力的人,让这一切发生了,自己犯了错误,然后从中学到了教训。完全在自己的掌握之中,没有任何可抱怨的。

这种价值观让人感觉是如此之好,我欣然的决定让我的余生都遵行“一切都是我的错”处事原则。

这种类似“人性本善”的思想,信比不信更有益,虽然有些例外的情况,但总比全然不信要好。

原谅那个偷走我9000美元的人吗?对,这是我的错。我早该认真对待他提出的诉求。

6年的恋人突然抛弃了我,原谅?对,这是我的错。是我让我们的关系停滞不前。

今天有人对我无礼了,原谅?对,是我的错。我事先应该让他们心情好一些。

不喜欢政府?我的错。我应该积极参政议政,去改变这个世界。

这是什么的力量?

是的,用“用于承担责任”这个词更准确,但这是一个很严肃的6字短语,远不如“一切都是我的错”的经验论更有趣,而且我还可以吟唱nirvana的歌“都是我的错”。

试一下。起立,打开窗户,面向外面的世界,大声说,“一切都是我的错!”

当想起自己的倒霉不幸的事情时,对自己说一遍。

神奇,不是吗?

这种力量看起来对你有好处。

Tags: ,,,,.
12月 25, 2012

我已经使用Python编程有多年了,即使今天我仍然惊奇于这种语言所能让代码表现出的整洁和对DRY编程原则的适用。这些年来的经历让我学到了很多的小技巧和知识,大多数是通过阅读很流行的开源软件,如Django, Flask,Requests中获得的。

下面我挑选出的这几个技巧常常会被人们忽略,但它们在日常编程中能真正的给我们带来不少帮助。

1. 字典推导(Dictionary comprehensions)和集合推导(Set comprehensions)

大多数的Python程序员都知道且使用过列表推导(list comprehensions)。如果你对list comprehensions概念不是很熟悉——一个list comprehension就是一个更简短、简洁的创建一个list的方法。

>>> some_list = [1, 2, 3, 4, 5]

>>> another_list = [ x + 1 for x in some_list ]

>>> another_list

[2, 3, 4, 5, 6]

自从python 3.1 (甚至是Python 2.7)起,我们可以用同样的语法来创建集合和字典表:

>>> # Set Comprehensions

>>> some_list = [1, 2, 3, 4, 5, 2, 5, 1, 4, 8]

>>> even_set = { x for x in some_list if x % 2 == 0 }

>>> even_set

set([8, 2, 4])

{1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False, 10: True}

>>> # Dict Comprehensions

>>> d = { x: x % 2 == 0 for x in range(1, 11) }

>>> d

在第一个例子里,我们以some_list为基础,创建了一个具有不重复元素的集合,而且集合里只包含偶数。而在字典表的例子里,我们创建了一个key是不重复的1到10之间的整数,value是布尔型,用来指示key是否是偶数。

这里另外一个值得注意的事情是集合的字面量表示法。我们可以简单的用这种方法创建一个集合:

>>> my_set = {1, 2, 1, 2, 3, 4}

>>> my_set

set([1, 2, 3, 4])

而不需要使用内置函数set()。

2. 计数时使用Counter计数对象。

这听起来显而易见,但经常被人忘记。对于大多数程序员来说,数一个东西是一项很常见的任务,而且在大多数情况下并不是很有挑战性的事情——这里有几种方法能更简单的完成这种任务。

Python的collections类库里有个内置的dict类的子类,是专门来干这种事情的:

>>> from collections import Counter

>>> c = Counter(‘hello world’)

>>> c

Counter({‘l’: 3, ‘o’: 2, ‘ ‘: 1, ‘e’: 1, ‘d’: 1, ‘h’: 1, ‘r’: 1, ‘w’: 1})

>>> c.most_common(2)

[('l', 3), ('o', 2)]

3. 漂亮的打印出JSON

JSON是一种非常好的数据序列化的形式,被如今的各种API和web service大量的使用。使用python内置的json处理,可以使JSON串具有一定的可读性,但当遇到大型数据时,它表现成一个很长的、连续的一行时,人的肉眼就很难观看了。

为了能让JSON数据表现的更友好,我们可以使用indent参数来输出漂亮的JSON。当在控制台交互式编程或做日志时,这尤其有用:

>>> import json

>>> print(json.dumps(data)) # No indention

{“status”: “OK”, “count”: 2, “results”: [{"age": 27, "name": "Oz", "lactose_intolerant": true}, {"age": 29, "name": "Joe", "lactose_intolerant": false}]}

>>> print(json.dumps(data, indent=2)) # With indention

{

“status”: “OK”,

“count”: 2,

“results”: [

{

"age": 27,

"name": "Oz",

"lactose_intolerant": true

},

{

"age": 29,

"name": "Joe",

"lactose_intolerant": false

}

]

}

同样,使用内置的pprint模块,也可以让其它任何东西打印输出的更漂亮。

4. 创建一次性的、快速的小型web服务

有时候,我们需要在两台机器或服务之间做一些简便的、很基础的RPC之类的交互。我们希望用一种简单的方式使用B程序调用A程序里的一个方法——有时是在另一台机器上。仅内部使用。

我并不鼓励将这里介绍的方法用在非内部的、一次性的编程中。我们可以使用一种叫做XML-RPC的协议 (相对应的是这个Python库),来做这种事情。

下面是一个使用SimpleXMLRPCServer模块建立一个快速的小的文件读取服务器的例子:

from SimpleXMLRPCServer import SimpleXMLRPCServer

def file_reader(file_name):

with open(file_name, ‘r’) as f:

return f.read()

server = SimpleXMLRPCServer((‘localhost’, 8000))

server.register_introspection_functions()

server.register_function(file_reader)

server.serve_forever()

客户端:

import xmlrpclib

proxy = xmlrpclib.ServerProxy(‘http://localhost:8000/’)

proxy.file_reader(‘/tmp/secret.txt’)

我们这样就得到了一个远程文件读取工具,没有外部的依赖,只有几句代码(当然,没有任何安全措施,所以只可以在家里这样做)。

5. Python神奇的开源社区

这里我提到的几个东西都是Python标准库里的,如果你安装了Python,你就已经可以这样使用了。而对于很多其它类型的任务,这里有大量的社区维护的第三方库可供你使用。

下面这个清单是我认为的好用且健壮的开源库的必备条件:

好的开源库必须…

包含一个很清楚的许可声明,能适用于你的使用场景。

开发和维护工作很活跃(或,你能参与开发维护它。)

能够简单的使用pip安装或反复部署。

有测试套件,具有足够的测试覆盖率。

如果你发现一个好的程序库,符合你的要求,不要不好意思————大部分的开源项目都欢迎捐赠代码和欢迎提供帮助——即使你不是一个Python高手。

Tags: ,,.

Welcome to DoNews Blog. This is your first post. Edit or delete it, then start blogging!