[.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;
} }
}

输出代码如下:

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(); 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(); Product product = new Product();
IProduct productData = Factory.CreateProduct(); productData.InsertProduct(product);
productData.GetProduct(); 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 面向对象程序设计进阶] (21) 反射(Reflection)(下)设计模式中利用反射解耦的更多相关文章

  1. [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程

    [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...

  2. [.net 面向对象程序设计进阶] (1) 开篇

    [.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对 ...

  3. [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下)

    [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下) 本篇导读: 接上篇继续介绍SVN的高级功能,即使用分支并行开发.随着需求的不断变更,新功能的增加.特别是 ...

  4. [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)

    [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下) 本节导读: 上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这 ...

  5. [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能

    [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种. ...

  6. [.net 面向对象程序设计进阶] (14) 缓存(Cache) (一) 认识缓存技术

    [.net 面向对象程序设计进阶] (14) 缓存(Cache)(一) 认识缓存技术 本节导读: 缓存(Cache)是一种用空间换时间的技术,在.NET程序设计中合理利用,可以极大的提高程序的运行效率 ...

  7. [.net 面向对象程序设计进阶] (5) Lamda表达式(一) 创建委托

    [.net 面向对象程序设计进阶] (5) Lamda表达式(一)  创建委托 本节导读: 通过学习Lambda表达式,学会创建委托和表达式目录树,深入了解Lambda的特性,让你的代码变的更加清晰. ...

  8. [.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门

    [.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习 ...

  9. [.net 面向对象程序设计进阶] (27) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git

    [.net 面向对象程序设计进阶] (26) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git 本篇导读: 接上两篇,继续Git之旅 分布式版本控制系统 ...

随机推荐

  1. Problem with "AnyConnect was not able to establish connection to the specified secure gateway."

    Cisco的VPN客户端最近报"AnyConnect was not able to establish connection to the specified secure gateway ...

  2. webpack

    webpack 通过一个主文件 .js ,webpack把这个文件所有的依赖文件,都处理打包成js文件 webpack 可以干嘛?1.执行打包 (把require()模块化整合成一个js文件给html ...

  3. JDK7和JDK8一些重要新特性

    jdk7新特性(部分) switch支持字符串 List AutoCloseable接口实现自动关闭,在try()中 新增获取环境信息的工具方法,getJavaHomeDir,getUserHomeD ...

  4. Error:Execution failed for task ':clean'. > Unable to delete directory :\build\intermediates (转)

    第一种方法: build文件夹,可以使用360文件粉碎机删除,然后重启Android Studio即可! 转自 第二种方法: 进入studio,进入settings,搜索instant run,进入该 ...

  5. java内存模型(待完善)

    JMM 1.内存模型的抽象. 本地内存是JMM的一个抽象概念,并不是真实存在,它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化. 2.内存可见性问题? ? 3.重排序  编译器优化重排序   ...

  6. xpath定位实战(1)

    1.执行scrapy shell "https://book.douban.com/subject/2256039/"

  7. DFS序+线段树+bitset CF 620E New Year Tree(圣诞树)

    题目链接 题意: 一棵以1为根的树,树上每个节点有颜色标记(<=60),有两种操作: 1. 可以把某个节点的子树的节点(包括本身)都改成某种颜色 2. 查询某个节点的子树上(包括本身)有多少个不 ...

  8. md语法

    标题 标题 标题是每篇文章都需要也是最常用的格式,在 Markdown 中,如果一段文字被定义为标题,只要在这段文字前加 # 号即可. # 一级标题 ## 二级标题 ### 三级标题 以此类推,总共六 ...

  9. C#对象序列化与反序列化zz

      C#对象序列化与反序列化(转载自:http://www.cnblogs.com/LiZhiW/p/3622365.html) 1. 对象序列化的介绍........................ ...

  10. 已知服务器ftp的账号密码,求解数据库表的内容

    一开始觉得这两个是完全不相干的东西,直到出现了这样一个问题,对方网站只有ftp的账号密码,并且能正常访问到代码.但是当需求了解注册人数的时候,后台没有显示,只能到数据库去找,这时怎么找呢? 原来是可以 ...