这个系列,记录实际工作中基础类库开发的思维方式、类设计过程。额外的,也会提及实战中如何使用Scrum类似的过程来组织自己的工作,以及在这类工作中如何使用Tdd方式。

这个世界充斥着一种叫做DbHelper的东西,微软的Entlib已经坚持不懈更新多年了,满世界都能够搜索到不知是谁发布的各类DbHelper类的代码,许多程序员都有自己的具备完全或不完全自主知识产权版本。这其实是件非常痛苦的事情,因为过多的选择总会令人迷茫。

于是在某一天,我决定终结这种鬼画符。

我首先思考,为什么要用Ado.net,为什么要用DbHelper?其实大家有许多选择,Linq To Sql、Aef是一层包裹,社区有许多Orm工具,不过大多数程序员仍然使用原生的Ado.net。原因有两种:性能问题,编程复杂度问题。从这个角度而言,说明微软自己的Entlib、Linq To Sql和Ado.net Entity,这类努力并没有带来合用的东西。

进一步,为什么需要DbHelper?想少写一些代码也就是复用代码,同时希望在多种数据库之间无缝切换,这两点是原生的动力。不过,这不是回答,真正的回答是,Ado.net本身的设计比较混乱。

一个良好的设计,应保证用户,也就是使用这个体系的程序员,能够非常简易的使用。这体现在两个方面,第一、概念要尽可能的少。第二、代码要尽可能的少。理解这一点其实也是非常容易的,程序员为最终用户提供的应用,需要易于理解、需要操作的步骤尽可能的少,这是对应的用户体验问题。

框架的设计和开发,最终用户是程序员,也应该遵循同样的原则。

我想做的事情,包括两个层面,第一、易于理解:程序员只需要理解连接、命令、DbReader三者,就能够便捷的应用Ado.net开发,这需要屏蔽DbProviderFactory、DbProviderFactories、DbDataAdapter和DataSet、DataTable之类概念。第二、简单的定义实体和实体集合对象,从而在大多数情况下无需直接写数据库访问代码。

当然,这里的前提,是维持Ado.net的性能水平。

幸运的是,这项工作远没有想象中那样困难。几个月前,经过5个工作日,相对比较轻松的达成了上述目标。两个地球人都没有想到的方式,第一、自己定义一个继承于DbConnection的类,解决消灭DbProviderFactory的问题;第二,使用索引器实现实体对象和DataRow的互相转换,从而避免使用反射,确保性能。

结合代码生成器自行创建实体类,多数情况下,无需编写任何数据访问代码。

我们遇到的第一个问题,是DbProviderFactory的问题。

Db系列的类基本上是抽象类,举例来说,SqlConnection和OleDbConnection都是继承于DbConnection的,为了尽可能容易的在不同数据库中切换,我们需要使用Db系列的类。由于是抽象类,当然无法自行实例化,你只能通过DbProviderFactory创建,无论是连接、适配器甚至参数,都需要通过它来创建。熟悉设计模式的朋友应该知道,这是提供者模式和工厂模式的混血儿,微软那些年轻的小学究们还真是煞费苦心。

DbProviderFactory通过DbProviderFactories.GetDbFactory(“提供者名称”)获取。这样,你需要在代码中维持一个DbProviderFactory对象,然后,比较笨拙的做法是,在每个地方,创建Db系列的对象时,都用DbProviderFactory.CreateXXX之类的方式去做。我非常讨厌这样子,为什么讨厌,你懂的:

这样不是很好吗?DbConnection connection= new DbConnection(数据库类型,连接字符串);甚至,你的系统中如果不涉及到同时使用Oracle、SqlServer的情形,你可以将数据库类型和连接字符串保存在配置文件中,这样创建连接的时候连参数也无需考虑。

既不需要理解DbProviderFactory,也不需要理解DbProviderFactories,更不需要理解“提供者名称”,我相信这些东西很难用自然的中文,来表达它是神马意思。每次实例化的时候代码也少了许多,这显然符合我们概念少、做事少的目标。

目的明确,那么,如同阿Q先生所说,我们革命吧

我选择的项目管理工具,比较懒惰。使用Team Foundation Server,使用微软的Scrum 1.0模版,我们基于这个模版创建一个团队项目,命名为Faster。按照惯例,应该先提出项目愿景、列出Product Backlog,这实际上就是Story、用例、用户情景的集合,这些翻译我不满意,因此我简单的翻译成“项目目标”和“功能清单”。

