今天,我们接着来说说设计模式中的抽象工厂模式,继上次工厂模式之后我们来说说他们中的大佬抽象工厂。
首先,还是想象一个场景,今天我们用到的是我们项目中经常要用的切换数据库;
例如,一开始项目用的是mysql数据库,后来又要切换成SQLserver数据库
今天,我们就用这个场景来学习一下反射+抽象工厂模式。
使用的dataBase优先的话我们当然是在项目中新建实体了
class DepartMent { private int _id; public int ID { get { return _id; } set { _id = value; } } private string name; public string Name { get { return name; } set { name = value; } } }
class User { private int _id; public int ID { get { return _id; } set { _id = value; } } private string name; public string Name { get { return name; } set { name = value; } } }
这里我们新建两个实体,User和DepartMent,各有Id和Name两个属性,就是这么简单。
然后接下来我们要干嘛呢(^_^),当然是抽象啦,设计模式怎么少得了抽象呢
实体嘛,无非就是CRUD,所以我们就抽象他们
interface IDepartment { void Insert(DepartMent departMent); DepartMent GetDepart(int id); }
这里我们抽象出两个方法,获取对象和添加对象方法,细心的你应该会发现这里是写死的,我们的项目中,不可能只有一个表的吧,按照这种写法的话那岂不是每个表都要新建一个类吗,那岂不是类型数量要爆炸啊(汗颜)
这里我们不急,我们首先看一个是怎么实现的,学会了一个,其他还不是一样的道理吗(搬砖),接下来就是要去实现了
class AccessDepartment : IDepartment { public DepartMent GetDepart(int id) { Console.WriteLine("在Access中根据ID得到Department表一条记录"); return null; } public void Insert(DepartMent departMent) { Console.WriteLine("在Access中给Department表增加一条记录"); } }
class SqlserverDepartment : IDepartment { public DepartMent GetDepart(int id) { Console.WriteLine("在SQL server中根据ID得到DepartMent表一条记录"); return null; } public void Insert(DepartMent departMent) { Console.WriteLine("在SQL server 中给DepartMent表增加一条记录"); } }
这里我们新建了两个类,分别代表access数据库和SQLserver数据库,分别实现IDepartment接口,好了,实现了抽象接口,接下来就是创建了
class DataAccess { private static readonly string AssemblyName = "抽象工厂模式"; private static readonly string db = ConfigurationManager.AppSettings["DB"]; public static IDepartment CreateDepartment() { string className = AssemblyName + "." + db + "Department"; return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className); } }
创建一个DataAccess类,这里要解释下,首先AssemblyName这个属性是我们的命名空间,也就是我们的程序集名称,然后db是我们写在app.config配置文件中的数据库名称
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="DB" value="Sqlserver"/> </appSettings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration>
当你想要切换数据库的时候,我们只需要修改这里的value值就可以达到切换数据库的功能,不用修改任何代码,是不是感觉有些小兴奋呢
CreateDepartment方法中className是用来组合我们要创建的类的名称,然后通过反射去动态的创建
最后,我们看看如何调用它
class Program { static void Main(string[] args) { DepartMent departMent = new DepartMent(); IDepartment id = DataAccess.CreateDepartment(); id.Insert(departMent); id.GetDepart(1); } }
实例化一个DepartMent实体,然后我们通过DataAccess类中的CreateDepartment方法去创建IDepartment,实际上我们创建的是SqlserverDepartment类,创建完成之后就可以使用方法执行数据库操作了
到这里,一个实体我们就学完了,前面我们说了项目中我们不会是一个实体,有很多个实体,那怎么办呢(看代码)
interface IUser<T> { void Insert(T user); T GetUser(int id); }
我们可以抽象一个泛型接口IUser(这里名字不重要,只是我们前面写了User实体,所以借用一下),然后我们实现它
class SqlserverUser:IUser<User> { public void Insert(User user) { Console.WriteLine("在SQL server 中给User表增加一条记录"); } public User GetUser(int id) { Console.WriteLine("在SQL server中根据ID得到User表一条记录"); return null; } public string GetTypeName(User t) { throw new NotImplementedException(); } }
class AccessUser : IUser<User> { public User GetUser(int id) { Console.WriteLine("在Access中根据ID得到User表一条记录"); return null; } public void Insert(User user) { Console.WriteLine("在Access中给User表增加一条记录"); } }
同样是access和SQLserver各自实现,这里我们用User来做例子,你也可以用Depart或者其他来,这样是不是就解决了呢,然后依葫芦画瓢
public static T CreateUser<T>() { string Name = typeof(T).GenericTypeArguments[0].Name; string className = AssemblyName + "." + db + Name; return (T)Assembly.Load(AssemblyName).CreateInstance(className); }
我们在DataAccess类中添加一个方法CreateUser<T>泛型方法,这里Name是获取传入的参数类型的名称,然后同样是组合程序集名称通过反射创建
同样,我们来看看如何使用
User user = new User(); IUser<User> iu = DataAccess.CreateUser<IUser<User>>(); iu.Insert(user); iu.GetUser(1);
这里实例化一个User对象,然后同样的道理通过DataAccess类的CreateUser泛型方法去创建,这里我们实际创建的是SqlserverUser类,如果你想创建AccessUser和AccessDepartment类,可以修改我们之前在app.config配置文件中的SQLserver修改成Access,然后运行程序看看它实现的方法吧,这里就不再多说,留给你们去实现了。这里我们其实是通过名称来区分数据库的,其实只是为了说明反射的用法。好了,反射+抽象工厂就说到这了