• [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦


    [.net 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦

    本节导读:上篇文章简单介绍了.NET面向对象中一个重要的技术反射的基本应用,它可以让我们动态的调用一个程序集中的成员,本篇文章将介绍如何将这一重要特性应用到设计模式中,达到swich……case,if……else带来的耦合问题,让我们的代码更漂亮,更灵活。

    读前必备:

    [.net 面向对象编程基础] (9) 类和类的实例
    [.net 面向对象编程基础] (16) 接口 

    [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能
    [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程

    1.从一个实例解决问题开始

     为了说的通俗易懂一些,本篇从一个常用实例开始,其实用过代码生成器的同学肯定很了解工厂反射模式了,这种模式应用在数据层面,主要解决了一个问题,那就是可以动态更换数据库而不需要改动代码,让我们的代码解耦。下面我们一步一步改进代码,最终实现我们的目标——动态数据访问层设计。

    1.1最简单的数据访问设计

    我们创建两个类,一个User类,一个UserSqlServer类,实现如下:

    User.cs 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class User
        {
            public int Id
            {
                get; set;
            }
    
            public string Name
            {
                get; set;
            }
        }
    }

    UserSqlServer.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class SqlServerUser
        {
            public void InserUser(User user)
            {
                Console.WriteLine("在SqlServer中新增一个用户");
            }
    
            public User GetUser(int id)
            {
                Console.WriteLine("在SqlServer中通过id获得一个用户记录,Id:"+id);
                return null;
            }
    
        }
    }
    View Code

    输出代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class Program
        {
            static void Main(string[] args)
            {
                User user = new User();
                SqlServerUser li = new SqlServerUser();
                li.InserUser(user);
                li.GetUser(1);
    
                Console.ReadKey();
            }
        }
    }
    View Code

    输出结果如下:

    1.2 利用工厂模式来改进

    上面的代码,让我们的代码死死的绑定在SqlServer上了,如果我们要换个Access数据库,换个Oracle呢,于是我们利用工厂模式改进一下。

    首先说一下什么是工厂模式,简单说就是通过一个抽象出一个工厂类,可以实例不同的类,来达到解耦。

    为了能让代码写的活一些,我们用工厂模式来改进上面的代码,假如除了User类之外,还有一个Product类,我需要代码实现能随时更换数据库。为了达到这个要求,我们创建了接口IUser,IProduct,然后分别用三种数据库的操作类来实现这两个接口,最后我们创建一个工厂类Factory,在工厂类中,我们可以更换数据库,来动态调用不同数据层。下面是类图:

     

    程序集结构如下:

    下面是具体代码:

    User.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class User
        {
            public int Id{get; set;}
            public string Name{get; set;}
        }
    }
    View Code

    Product.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class Product
        {
            public int ProductId { get; set; }
    
            public string productName { get; set; }
        }
    }
    View Code

    Factory.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class Factory
        {
            //数据库选择(AccessSqlServerOracle)
            private static readonly string db = "Access";
            public static IUser CreateUser()
            {
                IUser result = null;
                switch (db)
                {
                    case "Access":
                        result = new DataAccessUser();
                        break;
                    case "SqlServer":
                        result = new DataSqlServerUser();
                        break;
                    case "Oracle":
                        result = new DataOracleUser();
                        break;
                }
                return result;
            }
            public static IProduct CreateProduct()
            {
                IProduct result = null;
                switch (db)
                {
                    case "Access":
                        result = new DataAccessProduct();
                        break;
                    case "SqlServer":
                        result = new DataSqlServerProduct();
                        break;
                    case "Oracle":
                        result = new DataOracleProduct();
                        break;
                }
                return result;
            }
    
        }
    }
    View Code

    IUser.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        interface IUser
        {
            void InserUser(User user);
            User GetUser(int id);
          
        }
    }
    View Code

    IProduct.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        interface IProduct
        {
            void InsertProduct(Product product);
            Product GetProduct(int id);
        }
    }
    View Code

    DataAccessUser.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class DataAccessUser : IUser
        {
            public void InserUser(User user)
            {
                Console.WriteLine("在Access中新增一个用户");
            }
    
            public User GetUser(int id)
            {
                Console.WriteLine("在Access中通过id获得一个用户记录,Id:" + id);
                return null;
            }
        }
    
    
    }
    View Code

    DataSqlServerUser.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class DataSqlServerUser : IUser
        {
            public void InserUser(User user)
            {
                Console.WriteLine("在SqlServer中新增一个用户");
            }
    
            public User GetUser(int id)
            {
                Console.WriteLine("在SqlServer中通过id获得一个用户记录,Id:" + id);
                return null;
            }
        }
    }
    View Code

    DataOracleUser.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class DataOracleUser : IUser
        {
            public void InserUser(User user)
            {
                Console.WriteLine("在Oracle中新增一个用户");
            }
    
            public User GetUser(int id)
            {
                Console.WriteLine("在Oracle中通过id获得一个用户记录,Id:" + id);
                return null;
            }
        }
    }
    View Code

    DataAccessProduct.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class DataAccessProduct : IProduct
        {
    
            public void InsertProduct(Product product)
            {
                Console.WriteLine("在Access中新增一个产品");
            }
    
            public Product GetProduct(int id)
            {
                Console.WriteLine("在Access中通过id获得一个产品记录,Id:" + id);
                return null;
            }
        }
    }
    View Code

    DataSqlServerProduct.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class DataSqlServerProduct:IProduct
        {
            public void InsertProduct(Product product)
            {
                Console.WriteLine("在SqlServer中新增一个产品");
            }
    
            public Product GetProduct(int id)
            {
                Console.WriteLine("在SqlServer中通过id获得一个产品记录,Id:" + id);
                return null;
            }
        }
    }
    View Code

    DataOracleProduct.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class DataOracleProduct: IProduct
        {
            public void InsertProduct(Product product)
            {
                Console.WriteLine("在Oracle中新增一个产品");
            }
    
            public Product GetProduct(int id)
            {
                Console.WriteLine("在Oracle中通过id获得一个产品记录,Id:" + id);
                return null;
            }
        }
    }
    View Code

    应用程序调用入口:

    Program.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataBase
    {
        class Program
        {
            static void Main(string[] args)
            {
                User user = new User();
                IUser userData = Factory.CreateUser();
               
                userData.InserUser(user);
                userData.GetUser(1);
    
                Product product = new Product();
                IProduct productData = Factory.CreateProduct();
    
                productData.InsertProduct(product);
                productData.GetProduct(1);
    
                Console.ReadKey();
            }
        }
    }

    运行结果如下:

     

    1.3 利用工厂反射模式继续改进

    上面的设计,已经能够实现多个数据库切换了,但是我们依然要改动工厂类的代码,来实现这一目标。并且还是有很多case语句来实现不同数据库的调用,这样如果不仅仅只有User和Product类,那么代码量也是相当大的。我们利用本节重点要说的.NET特性,就可以达成目标了。

    先看改进后的类图:

    增加了一个缓存类,可以提高反射运行效率,改进了工厂类Factory,在工厂中使用反射,从而不再需要使用switch……case,if……else来写不同数据库的选择。为了实现不需要改动代码而动态变更数据库,我们使用了配置文件,在程序运行过程中,只需要更改配置文件中的数据库类型和对应的连接字符,即可以动态实现了切换到不同数据库。下面是改进后的工厂类和配置文件

    Factory.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Configuration;
    using System.Reflection;
    namespace DataBase
    {
        class Factory
        {
            //能过Config文件来更改数据库
            private static readonly string db = System.Configuration.ConfigurationManager.AppSettings["db"];
            //程序集
            private static readonly string AssemblyPath = "DataBase";
            /// <summary>
            /// 创建对象或从缓存获取
            /// </summary>
            public static object CreateObject(string AssemblyPath, string ClassNamespace)
            {
                object objType = MyCache.GetCache(ClassNamespace);//从缓存读取
                if (objType == null)
                {
                    try
                    {
                        objType = Assembly.Load(AssemblyPath).CreateInstance(ClassNamespace);//反射创建
                        MyCache.SetCache(ClassNamespace, objType);// 写入缓存W
                    }
                    catch (Exception ex)
                    { }
                }
                return objType;
            }
            /// <summary>
            /// 创建数据层接口IUser
            /// </summary>
            public static IUser CreateUser()
            {
                string ClassNamespace = AssemblyPath + ".Data" + db + "User";
                object objType = CreateObject(AssemblyPath, ClassNamespace);
                return (IUser)objType;
            }
            /// <summary>
            /// 创建数据层接口IUser
            /// </summary>
            public static IProduct CreateProduct()
            {
                string ClassNamespace = AssemblyPath + ".Data" + db + "Product";
                object objType = CreateObject(AssemblyPath, ClassNamespace);
                return (IProduct)objType;
            }
        }
    }

    下面是缓存类(需要引用System.Web.dll)

    MyCache.cs

    using System;
    using System.Web;
    namespace DataBase
    {
        /// <summary>
        /// 缓存相关的操作类
        /// </summary>
        public class MyCache
        {
            /// <summary>
            /// 获取当前应用程序指定CacheKey的Cache值
            /// </summary>
            /// <param name="CacheKey"></param>
            /// <returns></returns>
            public static object GetCache(string CacheKey)
            {
                System.Web.Caching.Cache objCache = HttpRuntime.Cache;
                return objCache[CacheKey];
            }
            /// <summary>
            /// 设置当前应用程序指定CacheKey的Cache值
            /// </summary>
            /// <param name="CacheKey"></param>
            /// <param name="objObject"></param>
            public static void SetCache(string CacheKey, object objObject)
            {
                System.Web.Caching.Cache objCache = HttpRuntime.Cache;
                objCache.Insert(CacheKey, objObject);
            }
            /// <summary>
            /// 设置当前应用程序指定CacheKey的Cache值
            /// </summary>
            /// <param name="CacheKey"></param>
            /// <param name="objObject"></param>
            public static void SetCache(string CacheKey, object objObject, DateTime absoluteExpiration, TimeSpan slidingExpiration)
            {
                System.Web.Caching.Cache objCache = HttpRuntime.Cache;
                objCache.Insert(CacheKey, objObject, null, absoluteExpiration, slidingExpiration);
            }
        }
    }

    配置文件:

    App.config

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
        </startup>
      <appSettings>
    
        <!--数据库类型选择 ConnectionType:Access|SqlServer|Oracle|MySql|Xml  -->
        <add key="db" value="SqlServer"/>
        <!-- 数据库连接字符串 -->
        <add key="ConStrAccess" value="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:aa.accdb;Persist Security Info=False"/>
        <add key="ConStrSqlServer" value="server=(local)SQLEXPRESS;database=data;uid=sa;pwd=123456"/>
        <add key="ConStrOracel" value="Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=local)(PORT=1521)) User ID=scott;Password=tiger;Unicode=True"/>
        <add key="ConStrMySql" value="server=localhost;user id=root;password=123456;database=ABC; pooling=true;"/>
        <add key="ConStrXml" value="C:/Data.xml"/>
      </appSettings>
    </configuration>

    下面是我们在app.config中选择了其中一个数据库后的输出结果:

     

    我们更改为Oracle看看输出结果:

     

    2.本节要点:

    可以看到,完全达到了我们的目标:

    A.多数据库切换

    B.切换数据库不需要改动代码,从而减少重新编译重新部署的麻烦

    C.各个数据库操作类相互独立,易于维护

    D.使用反射解决了switch if等分支判断带来的耦合。

    ==============================================================================================

    返回目录

    <如果对你有帮助,记得点一下推荐哦,如有有不明白或错误之处,请多交流>

    <对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>

    <转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>

    .NET 技术交流群:467189533 .NET 程序设计

    ==============================================================================================

  • 相关阅读:
    使用JS实现复制粘贴功能
    前端向后端发送请求(FormData),你们不要吐槽我,有的时候我也不想写注释
    最全面的数组去重详细解析
    查找字符串数组中的最长公共前缀
    最简单的让多行表格滚动方法
    送给vue初学者的 vue.js技巧
    git 和码云的上传文件代码操作
    常用模块 二
    深拷贝与浅拷贝
    常用模块升级
  • 原文地址:https://www.cnblogs.com/yubinfeng/p/5174442.html
Copyright © 2020-2023  润新知