zoukankan      html  css  js  c++  java
  • 从3层开始

    最近面试很多人,每个人来,扯啊扯的都会扯到3层上,不论是有工作经验的还是没工作经验的,既然你要面试技术,3层总应该做些准备,以防被人问住。

    不过答案真是,超过一半以上的人,张口就说model。

    为了不必要的误会,我一般还会再细问一下,你的model是不是那种只有一堆属性,没有方法的?100%的都说是。

    最后招了几个刚毕业的小孩,在公司做培训。所以记录下讲的东西,以供之后再培训可以省点事

    从最最简单原始的3层

    为了说明问题,我写一些模拟代码,代码只为说明问题,不是真实的东西,不必纠结是否有什么实际意义

       1: public class View
       2: {
       3:     public void Search()
       4:     {
       5:         int age = 20;
       6:         string name = "张三";
       7:         Bll bll = new Bll();
       8:         var users=bll.Search(age,name);
       9:     }
      10: }
      11:  
      12: public class Bll
      13: {
      14:     public IEnumerable<int> Search(int age,string name)
      15:     {
      16:         SqlDal dal = new SqlDal();
      17:         //dal.Search(...);
      18:         return null;
      19:     }
      20: }
      21: public class SqlDal
      22: {
      23:     public IEnumerable<int> Search(/*....*/)
      24:     {
      25:         //....查库
      26:     }
      27: }

    用一个简单的例子大概说明一下。

    首先,3层是 表现层,业务逻辑层,数据访问层。从上到下,上层知道相邻的下层,下层不知道上层,上层也不知道下层的再下层。

    也就是说,表现层知道业务层,业务层知道数据访问层,表现层不知道数据访问层,反着,数据层不知道业务层,业务层不知道表现层。

    啥叫不知道呢,就是不感知,不关心。我干我的事,你爱咋地咋地。

    那数据访问层的下层是谁呢?就是数据库,说的小点就是sql,mysql之类的,说的大点就是存储介质(硬盘)

    按照上面的分层,业务逻辑层,是不应该知道存储介质的。

    这里插点其他的话。这3层里,核心是业务,因为对你的客户而言,有价值的是你的业务。

    表现,也并不仅仅局限于一种形式。同样的业务,我可以做b/s,做c/s,甚至做手机端,这都是表现层的东西,下面应该是统一的业务层来处理不同的表现层发来的数据。

    所以,3层和mvc是两码事,至少和asp.net mvc是两码事,你不用asp.net mvc还可以用web form,你不用b/s,还可以用win form,你不用.net,还可以用java,php,等等。业务逻辑可以以服务的方式对外提供,而被表现层使用

    对上上面的示例代码,有两方面的问题。

    第一,对业务层来说,怎么就那么确定你要new的是SqlDal了?如果由业务层来负责确定new SqlDal,那事实上,业务层已经知道存储介质了。这违背了分层的意义

    所以,业务层,不能排脑袋就指定了数据访问用SqlDal,他只是知道,有数据访问这么个东西,具体是啥,不知道。

    所以这里应该是面向接口的

       1: public class SqlDal:IDal
       2: {
       3:     public void Search(/*....*/)
       4:     {
       5:         //....查库
       6:     }
       7: }
       8:  
       9: public interface IDal
      10: {
      11: }

    首先,我们建个接口,然后让SqlDal实现接口

    关键在业务里怎么办?

    如果仅仅是把SqlDal dal=new SqlDal()改成IDal dal=new SqlDal()这一点意义也没有,还是你确定了new出来的。

    可以引入Factory来解决

       1: public class Bll
       2: {
       3:     public IEnumerable<int> Search(int age,string name)
       4:     {
       5:         var dal = Factory.GetDal();
       6:         //dal.Search(...);
       7:         return null;
       8:     }
       9: }
      10:  
      11: public class Factory
      12: {
      13:     public static IDal GetDal()
      14:     {
      15:         return new SqlDal();
      16:     }
      17: }

    此时,业务并不知道到IDal是谁,那是由Factory高速我的。

    我们还可以让别人的人来高速,谁呢?谁用我Bll,谁告诉我,我该用那个IDal

       1: public class Bll
       2: {
       3:     private IDal dal;
       4:  
       5:     public Bll(IDal dal)
       6:     {
       7:         this.dal = dal;
       8:     }
       9:  
      10:     public IEnumerable<int> Search(int age,string name)
      11:     {
      12:         //dal.Search(...);
      13:         return null;
      14:     }
      15: }

    这就改成了依赖注入的方式,我们通过构造函数注入这样的手段,来让使用bll的人,告诉bll改使用怎样的IDal。

    这时,bll是完全不知道存储介质的。我们可以用一个容器来解决注入的问题。

    第一个问题,相对比较好理解,加个注入容器就能很好的解决

    第二个问题:

    最终我们总是要去查库的,在使用原生sql的情况下,请问sql语句,或者更窄一点说,where语句,写在那里?

    第一种答案是写在业务层,那大概的代码会是这样

       1: //bll
       2: public IEnumerable<int> Search(int age,string name)
       3: {
       4:     string where = "where name='" + name + "' and age=" + age;
       5:     dal.Search(where);
       6:     return null;
       7: }
       8: //dal
       9: public IEnumerable<int> Search(string where)
      10: {
      11:     //....查库
      12:     var sql = "select * from tab " + where;
      13:     //...
      14: }

    这样带来的问题是,业务层要想拼接sql,就意味着,首先,你知道他一定是存在关系型数据库里的,其次你还知道表结构。这又是知道了与存储介质有关的东西了。

    但是好处是,数据访问层,只需要接收where,不需要关系上层是谁,上层发给我什么where,我就用什么where查

    第二种答案是写在数据访问层,那参数怎么传呢?一般的代码大概会是这样

       1: //bll
       2: public IEnumerable<int> Search(int age,string name)
       3: {
       4:     dal.SearchByNameAndAge(age,name);
       5:     return null;
       6: }
       7: //dal
       8: public IEnumerable<int> SearchByNameAndAge(int age, string name)
       9: {
      10: //....查库
      11: var sql = "select * from tab where ...";
      12: //...
      13:  
      14: }

    注意,我这里给dal的方法重新命名了一下,之所以要重新命名是因为,如果按照这样的传参形式,就意味着,对不同的业务逻辑的方法,会调用不同的数据访问层的方法。我每加一个业务逻辑的方法,可能都有一个与之对应的数据访问层上的方法(简单情况考虑)。

    这意味着,数据访问层,其实是知道业务逻辑了。但好处是,业务逻辑层不用知道具体的存储介质。

    那怎么才能在两种答案中,各取他们好的地方,而不取他们坏的地方呢?

    这个时候,我们需要一种通用的数据结构,来表达各式各样的查询条件,在业务逻辑层去构建好这个查询条件,传递到数据访问层,而由数据访问层来解析这个数据结构中的数据,把它翻译成真正的sql语句(或者其他)

    首先,我们象征性的建立一个表达where的数据结构

       1: public class WhereClass
       2: {
       3: }

    然后来看两层之间的数据传递

       1: //bll
       2: public IEnumerable<int> Search(int age,string name)
       3: {
       4:     var where = new WhereClass();
       5:     //用age参数和namge参数,给where赋值
       6:     dal.Search(where);
       7:     return null;
       8: }
       9: //dal
      10: public IEnumerable<int> Search(WhereClass where)
      11: {
      12: //....查库
      13:     var wheresql = "...";//解析whereclass生成
      14:     var sql = "select * from tab "+wheresql;
      15: //...
      16:  
      17: }

    到目前为止,业务逻辑层是不知道存储介质的,而数据访问层,只要写一个通用的search方法,就可以了,他也不用知道具体有什么业务逻辑。

    可是,开发难度,其实是增加了;我们要定义一个通用的数据结构来表达where,还要为这个数据结构写一个sql翻译器,说不定还得写什么mysql翻译器,oracle翻译器等等。想想就很苦逼

    不过不管怎么样,先大概想想吧。

    一个where语句,各种条件的and,or,然后还有括号,括号里面,又可以and or,又可以括号,哎呀,就是个树啦。

    其实微软已经帮我们做了这样一个通用的结构了,而且大部分的数据库也做了这种结构的翻译器。

    就是linq啦,这种结构就是Expression<>

    到这个时候,能够发现,数据访问层,其实已经不能算一层了,他更像是一个通用的数据访问组件,他可能并没有很多的类,很多的方法。

    数据访问组件,对内把expression翻译成具体存储介质可以识别的东西去执行,对外以类的形式体现以供调用。

    那些orm,就干的这个事咯。

  • 相关阅读:
    第十天python3 函数的销毁
    第九天python3 闭包
    第八天pyhton3 函数的返回值、作用域
    第七天python3 函数、参数及参数解构(二)
    音视频不同步排查方法
    第六天python3 函数、参数及参数解构(一)
    第五天python3 内建函数总结
    第四天python3 python解析式-生成器-迭代器

    [转载]基于Java反序列化
  • 原文地址:https://www.cnblogs.com/czcz1024/p/4089474.html
Copyright © 2011-2022 走看看