好歹干软件架构设计和开发也有几个年头了,自诩对四人帮(Gang of 4)那23种套路中的至少十几种也能变着法、甚至变着“态”的把它们用在最适合出现的地方。作为开发者,其实这些年头干的比较多的事还是看代码,看Java代码,看别人的Java代码:
顶级高手的代码,比如Rod Johnson的Spring代码,简单的说就是在实现复杂的功能的同时具有良好的效率和开放性、扩展性。不过,这句话说起来简单,做起来可就没边了。
国内一般高手常常被定义为解决问题能力很强的人,他们能快速的实现复杂的程序功能,攻克“技术难点”,让产品、模块及时的Run起来,这点让管理者很开心,因为根据大部分国内软件企业正在原始积累、起步阶段的特点,快速的交付、推向市场,活命是关键。但很多情况下,这样写出的代码效率会有一些值得优化的地方;架构的扩展性、开放性就更差了,甚至很难读(其实这正满足了部分“高手”的虚荣心);设计的优雅、艺术性就更无从谈起了。当然,这是值得理解的:活命是关键,总不能光着屁股搞艺术。同时,也能理解为什么一些外企中的外籍架构师不是很信任中国企业出身的软件工程师了。
新手或菜鸟的代码一般很直接的功能实现的堆砌,bug多多,能正常运行就是上帝保佑阿弥陀佛了,这里没有研究的必要。
本文并不想讨论设计模式的应用,因为那很深很大,需要靠大量的研读大师的代码来修炼。那么,就以利用Java语言进行面向对象的设计开发中一些常见的问题和我的经验来说吧。
建议就是既然开发者喜欢代码,那就尽量“让代码说话”吧。多多利用Java Doc之类注解的机制,作为设计文档的局部、细节;当然,全局的设计,总体的架构仍然需要借助UML、Word之流陈清。如果你使用Eclipse开发的话,利用"TODO ..."去标识具有疑问的代码和未完成的功能是相当有效的,在解决问题后,还可以将之变为Java Doc的一部分,你可以在“任务(Task)”栏目中查阅“TODO”。你还需要在注释中利用“deprated ...”关键字标识并说明废弃的代码,这对别人阅读你的程序有着极大的帮助!如果你想故意迷惑阅读你代码的人,那么我建议你不要标识废弃的代码。
建议按照这个基本原则:类是用来归纳一种事物的,即把一种类型的事物的属性和行为规置规置,作成一个“类型”,那么包就是把相关的类集中在一起的玩意!在同一个包中的类“互相是关联的朋友”,所以我们可以使用“同包级访问”权限(即不声明为public/protected/private)来进一步完善系统模块的封装性,这就好比C++中的“友元”(——有缘——都是缘分哪!),这对你系统的模块化、调用的清晰度都有着极大的提升。这个划分的粒度就得根据你系统的复杂度来了,如果系统/模块很小,都放到一个包下也是合理的。
另外据我观察喜欢使用Eclipse“平面视图”浏览包的人似乎对包的划分不如喜欢使用Eclipse“层次视图”浏览包的人敏感,呵呵,可能是因为无论怎么划分包,前者看到的都是一个平行界面吧。
我们知道,结构化的程序都是静态(static)的,大量使用静态类型无疑不是面向对象的套路,走回结构化的老路上去了。那么有人会说,“不管黑猫白猫,逮到耗子就是好猫”,就算是结构化的思想,只要简单好用,为啥不用?!其实面向对象的好处多了去了,要不然喊它干嘛?就说几点见解吧:静态的玩意无法保持对象状态,你用静态域吧,大家是共享的,不是哪个对象独有,在多线程情况下,搞不好还给你来个线程不安全;既然没法用域保持,那就只好把数据当成参数传来传去咯,这样可就热闹了,你想想,那方法还不就像传统的函数似的,会又多又乱,而且,方法的参数也会又长又臭(注:你说长我就认了,为啥还说臭呢?想想,Java的方法参数全是值传递,大量的参数传来传去,万一修改了不该修改的、碰到了什么花花草草的……),这样就容易造成程序的可读性降低,效率、甚至内存消耗都有所增加的局面,那是我们所不想看到的。
因此,如果不是必须要使用静态类型(在某些情况下,选择使用工具类还是最合理的,例如字符串工具(StringUtil)等数据加工诸如此类的情形),还是尽量使用“定义类型 - 创建对象”的思路来完成设计吧。
但是,我建议大家还是要慎用单例,因为严格的说它是一种“反模式”,即不是很符合面向对象思想的模式,你想想,本来好好的可以有很多对象,你非要造成只能有一个对象存在内存中的局面,那和静态的、面向结构的思想还有多大差距?因此单例将为您老造成如下问题:它无法直接为你保持数据域,因为只有一个实例对象,域是共享的,所以,单例模式常常和“资源池模式”一起出现,你将不得不使用一个资源池和一组Key去存取、维护单例所需要使用的数据;如果,在单例中维护数据域的话,你将不得不去考虑线程安全的问题,即当多个线程进入你这个单例时,数据域被覆写、脏读等不可预知的结果,解决方法一般是加同步锁(synchronized)同步或利用线程本地变量(ThreadLocal)保存。结果是,无论你怎样解决上述问题,都不可避免的会造成加大系统的复杂度、系统存在更多风险以及降低系统效率的副作用。
当然,单例模式在某些情况下还是能起到避免实例多次创建、降低系统资源消耗的作用的,例如JavaEE Servlet / Spring Bean等的实现机制。所以,我们不是不能使用单例,但是要慎用,多考虑一点,权衡一下再使用。
普通工程项目级的设计人员和Platform级的设计人员之一大区别就在于,在可以预知例外情况下,后者通常会选择抛出含义明确的异常,而前者通常会选择返回空(null)值。
同时,在处理非运行时异常的时候,有经验的开发者通常会将异常逐层上抛至一个适合处理的层次中进行集中管理、处置;而经验欠缺的开发者一般遇到强制要求捕获的异常时,立即“就地正法”,当场处理,通常表现为e.printStackTrace()甚至System.out.print...,这样的话,log和debug可就比较混乱和麻烦了。
没有规划设计的、随意的抛出Exception同样是很不负责和令人boring的行为。
还是那句老话,尽量的抛弃这些面向结构的设计习惯吧,编程领域已经进入了面向对象的朝代,我们应该多加利用OO (Object Oriented) 的思想去解决这种需求,采用重载的方法、多态的形式以及策略、工厂诸如此类的模式来替代合法的传递null值,你看如何?