老鸟心得之设计模式

设计模式对很多程序员来说,都是如梗在喉。我们以SSH广大的程序员为例,其中struts2可能直接由servlet代替,持久层用hibernate或iBATIS再或者直接JDBC,前台做JS(可能是JQuery或者ExtJS再或者)和基本页面,后台用SSH。老老实实的写actioin,写service,写dao,写sql,写各种工具类。设计模式懂也罢不懂也罢,反正项目就一直是这么写了,有些简单工厂,有些单例什么的已经是很给设计模式面子了,写些socket多线程再来点策略模式什么的,差不多是极限了。设计模式似犹抱琵琶半遮面,半推半就,也就偶尔拿来调侃调侃炫耀炫耀来凸显自己作为程序员的存在感。程序员内心深处那股对设计模式卑微的呐喊,那份似有激情却了胜于无的心绪,深藏于心,如梗在喉,不痛不快。

 

老鸟鄙人不才,将设计模式所经历稍加梳理,得出三个阶段:

 

第一阶段:看山似山,看水似水

场景一:有些程序员始终是按耐不住的,不啃下设计模式这块骨头,出门都不好意思说自己是IT精英。于是乎,手捧天书觅龙穴,苦对经纶千尺寒,豪情万丈弹指处,不见风华空对月。面对各种设计模式的书籍和资料,看到心寒,因为最终在日常工作的软磨硬泡中,对设计模式要么一再忘却,一再复习,要么愈加迷惑,愈加伤怀。

场景二:通过痛苦的学习经历,似乎对设计模式有个大体的认识,并踉踉跄跄记住了几种设计模式。知道设计模式是解决软件功能灵活性问题,经典的描述是高内聚低耦合。知道java类库或者使用框架本身的一些设计模式,总体来说似懂非懂。

场景三:在使用工具类的时候,发现想对某个方法不够用,于是直接在工具类里面添加了一个方法,比以前的方法多处理了一些业务。改完之后,越看越觉得不对劲,渐渐闻到其中的badsmell,这个工具类本身是几个项目都可以通用的,现在加了方法之后跟当前系统的一些东西耦合了,如果将来其他项目直接把这个工具类拿过去,就用不了了。开始想办法,第一想法就是继承那个工具类,但一看那个工具类是final修饰的,差点直接一口血,后来重新写了个工具类,使用委派的方式,将原来工具类的所有方法重新包装了一遍,终于解决问题。仔细一看,总觉得这跟学的某个设计模式有点像,好像叫代理模式来着,于是翻书一看又发现不是,代理类和被代理类是继承同一个父类或者实现同一个接口。

一谈外圆内方:外圆内方狭义的解释是指一个人外在很圆滑,八面玲珑,内在却很刚正,很坚强。更内涵和广泛的解释是外圆代表世间一切显得是那么千变万化,摸不透,内方代表万变不离其中,一个事物万千变化它的本质是不变的。设计模式看似云里雾里,其本质都是为了改善软件系统本身的灵活性,或者有人理解是高内聚低耦合,有人理解是开闭原则的总纲领对扩展开发,对修改关闭等等。

 

第二阶段:看山不似山,看水不似水

场景一:大部分人总是有好奇欲的,代码写多了总会有疑惑,某些程序员总是想去知道为什么自己要这么写代码,为什么框架会规定这么写。为什么我只要在一个类里面声明接口类型,在Spring里配置一下具体类型就可以了。赶紧找了sping的相关资料,听说是依赖注入和aop的功劳。那依赖注入是什么呢,通过查资料发现就是通过反射机制调用属性的set方法进行注入,这个还比较好理解,那aop呢。查了资料发现是动态代理,据说是代理模式的一种变体。开始去看怎么写动态代理,发现似乎跟认识中的代理模式长的很不一样,进一步去探索动态代理是怎么跟代理模式扯上关系,终于知道动态代理是在事先不知道被代理类的情况下在运行期间知道了被代理类再去进行代理。但是,但是,为什么动态代理的写法这么奇怪,这是为什么,为什么。。。(答案是java动态代理在运行时用字节码在内存永久态中自动创建了一个代理类的Class对象,这涉及到JVM本身的实现)

