在程序中的数据访问层中,经常会遇到返回实体类的情况,而不同的实体类大多具有相似的代码,情况也大致相同:有的是返回实体类的一条记录,有的是返回实体类的集合。这样如果仅按照最简单的写法的话,程序中会出现许多相似的代码,给人一种不舒服的感觉,如果发现一个程序中有许多重复类似的代码,那么就是程序中的坏味道,我们就应该想办法,对这些代码的实质进行抽象,进行优化,那么接下来要总结的接口泛型就是这么一个例子,另外用到了策略模式+单例模式。有些地方还需要继续优化,不过先把今天实现的功能总结一下。
先来看看UML结构图:
1、解释一下单例模式:BasicDataReaderToEntityStrategy
单例模式有一个自身的组合,需要一个私有化的实例化入口,具备一个全局访问的方法,另外需要明白的是,单例模式的线程安全问题,我的理解是整个程序中再任何时刻只能允许单例的原始对象自身有的动作,如,修改里面的数据等,因此给它自己上一把或者两把锁是必要的了。有关单例模式的5种写法以及各自的特点,可以参考这里:http://www.jb51.cc/article/p-vmfqimdh-tk.html
我再把这里用到的BasicDataReaderToEntityStrategy的相关代码贴出来供大家参考或者讨论。
Imports Model Imports System.Windows.Forms Imports System.Collections.Generic Imports System.Data.sqlClient '单例模式 Public NotInheritable Class BasicDataReaderToEntityStrategy Implements IDataReaderToEntityStrategy(Of BasicData) '自身组合 Shared singleInstance As BasicDataReaderToEntityStrategy = Nothing '定义一个静态的系统锁 Shared ReadOnly padLock As New Object '私有化构造函数 Private Sub New() End Sub '全局静态属性,唯一实例化入口,双重锁 Public Shared ReadOnly Property GetInstance() As BasicDataReaderToEntityStrategy Get If singleInstance Is Nothing Then SyncLock padLock If singleInstance Is Nothing Then singleInstance = New BasicDataReaderToEntityStrategy End If End SyncLock End If Return singleInstance End Get End Property '实现策略模式的接口 Public Function DataReaderToEntity(ByVal dataReader As System.Data.sqlClient.sqlDataReader) As Model.BasicData Implements IDataReaderToEntityStrategy(Of Model.BasicData).DataReaderToEntity Dim BasicData As New Model.BasicData BasicData.Data_Date = dataReader.Item("Data_Date").ToString BasicData.Data_Head = dataReader.Item("Data_Head").ToString BasicData.Data_leastTime = dataReader.Item("Data_leastTime").ToString BasicData.Data_leastCash = dataReader.Item("Data_leastCash").ToString BasicData.Data_prepareTime = dataReader.Item("Data_prepareTime").ToString BasicData.Data_Rate = dataReader.Item("Data_Rate").ToString BasicData.Data_tempRate = dataReader.Item("Data_tempRate").ToString BasicData.Data_Time = dataReader.Item("Data_Time").ToString BasicData.Data_unitTime = dataReader.Item("Data_unitTime").ToString Return BasicData End Function End Class
2、泛化后的接口:IDataReaderToEntityStrategy
无疑这是这篇博客的重点总结,所谓的接口的泛化就是对接口再进行进一步的抽象,然后遵循一个编译器约定的书写格式,经过实践之后你会感觉也挺简单的,废话不多说,贴代码,供参考:
Imports System Imports System.Collections.Generic Imports System.Data Imports System.Text Imports System.Data.sqlClient '策略模式,定义泛型接口:将DataReader的数据返回实体类 Public Interface IDataReaderToEntityStrategy(Of T) Function DataReaderToEntity(ByVal dataReader As sqlDataReader) As T End Interface
解释:这里的 T就是对不同的实体类的进一步抽象,相当于在接口中定义一个占位符,然后在具体的环境中根据需求进行针对性的实例化。
3、sqlServerDALHelp类:
sqlServerDALHelp类不与具体的策略接口的子类BasicDataReaderToEntityStrategy关联,只与他们的父类接口关联,看看代码:
''' <summary> ''' 执行没有传入参数的存储过程,返回DataReader ''' </summary> ''' <param name="spName">存储过程名</param> ''' <returns>DataReader</returns> ''' <remarks></remarks> Public Function ExecuteNoProcReader(ByVal spName As String) As sqlDataReader Dim dataReader As sqlDataReader = Nothing Dim conn As New sqlConnection(connStr) Dim cmd As New sqlCommand(spName,conn) Try conn.Open() '当关闭dataReader的时候系统自动关闭数据库连接 dataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection) Return dataReader Catch ex As Exception MsgBox(ex.Message) Return Nothing Finally '这些语句不能有,因为该函数的返回值dataReader,要供其他函数调用, '需要保证dataReader的状态为开启,就像一扇门,如果关着的话,别人是无法进入其中进行操作的。 'conn.Close() 'cmd.Dispose() 'cmd = Nothing 'dataReader.Close() End Try End Function ''' <summary> ''' 利用泛化机制,返回一个泛化接口指定的实体类的一条记录 ''' </summary> ''' <typeparam name="T">泛化的实体类的类型</typeparam> ''' <param name="spName">存储过程名</param> ''' <param name="strategy">策略接口</param> ''' <returns>泛化的实体类的一条记录</returns> ''' <remarks></remarks> Public Function OperatorProcEntity(Of T)(ByVal spName As String,ByVal strategy As IDataReaderToEntityStrategy(Of T)) As T Dim dataReader As sqlDataReader dataReader = ExecuteNoProcReader(spName) Try If dataReader.HasRows = False Then MsgBox("没有数据可以读取,您需要先往数据库中添加配置信息后,系统才可正常使用!") Return Nothing Else While dataReader.Read() Return strategy.DataReaderToEntity(dataReader) End While End If Catch ex As Exception MsgBox(ex.Message) Return Nothing Finally dataReader.Close() End Try End Function
4、BasicDataDAL类:
上面做了一系列的铺垫,可谓磨刀霍霍向猪羊,此类才是真正的刀子——去执行返回实体类的动作。
这里值得注意的是我不太清楚BasicDataDAL类与BasicDataReaderToEntityStrategy是关联关系?还是依赖关系?下面是代码,望路过的高手多给指点。
Imports System.Windows.Forms Imports Model.BasicData Imports Utility Imports System.Data.sqlClient Imports System.Collections.Generic Public Class BasicDataDAL Implements IDAL.IBasicDataDAL Private sqlHelp As New Utility.sqlServerDALHelp Private addParam As New Utility.PublicMethod ''' <summary> ''' 获取基本配置表对象 ''' </summary> ''' <returns>基本配置表实体类</returns> ''' <remarks></remarks> Public Function GetData() As Model.BasicData Implements IDAL.IBasicDataDAL.GetData Dim spName As String spName = "proc_GetBasicData" Try Return sqlHelp.OperatorProcEntity(spName,BasicDataReaderToEntityStrategy.GetInstance()) Catch ex As Exception MsgBox(ex.Message) Return Nothing End Try End Function
这样就在DAL层完成了一个返回基本表实体类的一条记录的操作,剩下的工作就交代给其他的各层去执行就行,没有其他的变化。
有关泛化的进一步深入,可参考:http://www.yesky.com/SoftChannel/72342380468240384/20041116/1876323.shtml