循序渐进地培养⾯向对象的思维⽅式
在我踏⼊软件⾏业后,⼀直苦于没有前辈指点。我常年困惑于两个问题:⼀是怎样培养⾯向对象设计的思维能⼒?⼆是怎样进⾏架构设计,有⽆⽅法?
因为我做了那么多年项⽬,却很少看到有漂亮的⾯向对象思维写出来的代码,觉得有必要提醒下年轻从业者。如今总结⼀下⾃⼰的经历,希望对刚刚⼊⾏的朋友有些启发吧。
我的基本观念是,⾯向对象的思维⽅式是可以循序渐进地培养出来的。通俗地讲,就是不断地编码实践,量变会引起质变。
1 开发⼈员的思考动⼒不⾜
记得参加⼯作后做的第⼀个项⽬是某电信局的运营⽀撑系统开发,采⽤的开发框架是Struts1+EJB+Hibernate的组合,WebLogic8作应⽤服务器。
弹指间,⼗年时光飞逝,慢慢适应了中年⼤叔的⽣活。
有趣的是,经典的组合搭配⾄今没有过时(千万不要那么快过时啊,靠它混饭吃啊),特别是在企业应⽤中,例如⼤家常说SSH组
合:Struts/SpringMVC + Spring/EJB + Hibernate/Mybatis/JPA等。
开源框架的出现,使得只要在其基础上进⾏⼆次开发就可以⼤⼤降低开发⼯作量。只要⾼级⼯程师搭建好了项⽬的开发⼯程,初级开发⼈员就可以依照模版代码,依葫芦画瓢,流⽔线作业进⾏业务功能开发。
从项⽬组整体的⽣产效率⾓度看,这确实是很⼤的进步。各个成员不同分⼯,各⾃做好⾃⼰的⼀部分⼯作即可,也符合现代企业管理的理念。但从个体的⾓度看,也客观造成了⼀些弊端,对初级开发⼈员,他就是流⽔线长的⼀个螺丝⼯,没有机会去思考如何进⾏⾯向对象的设计。
这⾥我以⼀个常⽤的⽤户登录模块举例,系统采⽤经典的三层架构进⾏分层,类图如下:
简单解释下业务场景:
1) ⽤户⽤浏览器访问系统登录页⾯,输⼊⽤户名与密码,提交表单。
2) 系统对⽤户的⽤户名和密码进⾏验证,并对⽤户的访问进⾏⽇志记录以便以后作审计。其中数据库中存储的⽤户密码信息是加密后的字符串。
作为⼀个初级开发⼈员,开发业务功能常常只需要复制粘贴图中的6个类即可。有时甚⾄连PasswordEn
coder类都省去。还有些项⽬组有开发⾃⼰的代码⽣成⼯具,甚⾄连复制粘贴⼯作都省去,只需要对⼯具⽣成的代码作少量修改即完成了开发。
回到类图,这⾥的LoginService和UserDao接⼝是否有必要定义?复制粘贴以及代码⽣成⼯具使得⼯作量成本很低,初级开发⼈员就没有动⼒去思考这个问题的,依葫芦画瓢完成功能,打完收⼯即可。
我个⼈的观念是有没有必要取决于具体的项⽬需求与⼈员分⼯。
1) 如果该业务模块由1个开发⼈员完成,系统不需要⽀持多数据库,也就是UserDao没有多个实现类的需求,则UserDao接⼝可以移除掉。同时如果系统只有通过数据库查询认证的可能,LoginService也没有多个实现类的需求,则也可以移除掉。
2) 如果项⽬组中该模块每⼀层都由不同的⼈员分⼯合作,则由于层次间依赖的需要,引⼊接⼝使得上⼀层可以更早地开始开发,也使得上⼀层的单元测试变得简单。在这种情况下,LoginService和UserDao接⼝有存在的合理性。
3) 在项⽬中,某些模块因为业务需要Service层和Dao层必须要有多种实现类。从代码风格⼀致性⾓度考虑,存在⼀个类对应⼀个接⼝的情况也是可以容忍的。这样是为了维护代码的可读性,也客观上预留了系统的可扩展性。
⼀般来说,LoginService和UserDao接⼝存在有其合理性。
这些开发框架对通⽤功能进⾏了⼤量的封装,其本⾝源码中包含了⼤量OOD的思想。提供给开发⼈员进⾏⼆次开发时,单表单的增删改查由于业务需求简单,就体现不出OOD的价值了。这在⼀定程度上,使得开发⼈员去思考OOD的动⼒不⾜。
⽐如MVC架构中对于控制器与视图的分离,业务模型类与Servlet API的转换这些恰恰是复杂的需要OOD抽象能⼒的,框架已经给你实现了。框架做的多⼀点,所以开发⼈员就轻松⼀点。再⽐如Spring中对于Java bean的创建与管理,依赖关系的注⼊,基于和动态代理机制来实现的声明式事物以及⽇志处理,还有与其它框架的集成⽀持等复杂点,它都给你实现了。还有Hibernate中实体对象与关系型数据库中表的对应转换,对API调⽤翻译转换成SQL语句,对多种数据库语法的⽀持,查询结果的缓存等,也是复杂点。
反过来说,如果你不⽤任何框架,去实现⼀个中等规模的Web应⽤。看看⾃⼰写的代码与现在基于框架⼆次开发的代码差别⼤不⼤,差别在哪⾥。我想,⾃⼰去实现未必会⽐开源⼤⽜们设计的更好,但却完全可以体会到复杂点难点在哪⾥,OOD是不是有应⽤场景,因为写的过程中蛋疼了。编程虽易,OO不易,且编且珍惜。
2 Java平台中的⾯向对象举例
Java语⾔的API规范中,可以说是处处体现了OOD。这⾥仅仅举Servlet和JDBC规范两个例⼦,不同的⼚商底层对Servlet API的实
现,JDBC驱动的实现,完全对开发⼈员屏蔽,两套规范都实现了精炼的抽象。
Servlet API把HTTP协议中的请求信息封装成HttpServletRequest对象,响应消息封装成HttpServletResponse对象。开发⼈员直接从这两个对象中获取HTTP通信中的各种HTTP头信息,参数信息,以及完成对HTTP客户端的响应信息输出。
JDBC API使得开发⼈员可以不考虑具体的数据库类型,⽤相同的API完成与数据库的交互。
完全可以说,掌握了Servlet API就掌握了Java Web应⽤开发;掌握了JDBC API就掌握了与Java数据库应⽤开发。
Java开源社区奉献了⼤量优秀的框架,例如:Lucene,Hadoop,Hbase,Mina,Netty,ActiveMQ等在互联⽹和电商⾏业得到⼴泛应⽤。(看来搞Java⼀时半会不会不到⼯作,不过少年你天资聪颖活⼒青春,我还是建议你搞IOS开发简单粗暴,不解释)。
3 ⾯向对象不适⽤于所有业务场景
在Java语⾔中,⼀切都是对象。那是不是所有的业务问题,都可以⽤⾯向对象的⽅式去设计实现呢?要知道“尺有所短,⼨有所
长”,OOD也不是全知全能的宇宙真理啊!
举个例⼦,⽐如要实现⼀个⾃然整数n的阶乘。你再怎么⾯向对象去思维,也⽆法去抽象出对象模型对应这个问题。这种场景下,反⽽过程式的实现更加简单直接。还有很多数学推导公式的求解也是如此。
再举个例⼦,项⽬中有个实现最短路径算法的需要。虽然我⽤⾯向对象思维⽅式活⽣⽣写了10来个类实现了功能。⽹上⼀搜,C语⾔⽤邻接矩阵存储的⽅式来实现的⼀两个类就实现了该功能。⾯向对象的⽅式可能更加适合开发⼈员去读懂,对于计算机来说,可能⾯向过程的实现运⾏效率更⾼。
在我看来,计算机技术的本质是计算。各种⼆进制表⽰的数据,通过⽹络通讯进⾏传输,然后系统对计算的结果进⾏存储或通过⽹络返回给调⽤⽅。
我们的思维⽅式中不能排斥,不包容其它的设计理念。认为OOD⼀招打遍天下,就有点愚蠢了。这点其实挺难的,我们从⼩的受到的教育是同⼀种观念,同⼀种政治正确性,甚⾄同⼀种价值观,不允许有异见。经常能看到论坛上⾮此即彼的对骂。好在互联⽹的开放性,使得越来越多的⼈有了多元的世界观,价值观。
4 学习设计模式可帮助理解OOD
设计模式列举了⼀些经典业务场景的最佳实践,⾮常值得借鉴学习。我们学习设计模式中常⽤的23种招式,最终⽬的是培养⾃⼰对OOD的悟性。
就好像我们看武侠⼩说⾥⾯,⼗⼋般武艺招数全部学会,还不抵九阳神功⼀掌。对内功深厚的⼤师来说,飞沙⾛⽯⼀花⼀叶都可伤⼈性命。
但学武之初,扎扎马步,练练兵器拳法,还是有助于培养悟性的。
同时⼜不能⽣搬硬套为了模式⽽模式,觉得它精妙就想时时处处都模式了。举个邪恶点的例⼦,由于教育的缺失,就如在学⽣时代男⽣普遍性启蒙都是靠观摩岛国爱情动作⽚来领悟啪啪啪的要义⼀样,你要是模仿男主⾓把⾥⾯的每个场景每个招式都实践⼀遍吧,有些⾼难度动作会完成不了还会伤害⾃⼰,你懂的。
设计模式的精髓就在遵循开闭原则,将通⽤代码向⽗类抽取,对可变的⾏为抽象成接⼝进⾏封装。模式的提炼应该是⽔到渠成的事情。
只要平时养成⾯向接⼝编程,依赖于抽象⽽不是依赖于具体实现类的开发习惯。当编码实践经验达到⼀定的临界点后,量变引起质变,不知不觉中发现写的代码已经是运⽤了设计模式在⾥⾯了。⼤家都听说
过,⼀万⼩时理论,精通⼀项技能往往需要持续实践⼀万⼩时以上。但
凡5年以上扎实地编程实践,即使得不到⾼⼈指点,也会对OOD顿悟。
5 持续重构可帮助对抽象思维的培养
OOD的精华在于抽象,抽象,再抽象。但是每个⼈对于设计经验有⼀个积累的过程,不可能⼀开始就设计的⾮常完美,能应付项⽬中所有的需求。
抽象思维能⼒,更需要⼀个循序渐进的培养过程。我们不断地学习优秀开源框架的源码,学习设计模式都是⼀种外部⼿段,旨在迫使⾃⼰⼤脑中学会抽象思考的⽅式。
所⾯临的问题域是⼀个⼦系统,⼀个模块,那抽象的思维培养的是⾯向对象设计的能⼒,系统分析与领域建模的能⼒。放⼤了看,如果⾯临的问题域是整个系统或者多个系统,则培养的就是系统架构设计的能⼒。
有过⼀定编程实践经验的⼈都有过这样的经历,系统中如果有重复的代码段出现2~3次就会觉得很恶⼼,尤其是⼀⼤段⼤段上百⾏⼏乎⼀样的代码。因为每个⼈的编码能⼒经验不同,开发的时候很可能设计不到位。那可不可以将其进⾏提炼复⽤呢?
答案是可以,因为我们有重构(Refactor)这个法宝。
持续的重构是可以有效改进⾯向对象的设计的。我常常在看别⼈的代码时候,不⾃觉地帮着进⾏重构,这只是⼀种习惯。当然,必须在尊重原作者的前提下,⼀步步⼩范围内重构。
落实到细节上,难点在于类和⽅法的命名,类的职责划分,抽象的粒度⼤⼩适中。这些真的只能靠经验积累,去领悟理解了,没有⼀定的标准,什么是好,什么是不好。我觉得起码命名要清晰,易于理解,类的职责要专⼀,⽅法长度不能过长。细节⽅⾯可参照⼤⽜Martin写的那本关于重构的圣经书。
最后,⼀个⼈对知识的理解,不是线性增长或者抛物线上升的,应该是阶梯形上升的。每上⼀个台阶,需要熬过⼀段不规则的积累沉淀期,再由外界因素的触发引起内在的觉醒才能继续到下⼀个台阶。以前死活不明⽩的事情,或许随着年龄增长,都释然了。闻道有先后,但终究会顿悟。
>编程启蒙是学什么的
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论