一、什么是三层
我们在进行软件设计时,一个重要的思想就是“高内聚低耦合”,而以前我们所做的那些小项目,由于代码量少,结构简单,所有的操作都是在客户端和数据库之间(也就是两层架构)进行的,但是对于大型的项目而言,如果按照原来的两层架构进行设计,非常不利于软件的可维护、可复用和可扩展性,并且导致各个层之间的依赖紧密,不利于开发人员的开发,违背了高内聚低耦合的思想。
所谓三层结构就是在客户端和数据库之间加了一个中间层,也就是组件层,这里三层指的是逻辑上的三层,即把三层放到同一台机器上,它包括界面层(UI)、业务逻辑层(BLL)和数据访问层否(DAL)。
二、什么时候用三层
当业务逻辑复杂到一定程度,数据存储作为一个相对独立的数据存储介质的情况下,把数据访问层脱离业务层单独存在,业务逻辑脱离开界面层二单独存在。
三、为什么使用三层
我们之前用两层架构,将用户界面控制业务逻辑和数据访问放在一起作为一层,数据库单独作为一层,这样一来,如果数据库或用户界面发生改变时,需要重新开发整个系统。
应用三层架构,实现了高内聚低耦合的思想,将用户见面、业务逻辑、数据访问分开,当数据库或用户界面发生改变时,不需要重新开发只做简单的调整即可。
四、优缺点
优点
1、开发人员可以只关注整个结构中的其中某一层;
2、可以很容易的使用新的实现来替换原有的层次的实现
3、可以降低层与层之间的依赖
4、有利于标准化
5、有利于个逻辑层的复用
6、结构更加明确
7、在后期维护时,极大的降低了维护成本和维护时间
缺点
1、降低系统性能。由于增加了层,访问数据库时要多经历了一步,所以性能效率有所降低。
2、有时候会导致级联的修改。如果在表示层增加一个功能,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。
3、增加开发成本。
五、各层的作用
界面层(UI)
位于最外面,进行用户界面设计,向用户展现特定业务数据,提供交互界面,并且采集用户的输入信息和操作。
数据访问层(DAL)
只限于和数据源打交道
实现数据的增、删、改、查
业务逻辑层(BLL)
是DAL和UI之间的桥梁,负责数据的处理和传递。
从DAL中获取数据,以供UI使用
从UI中获取用户指令和数据后执行业务逻辑
从UI中获取用户指令和数据后,通过DAL写入数据源
model层(实体层)
并不属于三层中的任何一层,他的存在只是为了封装数据,使数据能够在三层之间顺畅的流转,内部与数据库中的表一一对应。
六、三层登录实现
应用三层实现最简单的登录功能。
界面层
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
//接收控件变量
string userID = txtname.Text.Trim();
string password = txtpassword.Text;
//实例化出一个业务逻辑层mgr
Login.BLL.LoginManager mgr = new Login.BLL.LoginManager();
//传入参数到业务逻辑层处理后,将处理好的数据封装在model层中
Login.Model.UserInfo user= mgr.UserLogin(userID, password);
MessageBox.Show("登录用户:" + user.ID);
}
}
业务逻辑层
namespace Login.BLL
{
public class LoginManager
{
public Login.Model .UserInfo UserLogin(string userID,string Password)
{
//实例化数据访问层的UserDAO类为udao
Login.DAL.UserDAO udao = new Login.DAL.UserDAO();
//传入数据后,用user接收数据访问后的结果
Login.Model.UserInfo user= udao.SelectUser(userID, Password);
//如果数据访问结果不为空
if (user !=null )
{
//实例化数据访问层的ScoreDAO类为sDao
Login.DAL.ScoreDAO sDao = new Login.DAL.ScoreDAO();
//传入数据
sDao.UpdateScore(userID, 10);
return user;
}
else
{
throw new Exception("登录失败。");
}
}
}
}
model层
namespace Login.Model
{
public class UserInfo
{
//定义好user需要的属性,
public int ID { get; set; }
public string UserName { get; set; }
public string Passsword { get; set; }
public string Email { get; set; }
}
}
数据访问层
namespace Login.DAL
{
public class UserDAO
{
public Login.Model .UserInfo SelectUser (string userID,string password)
//定义两个参数,接收业务逻辑层传来的数据
{
//实例出一个数据连接conn,using之后,连接自动关闭
using (SqlConnection conn =new SqlConnection(DbUtil.ConnString))
{
//定义一个查询语句
SqlCommand cmd = conn.CreateCommand();
//知识(@符号1、忽略转义字符,像路径中的\符号。2、让字符串跨行。3、标识符用法,加上后可以使用关键字。本处为2
cmd.CommandText = @"SELECT ID,UserName,Password,Email
FROM USERS WHERE ID=@userID AND Password=@password";
//将UserID和password作为要查询语句的条件,给cmd
cmd.CommandType = System.Data.CommandType.Text;
cmd.Parameters.Add(new SqlParameter("@userID", userID));
cmd.Parameters.Add(new SqlParameter("@password", password));
//打开连接
conn.Open();
//执行查询语句
SqlDataReader reader = cmd.ExecuteReader();
//初始化user属性为空
Login.Model.UserInfo user = null;
while (reader.Read())
{
if (user ==null)
{
//实例出一个model为user
user = new Login.Model.UserInfo();
}
//查询后,为model赋值
user.ID = reader.GetInt32(0);
user.UserName = reader.GetString(1);
user.Passsword = reader.GetString(2); //not suggestion
if (!reader.IsDBNull (3))
{
//如果email的查询结果不为空,则为model的email属性赋值
user.Email = reader.GetString(3);
}
}
//返回model层实例出来的user(此时已赋好值)
return user;
}
}
}
}
namespace Login.DAL
{
public class ScoreDAO
{
public void UpdateScore (string userID,int value)
{
//定义一个数据连接conn,using之后连接自动关闭
using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))
{
//定义一个插入语句
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = @"INSERT INTO SCORES(ID,Score) values(@ID,@Score)";
//以userID和value作为插入条件,赋给cmd
cmd.Parameters.Add(new SqlParameter("@ID", userID));
cmd.Parameters.Add(new SqlParameter("@Score", value ));
//执行插入操作
conn.Open();
cmd.ExecuteNonQuery();
}
}
}
}
总结
在进行三层的时候,最重要的一点就是要弄清楚各层之间的功能,明白层与层之间的调用关系,在代码执行过程中,认真跟着代码走几遍,清楚实现的原理。