Scala中的语言特性是如何实现的2

三层架构,通常意义上的三层架构就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。区分层次的目的即为了“高内聚,低耦合”的思想。

一、英文拓展:

    三层架构(3-Tier ASrchitecture)

    表现层UI(User Interface)

    业务逻辑层BLL(Business Logic Layer)

    数据访问层DAL(Data Access Layer)

 

二、各层作用解析:

  1、DAL作用:

    1)从数据源加载数据Select

    2)向数据源写入数据Insert/Update

    3)从数据源删除数据Delete 

  2、UI的作用:

    1)向用户展现特定业务数据。

    2)采集用户的输入信息和操作。

    3)特定的数据显示给用户

  原则:用户至上,界面简洁明了

  3、BLL的作用:

    1)从DAL中获取数据,供UI显示用。

    2)从UI中获取用户指令和数据,执行业务逻辑。

    3)从UI中获取用户指令和数据,通过DAL写入数据源。

  BLL的职责机制:

    UI——BLL——UI

    UI——BLL——DAL——BLL——UI

  4、数据模型的引入:

    为了避免三层之间的互相引用,所以出现Model,用于传输数据的,业务数据模型

 

三、系统登陆实例,步骤:

1、新建数据库

(名称)LoginDemo,包含两张表:

新建表Users

       

  其中,设定ID为主键,自增长。 

新建表Scores

      Scala中的语言特性是如何实现的2

  其中,设定ID为主键,自增长。 

2、编码阶段:

    解决方案名称:LoginSolution

    位置:LoginDemo

1)DAL数据访问层:

    新建项目名称:LoginDAL

    默认命名空间:Login.DAL

    添加类:UserDAO,ScoreDAO,DbUtil

    引用:LoginModel

复制代码
namespace Login.DAL
{
    class DbUtil
    {
        //sever机器名,Database数据库名,
        public static string ConnString = @"Server=192.168.**.**;Database=LoginDemo;User ID=sa;Password=123456";    
    }
}

namespace Login.DAL
{
    //每成功登陆一次用户,增加10点积分。
    public  class ScoreDAO
    {
        public void UpdateScore(string userName, int value)
        {
            using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))
            {
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = @"INSERT INTO SCORES(UserName,Score) Values (@UserName,@Score)";
                
                cmd.Parameters.Add(new SqlParameter("@UserName", userName));
                cmd.Parameters.Add(new SqlParameter("@Score", value));

                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
    }
}

namespace Login.DAL
{
    public  class UserDAO
    {
        //根据userName和password返回一个布尔值。
        public Login.Model.UserInfo SelectUser(string userName, string password)
        {
            {
                //有了using以后,connection就可以自动关闭 了
                SqlConnection conn=new SqlConnection (DbUtil .ConnString );
                {
                    SqlCommand cmd=conn.CreateCommand ();
                    cmd.CommandText=@"SELECT ID,UserName,Password,Email FROM USERS WHERE UserName=@UserName AND Password=@Password";
                    cmd.CommandType=CommandType .Text;
                    cmd.Parameters.Add(new SqlParameter ("@UserName",userName));
                    cmd.Parameters.Add(new SqlParameter ("@Password",password));

                    conn.Open();
                    SqlDataReader reader = cmd.ExecuteReader();
                    
                    //设置user的默认值为null
                    Login.Model .UserInfo user=null;
                    while (reader.Read())
                    {
                        if (user==null )
                         {
                            //如果user是null的话,则延迟加载
                             user=new Login .Model .UserInfo ();
                         }
                        user.ID=reader.GetInt32(0);
                        user.UserName=reader.GetString(1);
                        user.Password=reader.GetString(2);//not suggestion

                        //如果Email不是null的话,才可以去读。
                        if (!reader.IsDBNull(3))
                        {   
                             user.Email=reader.GetString(3);
                        }
            
                    }
                    return user;

                }
            }
        }
    }
}
复制代码

 

2)UI表示层:

    添加新项目,Windows窗体应用程序。

    名称:LoginUI ,设置为启动项目

    默认命名空间:Login.UI

    引用:LoginBLL,LoginModel

 

    登陆:btnLogin

    用户名:(Name):txtUserName

    密码: (Name):txtPassword; PasswordChar:*

    窗体: Text:系统登陆; MaximizeBox:False; MinimizeBox:False; FormBorderStyle:FixedSingle

 

