抽象工厂模式
抽象工厂模式提供一个创建一系列相关或互相依赖的接口,而无需再指定他们具体的类。
最大的好处是易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这使得改变一个应用的具体工厂变得非常容易,他只需要改变具体工厂即可使用不同的产品配置。
他让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。
访问数据库
数据类:User/Department
class User{ public int ID { get; set; } public string Name { get; set; } } class Department{ public int ID { get; set; } public string Name { get; set; } }
数据接口:IUser/IDepartment
用于客户端访问,解除与具体数据库访问的耦合。
interface IUser{ void Insert(User user); void GetUser(int id); } interface IDepartment{ void Insert(Department department); void GetDepartment(int id); }
接口实现类:MySQL/SQLServer User/Department
分别实现MySQL和SQLServer两种数据库的对用户表和部门表的数据操作。每种表的操作封装在一个类中。
class SQLServerUser : IUser { public void Insert(User user) => Console.WriteLine("在SQLServer中给User表插入一条记录"); public void GetUser(int id) => Console.WriteLine("在SQLServer中根据ID查询User表得到一条记录"); } class MySQLUser : IUser { public void Insert(User user) => Console.WriteLine("在MySQL中给User表插入一条记录"); public void GetUser(int id) => Console.WriteLine("在MySQL中根据ID查询查询User表得到一条记录"); } class SQLServerDepartment : IDepartment { public void Insert(Department department) => Console.WriteLine("在SQLServer中给Department表插入一条记录"); public void GetDepartment(int id) => Console.WriteLine("在SQLServer中根据ID查询Department表得到一条记录"); } class MySQLDepartment : IDepartment { public void Insert(Department department) => Console.WriteLine("在MySQL中给Department表插入一条记录"); public void GetDepartment(int id) => Console.WriteLine("在MySQL中根据ID查询Department表得到一条记录"); }
工厂接口:IFactory
声明了创建不同数据库都要实现的方法
interface IFactory { IUser CreateUser(); IDepartment CreateDepartment(); }
具体工厂类:MySQL/SQLServer Factory
为不同数据库创建对应的对表的操作封装的类的实例。
class SQLServerFactory : IFactory{ public IUser CreateUser() => new SQLServerUser(); public IDepartment CreateDepartment() => new SQLServerDepartment(); } class MySQLFactory : IFactory{ public IUser CreateUser() => new MySQLUser(); public IDepartment CreateDepartment() => new MySQLDepartment(); }
测试类:Program
//声明工厂,选择SQLServer工厂 IFactory factory = new SQLServerFactory(); //声明用户数据类,创建SQLServer的封装了创建对用户表操作的类的实例 User user = new User(); IUser iuser = factory.CreateUser(); //执行类中封装的对用户表操作的方法 iuser.Insert(user); iuser.GetUser(1); //部门数据类同上 Department department = new Department(); IDepartment idepartment = factory.CreateDepartment(); idepartment.Insert(department); idepartment.GetDepartment(1); //测试结果 在SQLServer中给User表插入一条记录 在SQLServer中根据ID查询User表得到一条记录 在SQLServer中给Department表插入一条记录 在SQLServer中根据ID查询Department表得到一条记录
使用简单工厂来改进抽象工厂
去除IFactory、SQLServerFactory和MySQLFactory三个工厂类。取而代之的是DataAccess类,用一个简单工厂模式来实现。
数据访问类:DateAccess
数据库名称可以替换成需要的,由于数据库名称的实现设置,所以在静态方法中可以根据选择实例化出相应的对象。
class DateAccess{ private static readonly string db = "SQLServer"; public static IUser CreateUser(){ IUser result = null; switch (db){ case "SQLServer": result = new SQLServerUser(); break; case "MySQL": result=new MySQLUser(); break; } return result; } public static IDepartment CreateDepartment(){ IDepartment result = null; switch (db){ case "SQLServer": result = new SQLServerDepartment(); break; case "MySQL": result = new MySQLDepartment(); break; } return result; } }
测试类:Program
直接使用DataAccess得到实际的数据库访问实例,不需要任何依赖。
User user = new User(); IUser iuser = DateAccess.CreateUser(); iuser.Insert(user); iuser.GetUser(1); Department department = new Department(); IDepartment idepartment = DateAccess.CreateDepartment(); idepartment.Insert(department); idepartment.GetDepartment(1);
用反射加抽象工厂的数据访问程序
Assembly.Load("程序集名称").CreateInstance("命名空间.类名");
数据访问类:DataAccess
如果增加Orac数据访问,相关类的增加是不可避免的,但是根据开放-封闭原则,应该开放扩展,关闭修改。现在只需修改private static readonly string db = "SQLServer";就可以创建不同的实例了。
using System.Reflection; class DateAccess{ private static readonly string AssemblyName = "FactoryTest"; private static readonly string db = "SQLServer"; public static IUser CreateUser(){ string className = AssemblyName + "." + db + "User"; return (IUser)Assembly.Load(AssemblyName).CreateInstance(className); } public static IDepartment CreateDepartment(){ string className = AssemblyName + "." + db + "Department"; return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className); } }
用反射加配置文件实现数据访问程序
利用配置文件来解决更改DataAccess的问题。在配置文件中写明是哪个数据库。读取文件来给db字符串赋值。
添加一个App.config文件
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <appSettings> <add key="DB" value="SQLServer"/> </appSettings> </configuration>
数据访问类:DataAccess
在程序开头增加using System.Configuration;然后更改DataAccess类的字段DB的赋值代码。
class DateAccess{ private static readonly string AssemblyName = "FactoryTest"; private static readonly string db = ConfigurationSettings.AppSettings["DB"]; public static IUser CreateUser(){ string className = AssemblyName + "." + db + "User"; return (IUser)Assembly.Load(AssemblyName).CreateInstance(className); } public static IDepartment CreateDepartment(){ string className = AssemblyName + "." + db + "Department"; return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className); } }
所有用在简单工厂的地方都可以考虑用反射技术来去除switc或if,接触分支判断带来的耦合。