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

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

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

读前必备:编程

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

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

1.从一个实例解决问题开始设计模式

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

1.1最简单的数据访问设计app

咱们建立两个类,一个User类,一个UserSqlServer类,实现以下:ide

User.cs post

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

输出代码以下:ui

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
    {
        //数据库选择(Access\SqlServer\Oracle)
        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 程序设计

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

相关文章
相关标签/搜索