[.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 程序設計

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

相關文章
相關標籤/搜索