场景二:老想知道Spring是什么东西了,是怎么写出来的。于是乎,抱着雄心壮志开始研究源代码,结果第一天就宣布放弃。原因嘛,不是说依赖注入就是一个反射调用属性set方法吗,怎么框架里这么多类跟这个事有关,怎么各种调来调去,绕晕了,绕烦了,绕心碎了。

场景三:咦,你看那struts1里面执行action方法还要传入ActionForm,request,response参数什么的,而struts2执行action方法这些参数都不需要了,转念一想struts2可以通过actionContext获得request,response,可以通过action里面的dto来获取struts1所谓的ActionForm。各种搜资料,struts2是通过服务器启动的时候动态的创建了单例的ActionContext对象,并且在每次请求进来之后ActionContext对象为每个请求创建一个线程来保存该次请求需要的上下文对象。长知识了,但是定睛一看,这东西叫上下文对象(Context Object)模式,而且是一种javaEE的架构模式。这,不小心趟了浑水,对设计模式都还是半生不熟,还来个什么javaEE模式。

场景四:struts2可以配置成action方法的入口是execute(),果断的是命令模式啊,无非就是invoke()编程了execute()嘛。可是struts2可以配置action方法入口是非execute(),这是怎么回事,还是命令模式不啊。经过仔细研究,发现的确是命令模式的一种变体,猛的心头一惊,完了,这设计模式还能有各种变体,没完没了了吗?

场景五:咦,这工厂方法模式怎么长的跟策略模式一个样啊,就类名和方法名不一样,难道是错觉吗,既然长的一样为什么是两种模式?

再谈外圆内方:设计模式三头六臂,各种变化,还能天外有天,跑出什么javaEE模式,一种模式能有N多变体,模式和模式之间还有各种纵横交错。无论如何,特定的模式就是用来解决特定的需求,特定的模式有其特定的使用场合和意图。

 

第三阶段:看山是山,看水是水

场景一:今天来了一个需求,这个需求会形成特定的类结构,为了使下面的程序员能更好的使用这些类对象,决定使用工厂方法来便利开发。刚把工厂方法结构封装好,又来了一个需求,需要根据用户的不同一个方法有不同的实现,为了能为不同用户定制不同的实现,决定使用策略模式。

场景二:需要用不同的类来处理不同国家的税率,当一个用户成为某个国家的公民时,需要调用相应的类来对个人资产进行税率包装。一开始想到了装饰模式,但仔细一想,装饰模式的结构可以对一个类进行多次包装。假设一个用户先是某个国家公民,调用相应类抽一层税,然后再是另一个国家公民,那如果用装饰模式,结果会造成再包装,也就是现在这个用户要身负两个国家的税,这是个问题。很快,决定用代理模式来解决这个问题,用户在哪个国家,就用那个国家的税收代理类,换国家的话换代理重新包装就行了。虽然从一般的理解上,代理模式多用于边缘功能的包装,如安全性,远程代理等,但是实际应用这个咬文嚼字的概念已经不重要了。

场景三:现有一接口,方法众多,如果程序员去实现这个方法,就必须把每个方法都丢到类里,但是很多方法都不必实现,这样很不好,决定由缺省适配器模式(default pattern)来解决这个问题。

场景四:发现某些action类可能会出现一长串if else块来对请求进行筛选相应的service进行处理,而且这些action的if else做的是一样的事情,初步方案是把这些if else抽出来独立成为一个类。很快,想到了初步方案还是有问题,将来如果多一条if else分支,就需要在该类中手动添加相应的判断并做其他额外处理,决定使用策略模式,并且在Sring配置文件中使用策略类来决定注入的具体类型。

三谈外圆内方:需求和意图万千不同,而且需求和意图将来可能面临变化。无论如何变化,无论意图如何,心有设计模式,心中就有杆秤,这杆秤让你能有信心的面对软件功能需求变化,有信心在一定程度上把握软件的灵活性,以不变应万变。

 

诗一首,与君共勉:

五行之内,纵横交错,来势汹汹,不得远帆。

道心常驻,平地而起,幻化而出,五行之外。

道外法影,别有洞天,澈明心境,自有风华。

还入五行,心有灵犀,一尺之地,有我一寸。

 

你可能感兴趣的:(java)