分层是为了减少层与层之间的依赖,增加程序的可读性,让整个系统结构清晰明确,还可大大降低维护成本,但是分层也有一定的缺点,有些可以直接访问数据库的层,却要通过负责访问数据库的层进行访问,这样,在访问数据库过程中就多出一个环节,增加了系统的开销,有时候要在表示层增加某个功能,为了降低耦合,就不得不自上而下,在每一层里面增加这个功能所需的服务类,这样就增加了开发成本
分层越多越好吗?答案是否定的,引用一句话“我们也要 时刻谨记:不能盲目分层,不应分层而分层不应模式而模式。这是很重要的。不然只能增加开发的负担(在今后的实践中更好的体会)。”应该是说要根据实际的情况进行分层,毕竟不是绝对的,因为有些系统不分层比分层有点更多一些。
基本的分层主要用的是三层架构:
表示层(UI)
主要用于与用户的交互,负责传达用户的指令以及数据给BLL层,并把用户需要的数据显示出来,通俗的讲就是用户能见到的界面,如窗体程序。
业务逻辑层(BLL)
对数据的逻辑处理,比如把访问数据库得到的数据,转换成用户向看到的数据,并提交给表示层进行显示。
数据访问层(DAL)
对数据库进行访问,提供增删改查等操作。
下面是我的包图,每个包就是一个层,其中增加了实体层(Model)、接口层(IDAL)和工厂层(Factory)
实体层(Model)
实际上就是对应的数据库里面的每一张表,一个表就建一个类,一个类里面的属性则是对应表里面的字段,比如表BaseData_Info
在实体层中,就可以这样建立一个类:
Public Class BaseDataEntity Inherits Entity Private FixedUserHalfHourCost As Single '固定用户半小时花费 Public Property FixdUserHalfHourCost_ As Single Get Return FixedUserHalfHourCost End Get Set(value As Single) FixedUserHalfHourCost = value End Set End Property Private CasualUserAnHourCost As Single '临时用户一小时花费 Public Property CasualUserAnHourCost_ As Single Get Return CasualUserAnHourCost End Get Set(value As Single) CasualUserAnHourCost = value End Set End Property Private IncreasingUnitTime As Single '单位递增时间 Public Property IncreasingUnitTime_ As Single Get Return IncreasingUnitTime End Get Set(value As Single) IncreasingUnitTime = value End Set End Property Private AtleastOnlineTime As Single '最少在线时间 Public Property AtleastOnlineTime_ As Single Get Return AtleastOnlineTime End Get Set(value As Single) AtleastOnlineTime = value End Set End Property Private ReadyTime As Single '准备时间 Public Property ReadyTime_ As Single Get Return ReadyTime End Get Set(value As Single) ReadyTime = value End Set End Property Private AtleastMoney As Single '最少金额 Public Property AtleastMoney_ As Single Get Return AtleastMoney End Get Set(value As Single) AtleastMoney = value End Set End Property End Class
有什么用呢?当你需要注册一个用户时候,你得从表示层(UI)把数据传递给用于访问数据库的数据访问层(DAL),但是,你不可能把用户注册的信息:学号,姓名,卡号,注册日期,注册时间,班级。。。。。。等等把参数传递给函数,进行增加行操作吧?
’你该不会真的这么传值吧?被吓到了 AddUser(StudentID,StudentName,CardID,RegisterDate,RegisterTime,Class。。。。。。) ’而如果你把这些数据封装成一个类,如上述,你只需要传递一个类就行了 AddUser(UserInfo)
对于数据库的访问,基本上什么语言都离不开这么几步:
1. 连接数据库
2. 执行sql语句
3. 返回sql语句的执行结构
而连接数据库的字符串,大家可以参考数据库连接字符串大全
执行sql语句可以参考sql语句大全
下面是一个简单的数据库访问例子:
ImportsSystem.Data.SqlClient ’引用sql客户端 Public Class SelectDAL Public Overloads Function SelectInfo(ByVal Table As String) '查询整张表 Dim DataS As New DataSet Try Dim SelectStr As String = "select * from " & Table '查询语句 Dim ConnectStr As String ="Data Source=服务器名/服务器地址;Initial Catalog=数据库名; UserID=sa;Password=123"'数据库连接字符串 Dim DBConnection As New SqlConnection(ConnectStr) '初始化数据库连接对象 DBConnection.Open() '连接数据库 'Dim DBCmd As New SqlCommand(SelectStr,DBConnection) '执行查询语句 Dim Adapter As New SqlDataAdapter(SelectStr,DBConnection) '把查询结果保存到缓存中 Adapter.Fill(DataS, Table) '把缓存中的表以Table为名保存到DataSet中 Catch ex As Exception Throw New ArgumentOutOfRangeException(""& ex.Message) End Try Return DataS End Function
其中连接数据库要用到的对象是SqlConnection,执行查询语句以及返回结果用的是SqlDataAdapter对象。
数据库的连接有很多种方式,如本地、远程等。只要参考参考数据库连接字符串,依据自身情况进行选择使用就可以达到目的。
对于反射,一开始我非常的茫然,在设计模式里面敲了例子之后,查了很多资料,也不知道怎么应用到收费系统中,确实笨到家了。不过,后来看到了高人的博客,才会了。看下图:
假如说你有BLL和DAL2个层,你希望在BLL里面的一个类A使用DAL里面的BalanceDAL类,你可以通过引用DAL从而达到目的,但是,据说为了减少BLL与DAL的耦合性,所以在BLL和DAL之间加了个接口层叫IDAL。如下图
可以看到框中的是上面DAL里面BalanceDAL的接口,这个接口里面包含了具体类(BalanceDAL)的所有方法,所以我们只要调用接口,就相当于调用了具体类(BalanceDAL),但是,前提是你必须把接口和具体类接通,就好比电视机和遥控器,遥控器是电视机接口,要想遥控器能控制接口,电视机里面应该要有一个能够接收并处理红外线新号的装置,当你使用遥控器的时候,遥控器跟电视机是联通的,他们之间通过了红外线联通。事实上,接口与具体类之间的关系,只是两者之间创建了一条管道,接口中没有具体的功能,但却有那个具体类使用的一个方法(就像遥控器上的按键),如果遥控器跟电视机之间没有联通,那么你按一下遥控器,是不会实现开机或者调频等功能的,所以,要使用接口,必须让接口与具体实现的类进行联通,这里有两步走,第一步是引用,第二步是创建实例。看下图:
在DAL的引用里把IDAL打上勾就行了也就相当于在这两个层之间架起了桥梁,接下来,我们让DAL来实现这个接口,也就是在DAL里面创建具体类之后,写上关键字+你要实现的接口,然后回车,它就会自动列出你在接口里写的方法,然后再方法里,你去具体的实现吧。
然后,我们需要在桥梁上面加管道,加了管道,就是把接口的方法和具体类的方法连接起来,使得我在BLL层里面调用接口的方法,就相当于调用了具体类的方法,但是我BLL并不知道具体类到底是怎么实现的,据说,这就是降低了BLL层与DAL层之间的耦合,因为我只关心接口IDAL就行了。如下代码,看BLL里是如何使用接口
Imports System.Reflection ’反射的引用 Public ClassBalanceBLL FunctionBalanceQuery(ByVal CardID AsString) IfCardID = "" Then ThrowNewArgumentOutOfRangeException("","请输入卡号:") EndIf DimIBalance As IDAL.IBalance '余额接口 IBalance = CType(Assembly.Load("DAL").CreateInstance("DAL.BalanceDAL"),IDAL.IBalance) '得到余额类 DimDS As NewDataSet Try DS = IBalance.SelectBalanceInfo(CardID) '查询并返回数据集结果 IfDS.Tables(0).Rows.Count < 1 Then Throw NewArgumentOutOfRangeException("","没有这个卡号的余额记录") EndIf Catchex As Exception ThrowNewArgumentOutOfRangeException("",ex.Message) EndTry ReturnDS EndFunction
这里,我们首先引用了反射,然后创建接口(IBalance),再然后就是通过反射,把DAL层里的BalanceDAL具体类的实例反射过来给接口,这样,我们就相当于在接口和具体类之间连接了管道,然后我们就可以使用具体类的方法了:IBalance.SelectBalanceInfo(CardID)'查询并返回数据集结果
后来发现,把DAL层里面的类反射到BLL层里面,也就相当于在BLL层里面生产了DAL层的类,然后反射得到具体类的那些代码,我便抽象出了一个层,名曰Factory层,只要BLL引用工厂,便可以通过工厂返回具体类了。这也就是反射+工厂的应用吧。
好像写的有点长,本来还有重载、配置文件、异常处理、存储过程等比较实用点的技术要写的,算了,放到下一篇吧。希望对大家有帮助。
虽说面向对象比较复杂,但是总体的感觉,它就是把很多复杂的东西分类、分块、分层、分......然后用线把他们窜起来,你不必完全的记住所有的分块,但你可以顺着那些图纸毫不保留的了解到细节,只要去习惯这样的思维方式,相信也不太难的。