The Pragmatic Programmer读书笔记之中的一个
DRY-Don’t Repeat Youself
尽管自己买了非常多软件project方面的书,可是由于时间的问题。一直没有静下心来充充电。
近期由于感觉自己在编程方面迫切须要有进一步的提高,于是打算好好的研读一下Andrew Hunt和David Thomas合著的《The Pragmatic Programmer:From Journeyman toMaster》。本来我是比較偏好原版的,买的书不是原版就是影印版,可是因为该书原版价格有点贵,舍不得花银子,于是买了电子工业出版社出的评注版(周爱民、蔡学镛评注),这样既有读原版的风味。又有高手的评注做參考,照理应该是非常不错的。
刚看完前面几章,边看边对比着手上的项目实践,感觉这书写得真是不错,真懊悔没早花时间来进行研读。真是应了概述序言的那句话。作者知道如何做,并且知道如何说。大多数时候。我是知道该如何做,可是说不出个所以然来。
为了备忘,我把在读书过程中和平时编码的感悟以读书笔记的形式写下来和各位分享,不妥之处,恳请批评指正。
先介绍一下书的总体结构。
书分了八个章节,介绍了编码的各个方面,当中以Tip的形式来浓缩作者的编程哲学。并详解为什么要这么做,这么做有什么优点,不这么做有什么坏处。全书加起来有70个Tip(我已把全部的Tip摘录出来附在文后),能够看做是两位作者合起来40多年编程经验的精华。
在读前几章20多个Tip的过程中,感觉眼下给我最大帮助、影响最深的一条Tip是:DRY - Don’t Repeat Youself,简称DRY原则,这条原则被其它非常多软件project方面的书所提及,如Robert C. Martin 的Clean Code,Martin Fowler的Refactoring等。
它的直译就是:不要反复你自己。
在非常多时候,同一个项目中的某些“事物”会多次出现而导致反复,这些“事物”包含需求、数据库结构定义、算法、类定义等等,如在数据库编程中。表的结构定义会在数据库设计说明书中存在,在代码中获取数据表的对象时也可能会有一份相同的“表定义”存在(用属性来一一相应数据表中各个字段)。同一时候在数据库中表的结构定义还得反复一次,一般当表结构定义要改动时,先是改动文档中的,然后是数据库中的,最后是代码中的,须要时刻保持这三者的一致,要不然程序就可能不对了。代码和文档也不一致了。
当表的数量较小时,如十几个、二十几个表,用手工的方式更改还不会认为是非常麻烦的事,但当表的数量添加到上百个时。假设还是用这样的手工的方式进行改动,不仅繁琐,并且非常easy挂一漏万,仅仅要代码和数据库有一处没有保持一致,程序就可能会报错崩溃,曾经我常常是手工改动。改得自己都不胜其烦,特别是在做需求不确定的项目,表结构常常须要改动,真是奇怪自己为什么没有意识到这个问题。并加以解决,看来这就是实务(高手)程序猿和普通程序猿的区别了,普通程序仅仅知道完毕任务,遇到问题机械的解决,而实务程序猿会思考是什么导致了这个问题,而进一步想办法予以解决和改进,以便在下次遇到相同的问题时可以套用之前的解决方法。
DRY原则和反复(Duplication)是紧密不可分的,Duplication即Repeat。
书中分析了反复发生的几种情形:外界编程环境强加的反复、粗心导致的反复、懒惰(没耐心)导致的反复(Impatient duplication)、大型项目各开发人员间的反复。
我想我犯得最多的就是粗心和没耐心导致的反复。有时候须要一个功能,这个功能在其它模块已经实现,可是在当前环境下须要一点逻辑上的小变动,于是就复制粘贴过来,改动改动就当做完事了,这样就产生了反复。
真是懊悔没有早点读这本书,害得自己曾经走了不少弯路。接下来说说我是怎么实践DRY原则的。手头上有一个项目,是一个信息统计查询系统,整个系统大概有100多个表。每一个表的字段数目从10个到30个不等,各个表之间基本上都是独立的,没有太复杂的关系,要求可以在线和离线录入数据,并可以对全部数据进行查询和统计。整个系统的用户是一个树形结构,有上下级的关系。
要求server放在上级单位,下级单位负责搜集录入数据,可是有些下级单位未和上级单位连接网络,无法通过网络直接录入和上报数据。
为了满足这样的需求。我採用C/S结构来进行设计。整个系统分两大块,server端和client。server端相对照较简单,就是一台实体的server。装Windows Server 2003,Oracle 10g R2数据库。全部的数据都存在server上,client分成两种,一种是管理查询端。第二种是录入端,管理查询端就是对下级上报的数据进行导入、改动、查询,录入端分两种应用方式,网络版和单机版的,网络版的可通过网络直接将数据上传到server中,单机版的则採用Access作为后台数据库,用户在本地将数据录入完成后将数据导出为上报数据包(为一个Zip压缩文件),再送至上级单位由人工导入server中。
在框架设计上没有什么难度,但最要命的是客户眼下对整个系统须要统计什么数据、对数据须要做哪些查询汇总操作都不是非常清楚。在做这个项目之前,客户都是使用Excel表的形式从下级单位搜集数据。不过作为信息存储的一种载体,并没有相应的方法对数据进行查询和汇总分析。在开发过程中,不可避免的,客户须要不断的变更收集数据的项目、对数据进行统计的方式。导致数据表的结构也须要相应的改动和调整。假设放在曾经。我可能还是採用老办法:客户提出改动意见,我改动文档、代码、数据库,来回往复的折腾,不出错是不可能的。
想想DRY原则。须要其它的办法来解决问题。
全部表结构的定义我都写在了数据库设计说明书里了,说明书是一个Word文档,以表格的方式具体列出了数据库中各数据表的字段名称、数据类型、凝视。假设可以把Word文档中的表结构解析出来。生成创建表的DDL语句,然后使用代码连接数据库,运行这些DDL语句,那么就能实现自己主动创建数据库的目的,哪不是非常好吗?要把Word文档里的表格内容解析出来,那非常easy,可以使用Microsoft的Word Interop,可是我手上有Aspose的Word组件,这个组件能够在不安装Microsoft Word的情况下解析和生成Word文档,并且使用比起微软本身的Word Interop方便得多。
我先使用代码把Word中的数据结构定义读取出来,然后拼接成创建表的DDL语句,能够依照须要生成创建Access、Oracle、SqlServer等各种类型数据库表的创建语句,最后使用代码连接数据库运行这些DDL语句以便在数据库中创建这些表。我仅仅要在Word中修改表的结构定义,在数据库中生成表和在代码中生成数据表的类定义就能够通过代码自己主动完毕了。
这样大大提高了编程的效率,降低了出错的几率。
在对数据进行编辑显示时,我又遇到了麻烦。这些表之间的相互关联不是非常紧密。基本上都是独立的,採用DataGridView进行数据显示是比較方便的,可是假设採用DataGridView来进行输入的话。有些不方便。由于有些表的字段是其它表的外键字段,在录入表数据时,仅仅能从已录入的数据中进行选择,举个样例。表A中有一个单位名称字段A1。表B中也有一个单位名称字段B1,B1的内容和A1的内容是一致的。B1是表B的外键,关联到表A的主键字段A1,这样在使用DataGridView是须要从表A中得到A1字段的内容列表。显示在表B的B1字段输入框中供用户进行选择。客户不太想通过这种方式录入数据。要求将表的内容显示为一个独立的编辑界面供数据录入。并且最好可以导出为Excel表的形式供多人录入。然后再导入到Access数据库中,以提高效率。假设要依照客户的要求做,那么在程序中必须存在一份表的结构定义。包含字段名称、数据类型,以便依据这些信息动态的生成一个数据编辑界面。
在之前,我已经将表定义写在数据库设计说明书里了,从数据库说明书将表结构定义提取出来,生成一个XML文档。让后使用代码解析这个XML文档。则既可以达到自己主动创建表的目的,也可以供程序对表结构进行解析,从而能自己主动生成数据录入界面供用户对数据进行编辑(待续)。
附:书中的70个Tip。
1. Care About Your Craft
2. Think。About Your Work
3.Provide Options, Don’t Make Lame Excuses
4. Don’t Live with Broken Windows
5. Be a Catalyst for Change
6.Remember the Big Picture
7. Make Quality a Requirements Issue
8.Invest Regularly in Your Knowledge Portfolio
9.Critically Analyze What You Read and Hear
10. It’s Both What You Say and the Way You Say It
11. DRY- Don’t Repeat Yourself
12. Make It Easy to Reuse
13.Eliminate Effects Between Unrealated Things
14.There Are No Final Decisions
15. Use Tracer Bullets to Find the Target
16.Prototype to Learn
17.Program Close to the Problem Domain
18.Estimate to Avoid Surprises
19.Iterate the Scheudule with the Code
20. Keep Knowledge in Plain Text
21. Use the Power of Command Shells
22. Usea Single Editor Well
23.Always Use Source Code Control
24. Fix the Problem, Not the Blame
25. Don’t Panic
26. “Select” Isn’t Broken
27. Don’t Assume It - Prove It
28. Lean a Text Manipulation Language
29.Write Code That Writes Code
30. You Can’t Write Perfect Software
31.Design with Contracts
32.Crash Early
33. If it Can’t Happen, Use Assertions to Ensure That It Won’t
34. Use Exceptions for Exceptional Problems
35.Finish What You Start
36.Minimize Coupling Between Modules
37.Configure, Don’t Intergate
38. Put Abstraction in Code, Details in Metadata
39.Analyze Workflow to Improve Concurrency
40.Design Using Services
41.Always Design for Concurrency
42.Separate Views from Models
43. Use Blackboards to Coordinate Workflow
44. Don’t Program by Coincidence
45.Estimate the Order of Your Algorithms
46. Test Your Estimates
47.Refactor Early, Refactor Often
48.Design to Test
49. Test Your Softwate, or Your Users Will
50. Don’t Use Wizard Code You Don’t Understand
51. Don’t Gather Requirements-Dig for Them
52. Work with a User to Think Like a User
53.Abstractions Live Longer than Details
54. Use a Project Glossary
55. Don’t Think Outside the Box -Find the Box
56.Listen to Nagging Doubts - Start When You’re Ready
57. Some Things Are Better Done than Described
58. Don’t Be a Slave to Formal Methods
59.Expensive Tools Do Not Produce Better Designs
60.Organize Around Functionality, Not Job Functions
61. Don’t Use Manual Procedures
62. Test Early. Test Offen. Test Automatically.
63.Coding Ain’t Done ‘Til All the Tests Run
64. Use Saboteurs to Test Your Testing
65. Test State Coverage, Not Code Coverage
66. Find Bugs Once
67.Treat English as Just Another Programming Language
68.Build Documentation In, Don’t Bolt It On
69.Gently Exceed Your User’s Expenctions
70. Sign Your Work