• LINQ to SQL活学活用(2):躲起来别让我看见


    Creative Commons License

    本文基于署名 2.5 中国大陆许可协议发布。作者:李永京(http://www.cnblogs.com/lyj/),原文地址:(请在这里替换原文地址URL)。

     

    引入

    看到上一篇的编写的测试吗?LINQ to SQL类完全暴露给了客户(这里指测试,接下来几篇将是UI表现层),客户完全操作数据(例如上一节的创建Customer),这篇我们要隐藏数据访问层的实现细节,躲起来别让我看见!

    注意:这是我第一次分析设计,我只想通过这个系列来讨论学习设计方面的东西,当然很多思想还不成熟或者有错误,只是希望通过这个系列来学习构架设计方面的东西,希望大牛们指点,大家拍砖头!

    改进

    这可以考虑到GoF23中的外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

    考虑在数据访问层中建立外观Facade,这样可以为复杂的数据访问方法提供一个简单的类,使得耦合大大降低。增加外观Facade可以只向客户提供一个简单的接口,减少客户与数据访问层之间的依赖,更容易维护和扩展了。

    数据访问层

    我们就使用这种方法来改进第一篇的程序吧。首先创建一个基类用于存放公共的方法,然后各个数据访问对象(这里只有Customer)继承这个基类实现具体细节。

    架构

    1.DataAccessFacadeBase基类

    使用一个公共基类,用于存放公共的方法,我们新建DataAccessFacadeBase.cs类,实例化数据访问实体DataContext,配置DataContext连接和日志。

    这里实例化数据访问实体DataContext代码同上一篇测试基类的代码相同,这里就不贴了。

    2.数据访问Facade对象

    我们为每个数据访问对象都定义一个数据访问Facade对象,用于封装一些CURD等操作,在具体数据访问类中没有Load()、Save()、Delete()方法。这样做的好处是客户使用时直接调用数据访问外观Facade里的对外提供的方法,更加基于服务,调用代码不需要担心具体的实现细节,也可很方便的对其测试。

    这里新建CustomerFacade.cs类用于封装对Customer的CURD操作,这个类继承自DataAccessFacadeBase基类,具体操作方法如下:

    1.新建临时Customer对象

    public Customer CreateCustomer()
    {
        return new Customer();
    }

    2.按CustomerId获取Customer对象

    public Customer GetCustomerById(int customerId)
    {
        return (from p in DataContext.Customer
                where p.CustomerId == customerId
                select p).FirstOrDefault();
    }

    3.获取Customer对象列表

    public IList<Customer> GetCustomerList()
    {
        return (from p in DataContext.Customer
            select p).ToList<Customer>();
    }

    4.更新保存Customer对象

    public void UpdateCustomer(Customer customer)
    {
        if (customer == null)
        {
            throw new ArgumentNullException("Customer", "对象为空");
        }
        else
        {
            if (customer.CustomerId == 0)
            {
                DataContext.Customer.InsertOnSubmit(customer);
            }
            DataContext.SubmitChanges();
        }
    }

    这里为了演示,仅仅写出了4个方法,大家可以按照自己的需要添加一些操作。

    单元测试层

    可以测试上面我们修改的结果了,在单元测试层新建一CustomerFacadeFixture.cs类依然继承测试基类UnitTestBase。

    Step1:实例化CustomerFacade

    在这个测试类中,首先实例化CustomerFacade。

    private CustomerFacade m_facade;
    public CustomerFacade Facade
    {
        get
        {
            if (m_facade == null)
            {
                m_facade = new CustomerFacade();
            }
            return m_facade;
        }
    }

    Step2:编写保存更新方法

    其次,编写一个创建并保存Customer的方法,因为我们每次测试前,数据库为空的,在测试前,我们需要输入一些原始数据。

    private Customer CreateAndSaveNewCustomer(string firstName, string lastName)
    {
        Customer newCustomer = Facade.CreateCustomer();
        newCustomer.FirstName = firstName;
        newCustomer.LastName = lastName;
        Facade.UpdateCustomer(newCustomer);
        return newCustomer;
    }

    从这个方法,我们就直接使用Facade提供给客户端的Create()方法和Update()方法,我们完全不知道具体的实现细节。

    Step3:测试UpdateCustomer()方法

    调用上面的方法,向创建保存为YJingLee的Customer为测试初始数据,正好也是测试了保存数据方法。

    [Test]
    public void UpdateCustomerTest()
    {
        Customer newCustomer = CreateAndSaveNewCustomer("YJing", "Lee");
        Assert.AreNotEqual(0, newCustomer.CustomerId);
        Assert.AreEqual("YJing", newCustomer.FirstName);
    }

    看看结果吧:

    结果

    分析一下:首先验证数据库是否存在,这里存在,删除原有的数据库重新创建一个新的数据库架构,向数据库中插入一条YJingLee的数据并查询这条数据。

    Step4:测试GetCustomerById()方法

    再来测试GetCustomerById()方法,首先在数据库中插入一条YJingLee的数据,看看这句reloaded = Facade.GetCustomerById(tempCustomer.CustomerId)调用外观Facade中的GetCustomerById()方法按CustomerId获取Customer对象,体现了对外隐藏具体的实现细节,最后断言数据是否符合预料的结果。

    [Test]
    public void GetCustomerByIdTest()
    {
        Customer tempCustomer = CreateAndSaveNewCustomer("YJing", "Lee");
        Assert.AreNotEqual(0, tempCustomer.CustomerId);
    
        Customer reloaded = Facade.GetCustomerById(tempCustomer.CustomerId);
        Assert.IsNotNull(reloaded);
        Assert.AreEqual(tempCustomer.CustomerId, reloaded.CustomerId);
        Assert.AreSame(tempCustomer, reloaded);
    }

    这个测试就留给大家测试了!测试好了把结果告诉我哦。 

    Step5:测试GetCustomerList()方法

    首先初始化三条数据,然后调用外观Facade中的GetCustomerList()方法获取Customer列表,测试是否一致

    [Test]
    public void GetListTest()
    {
        List<Customer> tempCustomers = new List<Customer>();
    
        tempCustomers.Add(CreateAndSaveNewCustomer("YJing", "Lee"));
        tempCustomers.Add(CreateAndSaveNewCustomer("li", "yongjing"));
        tempCustomers.Add(CreateAndSaveNewCustomer("cnblogs", "com"));
    
        var reloaded = Facade.GetCustomerList();
        Assert.IsNotNull(reloaded);
        Assert.AreEqual(tempCustomers.Count, reloaded.Count);
    }

    结语

    这篇文章我们通过修改第一篇完全裸露的代码,运用一个外观Facade类对外提供较清晰的接口来隐藏具体的实现细节,客户使用只需和Facade对象接口交互,从这篇的改进也完美地体现了依赖倒转原则和迪米特法则的思想。

  • 相关阅读:
    WCF双工通讯以及客户端间的间接通讯
    认识IoC
    学习“迷你ASP.NET MVC框架”后的小结
    MVP的PV模式与SC模式
    Android学习笔记(九) 视图的应用布局效果
    C# 动态编译
    C#中协变与抗变(逆变)
    线程池ThreadPool的初探
    关于异步的初步认识
    仿Office的程序载入窗体
  • 原文地址:https://www.cnblogs.com/wonder223/p/2747437.html
Copyright © 2020-2023  润新知