依赖注入与对象封装

依赖注入(Dependency Injection)是单元测试中非常重要的技术,以这个思想为基础,Java的世界里测试技术(比如Google Guice)已经达到了非常完善的高度。与之类比,C++的世界要落后太多,绝大部分时候都要自己动手造轮子。在自己动手的过程中,我常常被一些基本的问题困扰,比如,如果一个对象(父对象)的某个成员变量是另外一个对象(子对象),那么我们是应该在父对象的构造函数里创建子对象呢,还是在外面创建子对象然后再传递进构造函数? 在外面创建子对象然后再传递进父对象的构造函数符合依赖注入的基本原理。这种做法对于测试的好处是不言而喻的。我们可以很容易的创建一个 Mock 子对象,设置好它的行为,然后将它传入父对象中,然后对父对象进行各种测试。然而这种方法将子对象的创建代码都放在父对象之外,这给使用父对象的其他代码带来了很大的不便,因为客户程序需要同时创建父对象和子对象。如果对象的包含关系有好几层(比如子对象还有自己的子对象),按照这种方法编写程序很快就变得非常繁琐。 如果退一步想,上面这样使用依赖注入实际是违反对象封装(Encapsulate in OOP) 的基本思想的,因为父对象的使用者应该不需要知道子对象的存在。在实践中,我经常使用的程序库基本上都只要求用户创建顶层对象,而不使用依赖注入的方法。这样的程序接口要简洁好用得多。 在我看来,要真正用好依赖注入,我们需要仔细的考虑到底哪些对象是当前对象的Dependency,哪些对象实际上属于当前的对象。 对于Dependency关系,使用Dependency Injection适合的的。比如一个外部的 RPC Service Stub,或者一个数据库连接。一般来讲,创建这些对象的方法也是Well-Known,在父对象的构造函数中要求传入这些对象不会对客户代码带来特别的困难。 对于真正属于父对象的子对象(a.k.a. 一个确定的 Has-A 关系),那么使用 Dependency Injection 很多时候是不合适的。正确的做法是不应该要求构造函数中需要传入这些子对象。如果为了测试方便,设置一些 Setter 方法传入 Mock 对象来覆盖默认生成的子对象,这是可行的,因为单元测试是白盒测试的一种,测试代码了解一些对象内部的实现并不是什么问题。 参考资料:https://stackoverflow.com/questions/31121611/dependency-inversion-principle-solid-vs-encapsulation-pillars-of-oop

有心栽花花不开,无心插柳柳成阴

在后院种了几窝西瓜,精心照料到现在,西瓜只是开了一些花,什么果子都还没有长出来,倒是旁边冒出来一窝南瓜,估计是一不小心种子混在一起,或者是前几年岳母中的种子剩在地里。南瓜因为顺便也能浇到水,倒是长得很好,现在已经结了一个不小的瓜。 这真可谓是: 有心栽花花不开,无心插柳柳成阴。 谋事在人,成事在天。

假期围棋