?
namespace  LoginUI
{
     public  partial  class  Form1 : Form
     {
         public  Form1()
         {
             InitializeComponent();
         }
 
 
         private  void  btnLogin_Click( object  sender, EventArgs e)
        
             ////通常,直接使用DAO呼叫数据库。
             //IDbConnection conn = new SqlConnection("c....");
             //IDbCommand cmd = conn.CreateCommand();
             //cmd.CommandText = "Select UserName From USERS WHERE .....";
             //cmd.ExecuteReader();
 
         //利用三层架构,需要引用下一层的
             string  userName = txtUserName.Text.Trim();
             string  password = txtPassword.Text;
             Login.BLL.LoginManager mgr = new  Login.BLL.LoginManager();
            Login.Model .UserInfo user= mgr.UserLogin(userName, password);
 
            MessageBox.Show( "登陆用户:" +user.UserName );
         }     
     }
}

 

3)BLL业务逻辑层:

    添加新项目;

    名称:LoginBLL

    默认命名空间:Login.BLL

    添加新类:LoginManager/LoginService服务

    引用:LoginDAL,LoginModel 

复制代码
namespace Login.DAL
{
    class DbUtil
    {
        //sever机器名,Database数据库名,
        public static string ConnString = @"Server=192.168.**.**;Database=LoginDemo;User ID=sa;Password=123456";    
    }
}

namespace Login.DAL
{
    //每成功登陆一次用户,增加10点积分。
    public  class ScoreDAO
    {
        public void UpdateScore(string userName, int value)
        {
            using (SqlConnection conn = new SqlConnection(DbUtil.ConnString))
            {
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = @"INSERT INTO SCORES(UserName,Score) Values (@UserName,@Score)";
                
                cmd.Parameters.Add(new SqlParameter("@UserName", userName));
                cmd.Parameters.Add(new SqlParameter("@Score", value));

                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
    }
}

namespace Login.DAL
{
    public  class UserDAO
    {
        //根据userName和password返回一个布尔值。
        public Login.Model.UserInfo SelectUser(string userName, string password)
        {
            {
                //有了using以后,connection就可以自动关闭 了
                SqlConnection conn=new SqlConnection (DbUtil .ConnString );
                {
                    SqlCommand cmd=conn.CreateCommand ();
                    cmd.CommandText=@"SELECT ID,UserName,Password,Email FROM USERS WHERE UserName=@UserName AND Password=@Password";
                    cmd.CommandType=CommandType .Text;
                    cmd.Parameters.Add(new SqlParameter ("@UserName",userName));
                    cmd.Parameters.Add(new SqlParameter ("@Password",password));

                    conn.Open();
                    SqlDataReader reader = cmd.ExecuteReader();
                    
                    //设置user的默认值为null
                    Login.Model .UserInfo user=null;
                    while (reader.Read())
                    {
                        if (user==null )
                         {
                            //如果user是null的话,则延迟加载
                             user=new Login .Model .UserInfo ();
                         }
                        user.ID=reader.GetInt32(0);
                        user.UserName=reader.GetString(1);
                        user.Password=reader.GetString(2);//not suggestion

                        //如果Email不是null的话,才可以去读。
                        if (!reader.IsDBNull(3))
                        {   
                             user.Email=reader.GetString(3);
                        }
            
                    }
                    return user;

                }
            }
        }
    }
}
复制代码

 

4)Modle数据模型:

   添加新建项目:

    名称:LoginModel

    默认命名空间:Login.Model

    添加类:UserInfo

