所属文章系列:寻找尘封的银弹:设计模式精读
【隐喻】
“人不能两次踏入同一条河流”这是古希腊哲学家赫拉克利特的名言,到现在它仍然在启发着人们的智慧。
小孩子在学会了叫“奶奶”之后,突然有一天,她的奶奶指着另外一个满头白发的人,对孩子说:“叫奶奶。”这时孩子一愣,心想:“只有现在和我说话的这个人,才能被叫作奶奶,为什么另外一个人也可以被叫做奶奶?”经过奶奶的解释,孩子才慢慢理解了,“奶奶”这个词在一些情况下是特指唯一的一个人,有时会泛指所有和亲奶奶年龄相仿的女士。从此,孩子学会了一点点抽象思维。
有一个头脑有点不灵光的“傻姑”,在大街上逢人便问:“你看见爸爸了吗?”别人会认为她很奇怪,会反问:“你问谁爸爸?”如果路人是一个相声演员,他会指着自己说:“诶...往这儿看...”
我们总会认为自己不会这么傻,不过在代码的复杂世界里,如果抓不住问题的本质,一不留神就会成为那个“傻姑”而不自知。
看看下边这个例子:
在某个软件系统中,如果我们认为系统中某个概念只有一个实例,例如有一个Application类,那么这个系统中确实应该只有一个实例,如果系统中出现了两个实例,程序员反倒无所适从了。
于是,我们把Application类做成了单件,也就是说,通过这个类只能生成一个实例,系统中只有一个叫“应用程序”的东西。
但是,在一个偶然的机会中,我们想把这个系统中某个类的代码拷贝出来,放到另外一个系统中以实现代码复用,我们突然发现无法复用,因为这个类的代码中调用了Application::Instance(),而新系统中并不存在这样一个类。
这和“傻姑到大街上找爸爸”有什么区别?
本质上,在“傻姑”的故事里,她要找的是“我的爸爸”,而不是“爸爸”。在家里一提到“爸爸”,他就是个单件,但在大街上提到“爸爸”,他就不是一个单件了。同理,在应用程序A中,调用Application代表着“找爸爸”,放到应用程序B中,如果还“找爸爸”的话,一定会被笑死。
结论:
虽说“人不能两次踏入同一条河流”,每一件东西都是单件,但是,如果用抽象的眼光来看,人就有可能两次踏入同一条河流了。因为我们在这种抽象的眼光下,只关注它有没有水、是不是在流,而并不关心它水流是疾是缓、有没有泥沙。如果再考虑到上下文,在“傻姑”的故事里,在“大街上”这个上下文中,“爸爸”这个词就不能随意使用,因为它是抽象概念;而在“家里”这个上下文中,就可以随意使用,因为“爸爸”不是一个抽象,而是一个特指,即一个单件。
【引申】
温馨提示:
如果有人觉得前边的内容已经接近思维极限了,那么你可以跳过这部分,直接进入下一部分,那样你的身心会愉悦很多,你只需要知道一个结论:
何时使用单件,主要取决于团队共识,它首先是一个约定,其次才是它到底是不是一个完美意义上的单件,因为单件概念依赖于上下文。
既然,单件要考虑上下文,那么我们需要做到什么程度才算正确?对于上文提到的Application类,首先要把抽象概念“应用程序”继续单件化,例如MFC框架下的应用程序、Windows应用程序、21世纪的Windows应用程序...
很显然,这是一个没有穷尽的“单件化”过程,但有这个必要吗?团队有权而且应该去决定我们到底要不要考虑更大的上下文,而不是一味地追求极致。
在真实的开发团队中,其实我们有一个隐含的约定:我们不考虑那么多外部复用,只要团队的思维是一致的就是高效的。
这很像一种思维:心想事成,心能转物。
一个人,如果整天在想一个方向,例如我就想有房有车,那么他每天的思考,就会集中在这个方向,凡是和这个有关的信息,他都会很敏感,时间积累久了,自然就能做成。同样一个人,如果每天就想我要过极简的生活,那么时间长了,一样就会做成,或者说做成的概率很大。
在科学前沿量子力学的体系中,微观世界的规律是:人不去观察微观粒子的时候,粒子就处于既不是0又不是1的纠缠态;只要人一去观察,粒子就会变成要么0要么1的稳定态。也许未来的研究能证明:宏观世界也有一些类似的规律。现在还不得而知。
这么看来,人的意志还是很重要的。只要你知道你在做一个决定的时候,已经了解过你所能了解的所有重要情况,或者说团队了解过所有重要情况,那么这就够了,按照你们的当前知识和思考去做决定,这就是这一刻的最佳方案!于是,形成了团队的共识。
团队的共识,才是团队赖以生存、一以贯之的凝聚力!而要获得共识,先要知其雄,才能守其雌!
当然,这个最佳方案并不是说,我就想实现一个最简单的方案,例如用硬代码,那这也肯定不行。因为“用硬代码”这个思维代表了,你所在团队的思维模型和其他软件公司的思维模型差了一个时代,此时最应该做的是改造团队,而不是顺应团队。
就像老子说的“知其雄,守其雌”,我们需要先知道行业的“大势”,再了解了上下文的所有重要信息,也就是那个“雄”,才能选择当下的“小势”,也就是那个“雌”,否则的话就是闭门造车、夜郎自大。
【代码实现】
现在需要从“形而上”的讨论回到“形而下”。没有哲思早晚会出问题,但不落地,我们要哲思干嘛?
温馨提示:
1.在这小节中,我要给读者展示的是,单件到底是怎么落地的。对单件较为熟悉的读者,可以略过本小节。
2.本小节是为了让读者能看我这一篇文章,就能对这个模式有全面的了解,而不是再花很多时间去找其他的资料。
3.本小节安排在这个顺序,也是符合《金字塔原理》的,目的还是为了读者看得明白。
《设计模式》一书已经有了代码实现,我这里只是大致重复一下:
class Singleton {
public:
static Singleton* Instance();
protected: //把构造函数放在保护区,是为了避免客户代码直接调用构造函数,同时允许子类继承
Singleton();
private:
static Singleton* _instance;
};
Singleton* Singleton::_instance = 0;
Singleton* Singleton::Instance() {
if (_instance == 0) {
_instance = new Singleton();
}
return _instance;
}
温馨提升:下篇讲实现细节。未完待续...
作于2018-4-28