这周休假,张老爷子又邀请我下围棋,终究技痒难耐,欣然应约。 双方一共下了三局,第一局我输掉的过程和以前几乎一模一样,开局的棋走的太薄,被老爷子的攻势完全压制,不久就被吃掉一块,最终完败。剩下的两局终于有了一些长进,能拆三的地方老老实实拆二,孤棋多花一手出头走畅而不是贪抢实地,结果老爷子的棋很快就露出了破绽,被我抓住。第二局开局就吃掉对方一条大龙,第三局中央一步飞枷,吃掉棋筋,两局均胜。特别是第三局,发挥的水平已经接近我顶尖时候(2014年打上弈城四段)的状态,颇为得意。 第一局 开局走的太薄,只注意掏空,左上角被围攻后并吃掉之后,棋基本上就不行了。这个过程和8月20日那一局基本类似,只不过那次侥幸取胜,结果这次还是没有吸取教训。 第二局 这局棋实际下了两天,第一天走厚上方,结果对方急于打入,全部被吃掉。因为时间关系,下到112手时封盘。晚上花了点时间摆了摆中央和下方的变化,第二天果然用上,下方大龙轻松逃脱,中央防线屹立不倒。最终有惊无险的取胜。 第三局 这局棋开局注意走畅孤棋,很快对方在上方露出破绽,被我打穿大空。左边突围的时候又下出手筋,吃掉对方棋筋。后来虽然下的有些不简明,但是黑方一直没有什么像样的机会。 感悟与总结 培锋说的很对,长周末有空下一下棋是可以的,平时也就算了。培锋还提到老爷子在家的地位肯定没有我在家的地位高,到我们家培锋总是又倒茶又拿水果,在老爷子家一切之后我们自己动手。女人这种地方总是很敏锐。 每次下棋老爷子都主动提出要拿黑棋,难道他还是觉得我的棋下的比他好?我自己现在都没有这种心理优势了。不过黑棋确实有利于他发挥攻击力,这一点毋庸置疑。 托尔斯泰说他没有办法戒掉赌博,我也没有办法戒掉围棋吗?不过按照我2014年的经历,一旦能够克制自己,有规律的下棋,实际上也不是一件太坏的事。平时练习要能够立刻放下(做题,看视频),实际对局输了也能收手。能做到这一点就好了。 我的棋局部计算还成,但是有时太贪,棋走得太薄,跟老爷子下,这个棋风是大忌。克服这一点以后,获胜的把握就大了很多。其他类型的对手我还没有怎么遇到过,反正现在是不会随便到网上去下棋了。

减少手腕疼痛

最近左右两个手腕都隐隐作痛,而且左手手腕上还长了一个 Ganglion Cyst,虽然这是程序员的职业病,但是还是要想些办法来缓解一下,要不然不但能不能干到60岁是个问题,按照 这篇介绍Repetitive Strain Injure文章的说法,有可能连打字、拿东西这样的活计都干不了,那不就成了残废了? 刚好最近在家休假一周,主要是休息一下,顺便对照了一下文章中的说法,对自己工作环境做了如下的改变: 降低的桌子的高度。这样打字的时候手腕就悬空了,以前觉得Wrist Supporter是一个好东西,打字的时候稳定性高很多。现在才发现这个东西对于手腕健康绝对是个不好的东西,手腕放在Wrist Supporter上以后,整个手的移动就不方便了,这样按很多键的时候,手腕需要扭来扭去才能够到,时间长了,手腕很容易损伤。我现在才注意到,我按键盘的时候,即使是休息状态,手腕也是向外扭的,这是健康的大忌。悬空以后手臂可以自然的移来移去,手腕轻松很多,而且胳膊和背部的肌肉可以承担一下按键、移动的工作,也顺便减少了手部肌肉的负担。 重新坐到椅子上。前一阵子因为工作空间狭小,我把椅子拿掉,换成了一个小板凳。这样一来打字的时候姿势就全毁了,背是弯的,脖子也向前抻着。换成一把有靠背的椅子,然后再把显示器的高度调合适,这样打字的时候最少坐姿是端正的。虽然屁股和背有点累,但是这是正常的累,比起长期下来,脖子和肩膀积累一大堆问题,还是要好很多。 把我以前买的一个foot pedal 拿出来,设置成为向下的箭头键。这次手腕疼痛和以前几次不一样的是:左手的手腕也开始疼痛,以前可主要是右手。仔细想来,还是左手鼠标用得太多,尤其是滚轮,食指一天到晚总是在忙碌的滚来滚去,自然牵动手腕肌肉了。Foot Pedal 可以帮助向下滚动,减少很多需要使用鼠标的时间。 最后一点,也是最重要的一点,还是要减少使用计算机的时间:工作多动脑少动手;电脑上的娱乐尽量避免;平时多用用纸和笔没有什么坏处。 何振梁老爷子谈到自己工作的时候曾经自嘲过一句:小车不倒只管推。到我这个年纪,也只能用这句话自勉了。希望过一阵手腕疼痛能够缓解吧。

