终结DbHelper鬼画符1:Scrum实战演示
这个系列,记录实际工作中基础类库开发的思维方式、类设计过程。额外的,也会提及实战中如何使用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实战演示相关推荐
- 终结DbHelper鬼画符2 Tdd全攻略
在这一篇中,我们将采用Step By Step的方式,重点介绍如何在实际开发过程中,使用测试先行.代码复查和重构.形成这样的开发习惯之后,编写代码将不再是实现.排错的过程,而变成制造快乐的过程:写一个 ...
- NVIDIA专家实战演示,教你快速搭建基于Python的车辆信息识别系统
主讲人 | 何琨 英伟达 量子位编辑 | 公众号 QbitAI 随着智慧城市.自动驾驶的快速落地,车辆的检测和识别应用场景非常广泛,如车牌识别.车流统计.车辆属性识别等. 近日,在英伟达x量子位发起的 ...
- Python 调用有道翻译api接口翻译外文网站的整篇西班牙文实战演示
Python 调用有道翻译 api 接口翻译整篇西班牙文实战演示 第一章:翻译效果展示 ① 翻译文章示例一[阿尔卡拉门的无海摩纳哥:"不到4万欧元,你就不能在这里租任何东西."] ...
- 白盒测试工具 - sonar的安装、配置与使用入门手册,用sonar检查代码质量实战演示
sonar 检测代码质量实战演示 第一章:sonarqube 的安装与启用 ① sonarqube 获取 ② sonarqube 配置 ③ sonarqube 驱动放置 ④ sonarqube 的启动 ...
- PS菜鸟入门 -- 实战演示之磨皮
PS 工具.窗口基本认识了,现在简单的图像处理没有问题了.下面进入实战演示阶段. 参看:磨皮教程大全 一.光滑磨皮 适用范围:精度要求不高的图片. 操作技巧 (1)Ctrl + Alt + 2 调出高 ...
- Docker小白到实战之Dockerfile解析及实战演示,果然顺手
前言 使用第三方镜像肯定不是学习Docker的最终目的,最想要的还是自己构建镜像:将自己的程序.文件.环境等构建成自己想要的应用镜像,方便后续部署.启动和维护:而Dockerfile就是专门做这个事的 ...
- 千寻和省cors精度对比_使用中海达RTK实战演示千寻cors账号对比省cors网络,验证其测量效果究竟如何...
原标题:使用中海达RTK实战演示千寻cors账号对比省cors网络,验证其测量效果究竟如何 最近看到有很多的网友都在问千寻cors账号的定位服务怎么样?还在考虑要不要从省cors网络转用千寻cors网 ...
- Diskpart 磁盘管理实战演示
Diskpart 磁盘管理实战演示 这里我们来看一下Diskpart命令行分区的使用,这里我是启动了WAIK的PE,不过这个命令是不需要PE的,你们也可以在自己的电脑上执行 List disk从词上我 ...
- 视觉模型精度如何更上一层楼?百度技术专家实战演示调参技巧
作者 | 王金许 责编 | 欧阳姝黎 出品 | CSDN(ID:CSDNnews) 随着计算机视觉的应用场景拓展得愈加广泛,与此同时,AI开发者对企业级视觉模型开发的精度与推理速度也更加关 ...
最新文章
- oracle10G导入导出数据文件
- 程序人生:外链建设流程与细节都有哪些
- javascript中window.event事件用法详解
- 折衷的方式实现php与ruby共享session实现单点登录
- 360题带你走进深度学习!吴恩达深度学习课程测试题中英对照版发布
- 成长为一名Java架构师需要掌握的技术有哪些呢?
- Ubuntu 安装截图工具Shutter,并设置快捷键 Ctrl+Alt+A
- 强制卸载软件包linux,强制删除rpm包的方法
- Leetcode每日一题:104.maximum-depth-of-binary-tree(二叉树的最大深度)
- eclipse出现String错误,问题已解决
- VB6的后期绑定和前期绑定
- [渝粤教育] 西南科技大学 土木工程材料 在线考试复习资料
- esp连接服务器的协议,【零知ESP8266教程】WIFI TCP协议通信 TCP服务器示例
- 云计算5G的基本概念
- MAX485芯片收发详解 实现485通信
- Hadoop解除安全模式
- 小米手机显示崩溃日志
- WPA3 vs WPA2
- 携手共建安全生态|海泰方圆正式加入申威产业发展联盟
- 1.RecyclerView设置clipToPadding=“false“,scrollbars无法跟随列表滚动到底部的解决方案
热门文章
- Neovim开发Golang示例
- DbtPy API接口描述及示例
- shell脚本中if的“-e,-d,-f”
- JDK、JRE与JVM的区别与联系,已拿offer入职
- vue2的vetur
- matlab/simulink电力电子仿真三相变压器设置和使用
- Codewar (1)
- Linux学习03——管道符、重定向和环境变量
- nllb-200-distilled-600M语言缩写对照表
- c语言写按键控制蜂鸣器,51单片机用按键控制蜂鸣器发出do re mi fa...的声音,...