[.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. java 读写properties (配置)文件

    Properties属性文件在Java应用程序中是经常可以看得见的,也是特别重要的一类文件.它用来配置应用程序的一些信息,不过这些信息一般都是比较少的数据,没有必要使用数据库文件来保存,而使用一般的文 ...

  2. Axure RP = Axure Rapid Prototyping

    不要一味追求高保真,特别是交互后产生动态数据.并且将动态数据交互传递出去,违背了做原型的初衷了. 自己做着玩追求高保真可以,有成就感. 但作为工作的话,效率优先.能简单直观地展示必要的交互效果即可.

  3. ci框架登陆之后每隔几分钟就需要重新登录的问题

    一个简单的登陆写好之后,发现每次进入需要登陆之后才能进入的页面都会跳转到登录页面,猜测应该是session被清了,打印出来,果然为空,但是我没有设置session的生存周期,按照默认的应该是24小时, ...

  4. ThreadLocal内部机制及使用方法

    一.介绍ThreadLocal内部机制之前,先简单说明一下其特点及用途: 1.ThreadLocal是单线程内共享资源,多线程间无法共享(即线程A访问不了线程B中ThreadLocal存放的值): 2 ...

  5. 1.Linux中安装LNMP过程

    第一步安装mysql过程 安装包mysql-5.0.22.tar.gz,解压tar -zxvf  mysql-5.0.22.tar.gz cd mysql-5.0.22 进行源码安装./configu ...

  6. TAQSkinScrollBar 类美化滚动条再讨论

    再说:TAQSkinScrollBar 类美化滚动条,http://www.138soft.com/?p=156  里面有人提到不可以滚动 滚动的改善方法: unit AQSkinScrollBar; ...

  7. iOS之 利用通知(NSNotificationCenter)获取键盘的高度,以及显示和隐藏键盘时修改界面的注意事项

    我们在开发中会遇到这样的情况:调用键盘时需要界面有一个调整,避免键盘遮掩输入框. 但实现时你会发现,在不同的手机上键盘的高度是不同的.这里列举一下: //获取键盘的高度 /* iphone 6: 中文 ...

  8. JFreeChart

    花了四个小时给同学写的.还行吧,原来都没有用过到处找资料写的. package DrawLine; import org.jfree.chart.ChartFactory; import org.jf ...

  9. Eclipse 执行成功的 Hadoop-1.2.1 WordCount 源码

    万事开头难.最近在学习Hadoop,先是搭建各种版本环境,从2.2.0到2.3.0,再到1.2.1,终于都搭起来了,折腾了1周时间,之后开始尝试使用Eclipse编写小demo.仅复制一个现成的Wor ...

  10. [BZOJ1131][POI2008] Sta 树的深度

    Description 给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大 Input 给出一个数字N,代表有N个点.N<=1000000 下面N-1条边. Output ...