折腾人的新款MacBook Pro 之二:陈旧软件

Apple对于 GPL尤其是v3的敌视是非常明显的,从自己搞一个 Clang来取代GCC,到MacOS上预装的陈旧的开源工具 (参见下面的小节:Mac-Sierra2017的开源工具版本),都是明证。我最初从Linux笔记本切换到MacBook Pro的时候,还抱着美好的幻想:既然MacOS 是从 FreeBSD分支搞起来的,那么平常的开源工具应该都很容易使用。实践证明,完全不是这么回事。 这样搞的结果就是,如果你习惯了Linux的环境,想要在Mac上也搞一套,那实在是太痛苦了。我自己折腾了一阵子以后(安装Homebrew,安装Linux虚拟机,安装Xcode,等等),终于下定决心,就把这个Macbook Pro当作一个上网本,在上面安分的使用Chrome Browser,如果有需要的话通过SSH去个Linux主机(可以使用公司的桌面机,也可以是GCE的虚拟机)搞些开发,再也不在Mac上面折腾任何东西了 。 Mac (Sierra 2017)的开源工具版本 注意看这都是10年前的版本了! $ screen –version Screen version 4.00.03 (FAU) 23-Oct-06 $ bash –version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16) Copyright (C) 2007 Free Software Foundation, Inc. 一个老美关于这事的吐槽 http://meta.ath0.com/2012/02/05/apples-great-gpl-purge/ 字里行间流露出广大开发者几乎是被Apple欺骗了的感觉。

从 Vim 里面运行 Shell 命令

虽然IDE大行其道已经很多年了,我个人一直在Terminal上使用Screen + Vim + Shell来工作,这个工具集的好处就是比较稳定,这么多年来一直没有怎么变过,所以积累的经验到现在也还适用。另外一个好处是干活比较自由,反正终端上完成一切操作,远程主机和本地主机也没有什么区别,结果就是我整天背着公司的笔记本到处跑。 大概几年以前,在折腾过 Vim + Shell 的各种集成插件以后,自己动了心思写一个类似的东西。经过一番折腾,有了如下功能: 首先需要两个Terminal,每个里面都要跑一个Screen Session。这个上来估计很多人都觉得就太小众了,刚好我自己总是用笔记本连着外接显示器,所以到哪里都有两个显示器可用。 用一个文本文件记录常用的Shell命令,然后把快捷键 <Leader>r 把这个文件加载到Vim。加载的脚本顺便再把回车键映射到执行一个Vim函数。这个文本文件可以像正常的文本文件一样编辑。 当一个命令写好了以后,在这个命令上按下回车键,前面注册的Vim函数把当前的文本行送到另外一个Terminal里面的Screen Session当作一个Shell命令来执行。核心是利用了Screen的如下两个功能: 发送命令: /usr/bin/screen -S <target-session> -p <target-window> -X stuff <target-shell-command-line> 调整窗口 /usr/bin/screen -S <target-session> -p <target-window> -X eval <screen-command-like-split-focus-etc> 顺便赞一下,Screen的编程功能还真是挺强大的。虽然几乎所有的人都给我推荐使用tmux,可是自己已经习惯了Screen,也就懒的切换了。需要注意的是Screen的Stuff命令发送字符上限很小(MAXSTR = 768)大的命令行很容易引起 “Remote Command Too Long”的错误,我就本地把它改为16k,这样就方便多了。 几年间断断续续还折腾过一些上述方案的变种,比如在辅助的Terminal Session里面同时加载命令文本文件和Shell Window,或者只用一个Screen,弄来弄去,都觉得还是最初的方案好: 屏幕空间充分利用。 执行命令的时候窗口布局几乎没有什么变化,对于使用者注意力影响很小。 折腾过了,心满意足了,也就不再折腾了。

西瓜开花了