项目目标:以最简单的方式处理数据访问,消灭绝大部分与数据访问相关的代码编写任务。

1.简化DbConnection的创建方式:工作量评估2  优先级1000

2.获取数据库中的元数据:工作量评估5  优先级2000

3.自动生成Crud命令:工作量评估3  优先级3000

4.实现数据访问泛型类:工作量评估8  优先级4000

5.制作代码生成器:工作量评估3  优先级5000

6.通过外键处理一对多关系:工作量评估2 优先级6000

项目目标定义清晰是非常重要的,两句话,解决“我们这个项目要做什么”的问题。

功能清单,必须使用用户语言,每一项功能都是满足用户的一项特定的需求。这里非常简单的只列出标题,实际工作中,对于每一项功能,我仅仅是写一段两百字以内的介绍,绝不愿意书写非常庞大的文本,那既没有必要、程序员也不会真正用心去看。注意标题的书写格式,没有主语,动宾结构,形如“做--什么”,其中优先级应由用户决定,工作量评估则是团队程序员通过讨论决定。这里工作量评估的单位是一个相对的单位,你可以将其看成“理想工作日”,只是衡量每项功能相互之间的大小。我首先在功能列表中找出最小的第一项,将其定义为2,第二项我认为它的工作量应是第一项的2倍到3倍之间,定义为5。

然后,我们将这些功能的“迭代”也就是我翻译的阶段,设置为第一个版本的第一个Sprint,嗯,有些拗口。我们简单些:我认为这些任务在一个阶段就能够完成,无需划分为多个阶段。如果是比较大的项目,此时应根据优先级和工作量评估,将其划分成多个阶段,保持每个阶段的工作量总和大体均衡。微软Scrum模版中的版本概念不需要考虑。

现在,打开Product Backlog,什么都没有了。

面对你的第一个Sprint,也就是第一个阶段,刚刚那些功能都转到这里了。

接下来,我们为每一项功能划分“任务”,举例来说,对于第一项功能,简化DbConnection的创建工作,我将其划分为如下的工作任务:

1.创建测试数据库 优先级1005 需要1小时

2.继承DbConnection,创建Db类  优先级1010 需要2小时

3.创建连接:优先级1015 需要1小时

4.使用配置文件中的默认连接字符串,创建连接:优先级1020,需要1小时

5.实现其他2种创建连接方式:优先级1025,需要1小时

总体需要6小时的时间,一天的工作量。可以看到每项工作任务的状态,都是To do…分配给谁,还是空白。

那么,我先将第一项任务,分配给自己。当然如果是多人工作的话,为每个人分配一项任务。然后,我准备开始做第一项工作了,将其状态设置为in progress。

必须注意,团队中每一个程序员,在任何时刻只面对一项任务,这是关注点的问题。

那么,开始创建测试数据库。

首先,在这项工作任务的描述栏目中,已经有任务内容的简要描述。那么,先按照这样的格式写一句话:10月3日 8:50开始,预期1小时。之后记录工作内容,结束后加上结束时间和中途中断的时间。这只是个人的工作习惯。

首先建立一个Sql Server数据库,我创建了三个表格,Post、Tag、PostForTag,看起来很眼熟,嗯,这是一个简易的微博数据库,只处理发布微薄,没有用户系统也没有评论、转发之类的功能。我们为Post增加一个Image字段,这当然是为了测试方便,但这也让Post表格编程一个可以分类处理相片的东西。

然后,我们为三个表格定义外键关系,由于Post是数据量比较大的表格,那么同时也为其中几个经常会用于查询的字段定义索引。

为这个数据库创建脚本,将这个脚本加入到测试项目,这样同时也能够对数据库脚本进行版本管理。

另外我们加入一个简单的Excel文件,这个也是有必要的,测试OleDb,我们至少需要验证两种不同的Db对象,工作是否正常。

既然有创建测试数据库这项工作任务,这说明我们在写单元测试的时候,没有使用Mock对象。一向很排斥这个,因为Mock实际上增加了单元测试工作的工作量、逻辑也变得复杂,而带来的好处相当有限,这与“简单”的宗旨是不合拍的。

这项工作如期结束,将任务状态改为“Done“,将第二项任务的状态改为in progress…

