[.net 面向對象程序設計進階] (21) 反射(Reflection)(下)設計模式中利用反射解耦html
本節導讀:上篇文章簡單介紹了.NET面向對象中一個重要的技術反射的基本應用,它可讓咱們動態的調用一個程序集中的成員,本篇文章將介紹如何將這一重要特性應用到設計模式中,達到swich……case,if……else帶來的耦合問題,讓咱們的代碼更漂亮,更靈活。數據庫
讀前必備:編程
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; } } }
輸出代碼以下: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(); } } }
輸出結果以下:
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;} } }
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; } } }
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; } } }
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); } }
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); } }
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; } } }
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; } } }
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; } } }
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; } } }
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; } } }
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; } } }
應用程序調用入口:
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>
==============================================================================================