一 前瞻
空模式是设计模式的一种,设计模式是著名的四位程序设计大牛共同提出的,针对面象对象编程的一些相当通用的“思想”,甚至可以说是“过程”,只要略微实现一下就可以在应用中跑出来的“过程”。毫不客气地说,如果没有把《设计模式》读到一定的深度,哪怕是十年二十年的资深程序员,从根子上说也是有严重欠缺的。
百度空模式,可以得到很多类似的文章,这并不奇怪,它们很可能源自同一篇大牛的文章;因此对于中间发生的细节,是语焉不详的。求人不如求己,既然已经是半桶水了,那么把理解到的东西搞得扎实一些,更扎实一些,也是题中应有之义。顺便说一句,我一直记得GOF的《设计模式》里面是提过空模式的......当然我看这本书太早了,记忆出错也是很正常的,这并不是那么重要。
二 要解决的问题
大家都抄书,我也抄个例子--就是那个向图书馆借书的例子。伪码就不写了,用文字描述一遍就好:某图书馆有图书若干,编号为从1开始,各不相同的自然数;现在要开发一个接口,用图书编号为参数传入,返回图书的信息以供后期使用。这个问题还可以再抽象一些,但是那样会不太直观,所以先这样写着。
以面向对象的态度来看,当然是设计一个书本类Book,给他一个以图书编号为参数的构造函数New,传入参数返回对应编号的实体对象ConcreteBookxxx。但是如果这个传入参数不符合规范(比如给出了字符参数)或者参数是正确的数字,但库中没有这个数字对应的书本,该怎么处理?前者可以用数据类型检查来解决,但后者呢?
这实际是个错误(或者例外)处理问题。你或者可以无情地抛出例外,再使用一段通用的例外处理来做这件事?但对于小型的应用,这未免太浪费资源了--无论是人力资源还是系统资源。另外,抛出例外通常也意味着代码的逻辑存在某些不确定性,或者说,不够健壮。
另一个选择,是在发现例外的时候返回null。这可以说是每一个程序员都做过或者正在做的事。这么做和抛出例外相比,可说有好有坏;好处是让调用的用户“知道这个调用的返回是有问题的”,坏处则是让调用的用户“不知道有什么问题”。当然对小规模代码来说,这个坏处甚至不算是坏处--因为甚至不需要多人之间的协作就能完成;但对于工业级代码,这可能存在问题。于是空模式登场了。
三 应用
空模式的作用,就是在不返回null也不抛出例外的情况下,尽可能地把控制权交给被调用者(代码),同时调用者(用户)也能更多地了解到例外的细节。这两者听起来有些矛盾,让我们做起来看看。
还是上面那个借书的问题,我们用空模式来改写,看是什么样子:因为我们不能返回null,所以要先为Book类设计一个返回布尔值的判空函数IsNull(),并在缺省情况下返回true;再设计一个继承于Book的新类NullBook,重载这个IsNull()函数将它返回false。最后,当传入构造函数Book:New()的参数不符合规范时,就返回一个NullBook对象。
这样做的好处是,无论给出什么样的输入,都可以“正常”返回一个Book对象;你只要通过Book:IsNull()就可以判定它是不是有效的Book--当然,我们刚才通过判定null也能做到这一点,但NullBook对象还可以在输入参数不符合规范时将这个“不符合”的上下文用某种方式保存下来(比如私有变量)并且输出给用户看(以你想得到的方式),这就是返回null永远也做不到的事了。
四 新的问题
其实你已经看到了,就是“空模式”不一定要使用--只要你确定“永远也不需要”返回什么东西给调用者,而调用者也只希望用最简单的判定null来处理即可。这在小规模的代码中是很常见的;永远也不要为了用模式而用模式,不是每个钉子都需要锤子敲几下的,你可能弄巧成拙。
除了代码量增加之外,另一个问题就是上面的代码会产生很多的NullBook空对象。这个的处理需要用到另一个设计模式“单件”,也可以用静态对象来配合处理,和本文关系不密切,就不在这里细说了。
五 其他
这是在对我很喜欢的打谱软件multigo的模仿过程中得到的一些感悟。设计模式是非常好的东西,如果可能,以后还会继续写,继续思考,也希望能继续进步--不管多小的进步,都是好的。
网友评论