最初我企图用扩展方法解决问题,不过,由于扩展方法的语法不支持属性和字段,我决定使用原始的方法。定义一个类Db,继承于DbConnection。当然,要实现这个类需要覆盖抽象类的许多方法和属性,这是比较艰难的工作。不过,我用一种很奇怪的方式规避了这类工作。我在这个类里,加入一个私有的DbConnection类型的字段connection,所有需要覆盖的方法、属性和事件,都传递给这个connection去做,这就如同抄书,几分钟就搞定了一个自定义的DbConnection类,当然,这不是一个抽象类。在这个类里,同时加入了一个DbProviderFactory类型的私有字段,私有,所以使用该类的程序员是不能感觉到DbPRoviderFactory的存在的。

嗯,这就是所谓的封装。所谓面向对象,其实百分之七十以上的场景,都只是运用“封装”。屏蔽内部细节,提供简易的服务接口。

不过,我实际上做的工作,是消除Ado.net中的提供者模式和工厂模式,换句话说,是在替微软的Ado.net打补丁,将他们高深的设计模式知识粉碎掉。由于工作性质龌龊,工作内容低级趣味,所以在这里谈及面向对象还是多少有些惭愧的。

第二项任务就这么忽悠过去,没有必要写任何单元测试,这只是抄写的工作。

开始做第三项任务,先从全局归纳一下,我们需要为Db类提供三种构造方法,用于创建连接:

Db():使用配置文件中默认的提供者和连接字符串创建连接,我们简单的将这个配置项命名为"ApplicationServices",因为Asp.net 和Asp.net Mvc项目中都包含这个配置项。

Db(连接字符串,提供者名称)

Db(配置项名称):根据配置文件中某个配置项,获取连接字符串和提供者名称,创建连接。

另外,桌面应用中,我们常常在程序运行的整个周期,使用唯一的一个连接,当然,我是指类似Sqlite一类的桌面数据库。那么,我们为其提供一个静态属性Default,这个属性返回默认的Db对象。

这样,多数工作场景,你可以通过如下两个步骤,使用这个连接,

第一、在配置文件的ConnectionString节,定义连接字符串和数据库类型,保持此前同样的语法:

<add name="ApplicationServices" connectionString="Data Source=.;Initial Catalog=MiniBlog;Integrated Security=True;"
     providerName="System.Data.SqlClient" />
<add name="RunTime" connectionString="Data Source=.;Initial Catalog=MiniBlog;Integrated Security=True;"
providerName="System.Data.SqlClient" />

第二个RunTime,是访问Excel文件的用到的连接字符串。大家应该看出,这是在单元测试项目的App.config中使用的。

这里要关注一下,在Vs2010结合Tfs的情形下,怎样写单元测试是最轻松的。

一般的流程是:先写单元测试、让代码编译通过、写代码实现让测试通过、写下一个单元测试。这会导致在写单元测试的时候,代码语法自动提示是无法工作的。

我这么做:先在类图中添加方法,然后在这个方法中“创建单元测试”,再写单元测试,再实现。然后下一个方法或属性。这个过程中,永远不要对私有成员进行单元测试,虽然Vs2010也能够通过创建访问器来测试私有成员,但那个毫无意义。对public成员的测试必然会覆盖私有成员,如果私有成员逻辑很复杂,那就需要重构。

下一篇将描述第三项工作任务的具体过程,用Step By Step的方式讲述Tdd的工作方式。   类图如下:

转载于:https://www.cnblogs.com/by1990/archive/2011/03/03/1969719.html