    Model数据模型:是独立于其余层次的,不知道其余层次的信息,其余层次都会引用Model。介于UI和BLL,此处表示我们想要返回的数据为USER对象。

 

复制代码
namespace Login.Model
{
    public  class UserInfo
    {
        public int ID { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }
    }
}
复制代码

 

 

上篇博文的末尾留了三个问题,现在自问自答一下。

在Scala中被声明为val的v4为什么在反编译的Java中不是final的呢?

在方法中声明局部变量时,如果用Scala的val关键字(或者是Java中的final)来修饰变量,则代表着此变量在赋过初始值之后不可以再被重新赋值。这个val或者final只是给编译器用的,编译器如果发现你给此变量重新赋值会抛出错误。

而bytecode不具备表达一个局部变量是immutable的能力,也就是说对于JVM来说,不存在不可变的局部变量这个概念。所以v4在反编译之后,就和普通的局部变量无异了。

在Scala中被声明为val的v2为什么在反编译的C#中不是readonly的呢?

这是个挺tricky的问题,我试着解释一下。Scala .NET是基于IKVM实现的,IKVM可以把Java bytecode翻译为CIL。 所以Scala编译为CIL的过程实际是这样的:

Scala —–Scala编译器—–> bytecode —–IKVM—–> CIL

Scala编译器编译出的bytecode实际是用final修饰了v2的,但是bytecode中的final和CIL中的initonly(对应C#的readonly)是不一样的。

Java中,final实例变量定义的时候,可以先声明,而不给初值,然后我们可以在任何一个方法中给它赋初值。这提供了更大的灵活性,一个Java类中的final成员可以依对象而不同,却保持其immutable的特征。

而CIL的initonly则要严格一点,CLI标准(ECMA-334)这样描述:

initonly marks fields which are constant after they are initialized. These fields shall only be mutated inside a constructor. If the field is a static field, then it shall be mutated only inside the type initializer of the type in which it was declared. If it is an instance field, then it shall be mutated only in one of the instance constructors of the type in which it was defined. It shall not be mutated in any other method or in any other constructor, including constructors of derived classes.

可见,一个initonly的成员,不是随便在哪儿都可以赋初值的。由于这点不同IKVM就没有直接把final翻译成initonly。如果想让v2在C#代码中变成readonly的,可以给IKVM加上strictfinalfieldsemantics这个参数。

为什么反编译出来的C#代码中的实例级公开方法都是标有override的呢?

这个问题还没搞明白。

但是有个有趣的现象,如果用Scala .NET来编译Scala源码,编译出的实例级方法都是标有override的;而如果先把Scala代码编译为.class然后再用IKVM把.class文件转换为CIL的话,方法则是标有virtual的。我猜这可能和Java中的方法默认是可以被overirde的有关。

下面开始正文,前面填坑用了不少篇幅,所以这次只分析一个语言特性:Scala中的constructor。

Constructor

Scala中可以在声明class的同时声明一个constructor,比如这样:

1
2
3
class ScalaConstructorExample(val x: Double, y: String) {  println(x + y) } 

构造函数接收两个参数x和y,然后把x和y拼在一起打印出来。反编译为Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ScalaConstructorExample {  private final double x;  public double x()  {  return this.x;  }  public ScalaConstructorExample(double x, String y)  {  Predef..MODULE$.println(new StringBuilder().append(x).append(y).toString());  } } 

可以发现编译器给标为val的x生成了一个getter,很方便的语法糖。而直接写在类内的打印语句则被放到了构造函数内。下面是反编译为C#的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ScalaConstructorExample : ScalaObject {  private double x = x;  public override double x()  {  return this.x;  }  public ScalaConstructorExample(double x, string y)  {  Predef$.MODULE$.println(new StringBuilder().Append(x).Append(y).ToString());  } } 

和Java代码基本无异。比较一下,Scala用3行代码表达的含义,Java和C#要用14行才行。

现在加一个重载的构造函数:

1
2
3
4
5
6
7
class ScalaConstructorExample(val x: Double, y: String) {  println(x + y)  def this(x: Double) = {  this(x, "hello")  } } 

这个构造函数给了y一个默认值“hello”。反编译为Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ScalaConstructorExample {  private final double x;  public double x()  {  return this.x;  }  public ScalaConstructorExample(double x, String y)  {  Predef..MODULE$.println(new StringBuilder().append(x).append(y).toString());  }  public ScalaConstructorExample(double x)  {  this(x, "hello");  } } 

对应的C#代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ScalaConstructorExample : ScalaObject {  private double x = x;  public override double x()  {  return this.x;  }  public ScalaConstructorExample(double x, string y)  {  Predef$.MODULE$.println(new StringBuilder().Append(x).Append(y).ToString());  }  public ScalaConstructorExample(double x) : this(x, "hello")  {  } } 

构造函数重载这个特性就显得平淡无奇了,不过还是比较一下行数。定义两个构造函数,打印出构造函数的参数,声明一个getter,这三件事Scala只用7行代码就完成了,Java和C#都需要将近20行。

 
分类:  三层学习

你可能感兴趣的:(C#,三层结构,系统登陆实例)