西瓜是我最喜欢的水果,今年一时兴起,就和孩子们在后院种了一点。一共种下三窝种子,一窝从来没有发出来,另外一窝发出来后被鸟儿吃掉了,只有一窝长成苗了。 可能是因为种的太晚,虽然常常浇水施肥,但是一直长得很慢。看着天气越来越冷,觉得今年可能来不及开花结果了。孩子们倒是一直兴趣很大,常常跑去看。而且他们一直对父母抱有绝对的信心,总是觉得爸爸种的西瓜肯定能长好。 今天跑去浇水,突然发现西瓜开花了!孩子们兴奋的又唱又跳,我也很开心,虽然还没有结果,但是有了花就有了希望呀!

玩具和书籍

昨天带孩子们去 Barnes & Noble。本意是让他们每人买一本书,结果儿子挑了一个会射箭的大象玩具,女儿挑了一个乐高积木玩具,真是让我恼火。好说歹说,儿子算是同意放弃了,可是女儿就当众很伤心的哭起来了,最终一狠心给他们就都买了。 下意识里,我总是觉得书籍比玩具高尚,可是对于孩子来说,也许两者也真是差不多。女儿很喜欢读书,但是做手工的玩具她也非常喜欢,儿子好像更喜欢玩具,但是也常常抱着书看个不停。现在很多出版的儿童书籍,故事也很平庸,在我看来,很难说有很多价值,也许真的不如一个玩具?

折腾人的新款MacBook Pro 之一: USB C

一直习惯使用显示器附带的USB口做USB Hub,这样键盘、鼠标、音响、网线都连在上面,笔记本移动的时候只需要插拔电源线、外接显示器线和一个USB线。 自从换到了新款MacBook Pro,这个USB连接就开始常常出问题。症状时是当笔记本重新唤醒的时候,有很大的机会键盘不识别,偶尔鼠标也会不识别,唯一可靠的解决办法就是拔下来再插上去。找过公司的IT部门,也切换到独立的USB Hub,问题怎么也解决不了,估计还是和这个新的USB C 端口有关系。这个新款的MacBook Pro真是问题多多。 就像很多人把电脑重启作为修正问题的一个有效方法,这里我也只能忍耐了。毕竟已经习惯了Mac的快捷键。用一句老话来鼓励自己:如果一个笨办法能够解决问题,那它就是一个好办法。 Update 2017-09-01: 最近连重新插拔好像都不行了。关掉显示器也不一定能行。买了一个专门的 USB B to USB C 的线缆,希望这次能够解决问题,要不然只能切换回原来的 MacBook Pro了。 Update 2017-09-11:最近和一个搞硬件的朋友聊了聊,他说这个USB C 完全是大厂商争权夺利的妥协产物,里面一大堆不必要的复杂东西,很多软件都支持不了,也难怪有这么多问题。正在考虑切换回一个老的Laptop。 Update 2018-12-11:切换到了一个 gLinux 的电脑,各种后悔。参见 gLinux 的问题实在太多了,无力吐槽 Update 2018-12-11:现在的问题是要不要重新再切换为 Macbook Pro with USB C Only,难道真的是好了伤疤忘了疼? Update 2019-07-25: 已经切换回 Macbook Pro with USB C Only, 可能这几年中软件的Bug修的差不多了,使用起来一切都好,再也没有问题了。

偶尔治愈,常常缓解,总能安慰

Ben的眼睛眨了有好几个星期了,而且跟我们抱怨眼睛不舒服。妈妈带他去看了家庭医生,结果从医生那儿回来以后更担心,要去看专科医生。 上午专门请了一个假,带着Ben去看医生。路上塞车很严重,到了地方以后,先是等了很久,然后有一个助手带着我们又是散瞳,又是眼底灯,又是色盲图,又是视力表。折腾了两个小时,真正的医生出来看了不超过10分钟,宣布Ben一切健康,就把我们给打发了。 妈妈们总是这样大惊小怪,儿科医生的很大一部分工作在于向她们保证孩子一切健康。这让我想起来关于医生的一句名言:偶尔治愈,常常缓解,总能安慰。(To cure sometimes, to relieve often, to comfort always.)