终结DbHelper鬼画符1:Scrum实战演示相关推荐

  1. 终结DbHelper鬼画符2 Tdd全攻略

    在这一篇中,我们将采用Step By Step的方式,重点介绍如何在实际开发过程中,使用测试先行.代码复查和重构.形成这样的开发习惯之后,编写代码将不再是实现.排错的过程,而变成制造快乐的过程:写一个 ...

  2. NVIDIA专家实战演示,教你快速搭建基于Python的车辆信息识别系统

    主讲人 | 何琨 英伟达 量子位编辑 | 公众号 QbitAI 随着智慧城市.自动驾驶的快速落地,车辆的检测和识别应用场景非常广泛,如车牌识别.车流统计.车辆属性识别等. 近日,在英伟达x量子位发起的 ...

  3. Python 调用有道翻译api接口翻译外文网站的整篇西班牙文实战演示

    Python 调用有道翻译 api 接口翻译整篇西班牙文实战演示 第一章:翻译效果展示 ① 翻译文章示例一[阿尔卡拉门的无海摩纳哥:"不到4万欧元,你就不能在这里租任何东西."] ...

  4. 白盒测试工具 - sonar的安装、配置与使用入门手册,用sonar检查代码质量实战演示

    sonar 检测代码质量实战演示 第一章:sonarqube 的安装与启用 ① sonarqube 获取 ② sonarqube 配置 ③ sonarqube 驱动放置 ④ sonarqube 的启动 ...

  5. PS菜鸟入门 -- 实战演示之磨皮

    PS 工具.窗口基本认识了,现在简单的图像处理没有问题了.下面进入实战演示阶段. 参看:磨皮教程大全 一.光滑磨皮 适用范围:精度要求不高的图片. 操作技巧 (1)Ctrl + Alt + 2 调出高 ...

  6. Docker小白到实战之Dockerfile解析及实战演示,果然顺手

    前言 使用第三方镜像肯定不是学习Docker的最终目的,最想要的还是自己构建镜像:将自己的程序.文件.环境等构建成自己想要的应用镜像,方便后续部署.启动和维护:而Dockerfile就是专门做这个事的 ...

  7. 千寻和省cors精度对比_使用中海达RTK实战演示千寻cors账号对比省cors网络,验证其测量效果究竟如何...

    原标题:使用中海达RTK实战演示千寻cors账号对比省cors网络,验证其测量效果究竟如何 最近看到有很多的网友都在问千寻cors账号的定位服务怎么样?还在考虑要不要从省cors网络转用千寻cors网 ...

  8. Diskpart 磁盘管理实战演示

    Diskpart 磁盘管理实战演示 这里我们来看一下Diskpart命令行分区的使用,这里我是启动了WAIK的PE,不过这个命令是不需要PE的,你们也可以在自己的电脑上执行 List disk从词上我 ...

  9. 视觉模型精度如何更上一层楼?百度技术专家实战演示调参技巧

    作者 | 王金许       责编 | 欧阳姝黎 出品 | CSDN(ID:CSDNnews) 随着计算机视觉的应用场景拓展得愈加广泛,与此同时,AI开发者对企业级视觉模型开发的精度与推理速度也更加关 ...

最新文章

  1. oracle10G导入导出数据文件
  2. 程序人生:外链建设流程与细节都有哪些
  3. javascript中window.event事件用法详解
  4. 折衷的方式实现php与ruby共享session实现单点登录
  5. 360题带你走进深度学习!吴恩达深度学习课程测试题中英对照版发布
  6. 成长为一名Java架构师需要掌握的技术有哪些呢?
  7. Ubuntu 安装截图工具Shutter,并设置快捷键 Ctrl+Alt+A
  8. 强制卸载软件包linux,强制删除rpm包的方法
  9. Leetcode每日一题:104.maximum-depth-of-binary-tree(二叉树的最大深度)
  10. eclipse出现String错误,问题已解决
  11. VB6的后期绑定和前期绑定
  12. [渝粤教育] 西南科技大学 土木工程材料 在线考试复习资料
  13. esp连接服务器的协议,【零知ESP8266教程】WIFI TCP协议通信 TCP服务器示例
  14. 云计算5G的基本概念
  15. MAX485芯片收发详解 实现485通信
  16. Hadoop解除安全模式
  17. 小米手机显示崩溃日志
  18. WPA3 vs WPA2
  19. 携手共建安全生态|海泰方圆正式加入申威产业发展联盟
  20. 1.RecyclerView设置clipToPadding=“false“,scrollbars无法跟随列表滚动到底部的解决方案

热门文章

  1. Neovim开发Golang示例
  2. DbtPy API接口描述及示例
  3. shell脚本中if的“-e,-d,-f”
  4. JDK、JRE与JVM的区别与联系,已拿offer入职
  5. vue2的vetur
  6. matlab/simulink电力电子仿真三相变压器设置和使用
  7. Codewar (1)
  8. Linux学习03——管道符、重定向和环境变量
  9. nllb-200-distilled-600M语言缩写对照表
  10. c语言写按键控制蜂鸣器,51单片机用按键控制蜂鸣器发出do re mi fa...的声音,...