已将此博客搬家至百度空间:http://hi.baidu.com/cly84920/blog ,此博客已不再更新。
关于“责任”和“权力”:
“责任不能被指派,只能被接受。如果有人试图给你责任,只有你自己能够决定是否负这个责任。”
这点和scrum的“团队自管理”异曲同工,项目不是由项目经理或者什么人去指派任务的,项目经理只是牧羊犬,而产品经理只是项目需求方,任务仅限于提出 需求和对需求排出优先级。至于本轮次中的任务,完全由开发团队自行决定如何安排开发任务,也只有开发团队能决定本轮次接受多少任务。
是的,这涉及到两个问题:“权力和责任问题”,“勇气问题”。
开发团队这时本身需要“勇气”,客观地面对需求,做出切实的时间成本估算,xp的“勇气”价值观在这时需要表现出来。
客观地,开发团队也需要“权力和责任问题”的明确来对它们进行保护。
“责任和权力需要并行。两者错位会扭曲团队的沟通。当一个过程专家告诉我该怎样工作,却不承担这些工作及其后果的时候,权力和责任就错位了。我俩都无法从一个理智的角度出发来看待或使用那些有助于我们改进的反馈。另外,错位还要付出情绪方面的成本。”
说得太对了,责任和权力需要并行。如果一个人对其它人指手划脚却不对自己的言论负责,这完全没法工作了。老实说,我吃过这方面的亏,所以对此深恶痛绝。开 发人员绝不能估息这种情况出现,如果有,一定要进行阻止,哪怕吵架!《没人把程序员当回事儿》的作者说得对,工程师的注意力全放在工程本身上了,无暇顾及 人事上的陷阱,不知道谁在背后捅刀子,所以老吃这方面的亏。如果不想把精力全花在这种项目之外的人事间的消耗上,就一定要明确责任和权力的问题。只有这样 才能建立起一个健康的开发制度。
就《编写高质量代码——web前端开发修炼之道》,我会于8月14日在php china做一次交流分享活动。
活动详情见:http://bbs.phpchina.com/thread-190515-1-1.html
或 http://www.douban.com/event/12334828/
请大家多支持。
1) 在html中带滚动条的容器,是通过对容器设置overflown属性实现的,在wxpython中带滚动条的容器是一个单独的容器,而不是通过style设置的方式。这个容器就是wx.ScrolledWindow,基本上用wx.Panel的地方都可以换成它。
2) 对于对象框,你可以使用样式wx.STAY_ON_TOP将对话框显示在系统中任何其它窗口的上面,包括系统窗口和wxPython应用程序窗口。
3) wx.ProgressDialog用于显示进度条。其最有用的方法是Update()。
4) wx.FileDialog为文件对话框。wx.FileDialog的两个最重要的样式标记是wx.OPEN和wx.SAVE,它们表明对话框的类型并影响对话框的行为。
用于打开文件的对话框有两个标记,它们进一步影响对话框的行为。wx.HIDE_READONLY标记灰化复选框,使用户以只读模式打开文件。wx.MULTIPLE标记使用户可以在一个目录中选择打开多个文件。
保存文件对话框有一个有用的标记wx.OVERWRITE_PROMPT,它使得保存文件时,如果有相同的文件存在,则提示用户是否覆盖。
你可以使用方法GetPath()来得到用户的选择,该函数的返回值是字符串形式的文件全路径名。如果对话框是一个使用了wx.MULTIPLE标记的打开对话框,则用GetPaths()代替GetPath()。
5) 如果用户想去选择一个目录而非一个文件,使用wx.DirDialog。该对话框的样式标记wx.DD_NEW_DIR_BUTTON给对话框一个用于创建目录的一个按钮。这个标记在老版的微软Windows中不工作。
6) 对话框与框架之间的另一区别是对话框有内建的验证器行为,而框架没有。如果你喜欢将验证器用于不在对话框内的控件,那么调用父窗口的Validate()方法。如果父窗口已设置了wx.WS_EX_VALIDATE_RECURSIVELY额外样式,那么所有的子窗口的Validate()方法也被调用。如果任一验证失败,那么Validate返回False。
1) wx.SpinButton设置style为wx.SP_ARROW_KEYS可以支持键盘上下键,设置wx.SP_WRAP可以使数值上下限循环起来。它可以响应EVT_SPINCTRL和EVT_TEXT事件。除了GetValue()外,它还可以GetMin()和GetMax()。
2) wx.Gauge可以用来生成一个进度表。它没有事件。设置style为wx.GA_HORIZONTAL或wx.GA_VERTICAL可以控制进度表水平或竖起显示。如果是windows平台上,那么样式wx.GA_PROGRESSBAR给你的是来自Windows工具包的本地化的进度条。
3) wxpython中的复选框是通过wx.CheckBox来实现的,和html不同的是wx.CheckBox本身自带label属性,其文本并不需要一个单独的label容器。复选框没有样式标记,但是它们产生属于自己的独一无二的命令事件:EVT_CHECKBOX。wx.CheckBox的开关状态可以使用GetValue()和SetValue(state)方法来访问,并且其值是一个布尔值。IsChecked()方法等同于GetValue()方法,只是为了让代码看起来更易明白。
4) 单选框在wxpython里有wx.RadioButton和wx.RadioBox两种类型可以进行支持。其中wx.RadioButton是针对单个单选框的,更加灵活,而wx.RadioBox是用来批量处理一组单选框的,而且在排列上,会让这一组单选框组成一个矩形。
5) wx.RadioButton有wx.RB_GROUP样式,此样式声明该按钮位于一组单选按钮开头。一组单选按钮的定义是很重要的,因为它控制开关行为。当组中的一个按钮被选中时,先前被选中的按钮被切换到未选中状态。在一个单选按钮使用wx.RB_GROUP被创建后,所有后来的被添加到相同父窗口部件中的单选按钮都被添加到同一组,直到另一单选按钮使用wx.RB_GROUP被创建,并开始下一个组。在例7.11中,第一个单选按钮是使用wx.RB_GROUP声明的,而后来的没有。结果导致所有的按钮都被认为在同一组中,这样一来,敲击它们中的一个时,先前被选中按钮将关闭。
6) 使用wx.LB_SORT样式,可以设置列表中的项按字母顺序排列。包括wx.ListBox、wx.CheckListBox、wx.Choice和wx.ComboBox。
7) 框架和对话框都是wxpython中的顶级窗口。它们很相似,但其关闭事件,默认的行为却是不太一样的。
对于EVT_CLOSE事件,如果你不声明你自己的事件处理器,那么默认的行为将被调用。默认的行为对于框架和对话框是不同的。
1、默认情况下,框架处理器调用Destroy()方法并删除该框架和它的所有的组件。
2、默认情况下,对话框的关闭处理器不销毁该对话框——它仅仅模拟取消按钮的按下,并隐藏对话框。该对话框对象仍继续存在在内存中。因此,如果需要的话, 应用程序可以从它的数据输入部件获取值。当应用程序完成了对对话框的使用后,应该调用对话框的Destroy()方法。
所以框架可以用Close()方法,但对话框推荐用Destroy()方法。
8)在wxPython中,有三种查找子窗口部件的方法,它们的行为都很相似。这些方法对任何作为容器的窗口部件都是 适用的,不单单是框架,还有对话框和面板(panel)。你可以通过内部的wxPython ID查寻一个窗口部件,或通过传递给构造函数的名字(在name参数中),或通过文本标签来查寻。文本标签被定义为相应窗口部件的标题,如按钮和框架。
这三种方法是:
- 1.wx.FindWindowById(id, parent=None)
1.wx.FindWindowByName(name, parent=None)
1.wx.FindWindowByLabel(label, parent=None)
这三种情况中,parent参数可以被用来限制为对一个特殊子层次的搜索(也就是,它等同于父类的Find方法)。还有,FindWindowByName()首先按name参数查找,如果没有发现匹配的,它就调用FindWindowByLabel()去查找一个匹配。
2) 定义样式除了可以使用 | ,还可以使用 ^ 。例如,如果要定义一个不能缩放的窗口,style可以设置为wx.DEFAULT_FRAME_STYLE ^ (wx.RESIZE_BORDER | wx.MINIMIZE_BOX |wx.MAXIMIZE_BOX)
3) 在wxpython中的标准弹窗和js中的有所区别,js中的confirm和alert是两种不同的弹窗,在wxpython中是同一种弹窗
wx.MessageDialog,至于显示的按钮数量是通过style属性定义的。另外,prompt弹窗在wxpython中对应的是wx.TextEntryDialog。有意思的是,wxpython中的所有弹窗,返回值都是数值类型。
4) wx.StaticText虽然可以设置长宽,但一旦用SetLabel()方法改变其内容,其长宽就会自动进行调整,刚刚包下新的文本。可以通过设置style为wx.ST_NO_AUTORESIZE来防止这种自动调整。
5) wx.StaticText不接受鼠标事件。
6) 设置style=wx.TE_PASSWORD,可以让wx.TextCtrl变成密码输入框。
7) 设置style=wx.TE_READONLY,可以让wx.TextCtrl变成只读输入框。
8) 如果文字过长,wx.TE_DONTWRAP设置强制不换行,wx.TE_LINEWRAP以字符为界断行,wx.TE_WORDWRAP以单词为界断行。
9) wx.TextCtrl可以响应wx.EVT_TEXT事件,当输入的内容有变化时即可触发此事件。无论是用户输入引起的变化,还是通过setValue()引起的变化都会触发。
10) 按钮的SetDefault()方法可以让设置按钮为默认按钮,点击回车时默认点击此按钮。
11) 位图按钮的类叫wx.BitmapButton。关于位图按钮有几个有趣的特性。首先,一个样式标记wx.BU_AUTODRAW,它是默认的。如果该标记是打开的,那么位图将带有一个3D的边框,这使它看起来像一个文本按钮,并且按钮比原位图大几个像素。如果该标记是关闭的,则位图被简单地绘制为按钮而没有边框。通过设置style=0按钮关闭默认设置,它没有了3D的效果。 默认情况下,给wxPython传递单个位图作为主显示的位图,在当按钮被按下或获得焦点或无效时,wxPython自动创建一个标准的派生自主显示的位图的位图作为此时显示在按钮上的位图。如果自动创建的位图不是你想要的,你可以使用下面的方法: SetBitmapDisabled(), SetBitmapFocus(),SetBitmapLabel(), 和SetBitmap-Selected()显式地告诉wxPython你要使用哪个位图。这些方法都要求一个wx.Bitmap对象作为参数,并且它们都有相应的get*()方法。
12)在wx.ToggleButton与父类wx.Button之间只有丙个区别: 1、当被敲击时,wx.ToggleButton发送一个EVT_TOGGLEBUTTON事件。 2、wx.ToggleButton有GetValue()和SetValue()方法,它们处理按钮的二进制状态。
=====================================================
编程对很多人来说有点神秘。这就造成了在公司内部,人们对编程的事情产生了很多怀疑和疑惑。 通常,当你不了解一个东西是怎样做成的时,你只能说:可能是这样吧。 如果你从没见过工地,你也许会认为几个星期就能建出一栋大楼。 事实上,在这样的时间内是可以完成这栋建筑的,只是能不能用就不知道了。 如果你看过房子如何建造,跟踪它的建造过程,你能从物理实物看到地基如何浇灌,钢架结构如何搭成,等等。 但给电脑编写程序,或建设一个网站却是不可见的。
除了程序员外,程序代码对其他人来说是接触不到 的。程序的运行好像是大幕后发生的魔术戏法。 只有开发团队的成员才能知道程序是什么,怎么工作的,不能干什么。 从程序员的角度看问题,你就能得到最好的开发结果、项目评估数据和进度更新。 很多的A型性格的人对此不以为然,但事情并没有那么简单。
当 客户提出他们想要什么东西,而且要在什么时候完成时,问题就开始出现了。 销售人员希望做成这笔交易。拜托,请告诉客户,他们的想法不现实,这个生意做不了。 这样做下去只能导致一场灾难。 我曾看见过销售部门把估算的工期消减一半,四处花钱去达成他们的销售,完成他们的任务。 直到最后有一天,事情的发展看起来都是程序员的错造成的。他们这样做结论是因为程序员是最容易责备的。
程 序员们在学校里没有学过办公室政治学。他们应该学,当然这是另外一个话题了。 作为一个程序员,他需要集中精力,沉着的思考,去开发出清晰好用的程序。这是个困难的事,需要用去你全部精力。 程序员们没有时间去理会是谁背后给了自己一刀。可销售部门玩的这些把戏却有严重的后果。
我的前一个公司,一个百万美元的项目,热热闹闹的,像烟火一样,短暂的光华后就落地地上了。 什么原因?是这个公司指使程序员们每周工作70小时以上去完成客户专横的进度表导致的?还是销售部门对客户言听计从导致的?
我也不认为开发人员没有任何责任。如果你看过电视剧Seconds From Disaster(CSDN编者注:美国国家地理频道的一个系列节目,讲述了各种人为和自然灾害),你会明白,灾难的发生是一群人都没有做自己该做的事情导致的。 但是,我可看见程序员们都在做他们自己的工作。而其他人都在干什么呢?
那么,公司是怎么认为的?他们解雇或开除了所有的程序员。然而整个销售部却没事。 这次惨败的死亡之旅后,也没人愿意留在那里了。
程 序员被打入地狱的过程都是有一个个的“遵命”铺就的。 为了对得起自己,对得起自己的职业,程序员应该警惕那些危险的事情。 评估分析,评估工作通常会花掉很多的精力。据我所知,这个比任何事情都要费神,它需要你从多个层面去考虑整个事情。 不幸的是,我曾亲身经历优秀的评估报告被驳回或修改。 评估的越符合实际,招惹的众议越多。
把符合实际的预期报告告诉用户是个困难的事情。这会使生意的成交增加困难。 程序员在承担其他人冒险的后果。程序员的工作从来不轻松。 事实上,程序员是一个公司里对这个事情看的最清楚的人。他们懂编码,知道需求业务。他们也许不善于和客户打交道,但他们却真正知道项目应该怎么做。
重视你们的程序员。他们不仅仅是个技工,他们也是懂业务的。 他们能凭借自己的经验判断出,是谁在为了留住客户而胡乱夸下海口。
这篇文章的英文原文曾经在Reddit等开发人员网站引发很多争议。
得到回应最多的评论是:“企业IT项目真不是人干的。” 有人用企业IT=“IT+governance+audit+project+management+estimates+requirements+ J2E+cobol+xml+corba+the+microsoft+stack+sharepoint+biztalk+the+ ibm+oracle+bea+sap+stack+eai+esb+soa+bpm+6sigma+thedailywtf”的公式表示认同。
有人评论,合同是程序员和软件开发公司最好的武器。马上有人回应:开发人员往往不是签合同的人,很多项目合同协商过程中都没有开发人员参与。有人则表示,自己的很多客户都是公司内部的部门,这招不灵。
的确,我们都知道,软件需求变化叵测、难以捉摸,预先的项目估算往往很难准确,完全依赖合同,并不现实。
怎么解决这个问题呢?有人提议让公司的高层了解更多软件开发原理和流程,让销售人员以质量为由尽量争取宽松的预算。有同学很快尖锐指出,“很多情况下,都是钱的问题。这是没法解决的。”
你的看法呢?你日常工作中是怎样处理这一问题的?
==========================================================
[感想]
工程师总是喜欢另起一套东西,一套自己熟悉的东西。对于过往的代码,往往会因为可读性差而决定放弃使用他们,更宁愿重头再来重写一份代码。
其实很能理解。代码在长久的维护过程中,慢慢腐坏变得越来越丑陋是司空见惯的事,对于这样的代码,团队的老成员都会头皮发麻,团队新手进来就更难以溶入了。非常多的工程师在代码维护成本高达至不堪忍受时,都会想要放弃原代码从头再来。
还真是。我在接手站长天下代码,要将其改成淘宝外店时,也是觉得“非重构不可,我宁可从头再来”。接手web聊天室时,我、纯华、葛岩都非常想要重构,想要一份自己能够“掌控”的代码,而不是基于别人的代码进行维护。
代码为什么会变得越来越丑陋呢?前期构思不充分固然是可能的原因之一,但更大的可能性是因为有太多意料外的需求和bug。这不是前期构思不充分的错,前期不可能预测至所有的问题,只要主体架构搭得合理,前期的构思就算是成功的,小的细节问题是一定会存在的。这些细节问题只能在开发过程中发现,别无他法。每发现一个这种问题,就必须打一次补丁,补丁多了,代码就变得可读性越来越差。一直到那个无法再忍受的临界值达到为止,代码会越来越腐坏。然后终于要开始重构了,重头再来。
就像Joel Spolsky说的,重构的成本是非常大的。如果重构的团队还是之前那支团队,那还相对较好,如果团队是支新的团队,那效果未必好。你无法保证你的代码不会犯过去团队相同的错误,你在选择重构时,
丢弃了过往的累赘,但也丢弃了过往的经验。尽管过去的代码十分丑陋,但他们确确实实可以正常地在运转。如果要重构,你能保证你的代码不会像过去团队的代码一样,慢慢变丑陋吗?你面临同样的风险,而且,过去团队已经付出的时间成本,你还要再次付出一遍。
没有人愿意写出难看的代码,你们团队的实力也不一定会比过去团队更高明。在项目初期,大家的愿望一定都是好的,代码的组织结构也一定是满意的。在长期的维护过程中,意料外的事件是不可避免的。重要的不是重构,重要的是如何防止代码腐坏!这是最治本的,永远别说“稍后再来修改它”,因为“稍后”等于“永远”,代码就是在一次次这样的“稍后”中腐坏的。马丁说得对,永远要保证“签出”的代码比“签入”时更干净、更整洁。永远保证你的代码是高品质的输入,不要留任何坏的东西在代码库里,它会像破窗户原理一样,让你的代码越来越破,永远别打破那第一扇窗户。
如果代码已经很乱了,怎么办?重写吗?如果你的团队还是之前那支团队,你们已经有第一版的“经验”在了,我觉得是可以去重构的,这个风险在可控范围。但如果之前版本并非由你们来做,那么不要去重构,而是去读他们的代码,再难读也要去读,然后再去优化他们,而非重构。
也就是说,如果项目要交接给其他团队,不要寄希望于日后的新团队的重构,那成本实在太大。应该在交接的时候,交接清楚,我知道如果开发周期长,这个交接的过程并不容易,但即使如此,这个成本也是最小的,交接的过程不妨给长一些,保证质量。
2) 调查继承。issubclass(subclass,superclass)可以判断subclass是否superclass的子类。
3) 调查超类。通过subclass.__bases__ 可以得到subclass的超类组成的列表。因为python是支持多继承的,所以超类可能有多个。
4) 查检对象是否某个类的实例。isinstance(s,subclass)、isinstance(s,superclass)。 注意,instance()方法是可以往上追朔的。如果只能知道某对象属于哪个类(不算超类),可以使用s.__class__属性。另外,type()方法也可以帮助查看对象的类型。
5) 类变量和实例变量。python中的类变量有点奇怪,如果实例中没有设置同名的实例变量,那么默认其同名实例变量值为类变量的值。但只可读,不可改。如果修改,只会改自己的实例变量,影响不到类变量。
6) python事实上并不真正支持私有变量。其私有变量是通过在方法名前加双下划线来实现的,虽然无法在实例中直接访问,但事实上python只是将所有以__为前辍的方法将其方法名变成了_类名_方法名的形式,只是个魔法,并非真的让其私有了,通过_类名_方法名还是可以访问到的。另外,__过于强制了,还可以像js一样,通过_这样的“约定”来让人知道它是个私有属性,但其实它还是可以被访问的,只是在from 模块名 import *的时候,带单下划线的方法不会被导入。(这点比js还是强些)
====================================
a = {}
a["a"] = "A"
print a["b"] #=> 异常
print a.get("b") #=> None
c = a.get("b") or "B" #=> c为“B”,这更类似于我们写别的程序时 c = xxx || "B"的思路
c = a.get("b","B") #=> c为“B”,在python中也可以使用这样的写法。
====================================
2) 列表可以使用pop()方法弹出最末尾的元素,也可以通过pop(n)弹出指定索引的元素。而字典也有类似的用法。a.popitem()方法可以弹出字典中的一个元素,但这个元素是随机的,因为字典本身是无序的。a.pop(key)可以弹出指定key的项,类似于列表的pop(n)。
3) update()方法。 update方法类似于yui的merge()和jQuery的extend()。合并两个字典对象。
===================================
a = {"a":"A","b":"B"}
b = {"c":"C","b":"ABC"}
a.update(b)
print a #=> {"a":"A","b":"ABC","c":"C"}
===================================
4) items()和iteritems()方法都普遍用于for循环的迭代中,不同的是items()返回的是列表对象,而iteritems()返回的是迭代器对象。两者的用法差不多,但iteritems()的性能更快。
5) 字典有自己的copy()方法,但它只是浅复制,如果用深复制的话,需要使用copy模块的deepcopy方法。(怎么跟js一个毛病?我靠,是谁借鉴了谁的思路吗?)
