在学习设计模式时,我接触到了简单工厂模式、工厂模式和抽象工厂模式,敲完三个模式的小例子,还是感觉抽象工厂模式比较好。代码与代码之间,类与类,接口与接口之间耦合显然降到了至今的最低。大大提高了复用性和后期软件的维护,方便了用户需求的更改。
抽象工厂设计模式中,最为典型的是反射“+”思想。下面举例子说明:
给一家企业做的电子商务网站,使用SQLServer作为数据库的,应该说上线后除了开始有些小问题,基本都还可以,而后,公司接到了另外一家公司类似的需求的项目,但是这个公司采用比较省钱的方式,租用了一个空间,只能用Access,不能用SQL Server。目标是将原来用SQL Server作为数据库的代码改为用Access作为数据库的代码。
这样的要求就要看原来的代码了,如果原代码之间耦合度够低,那么转换起来不至于那么费劲。且看用抽象工厂模式写的代码:
用户接口部分:
//作者:周丽同 //用户接口 interface IUser { void Insert(User user); User GetUser(int id); } //sqlserverUser类 class SqlserverUser : IUser { public void Insert(User user) { Console.WriteLine("在SQL中User表中增加了一条记录"); } public User GetUser(int id) { Console.WriteLine("在SQL Server中根据ID得到User表一条记录"); return null; } } //AccessUser类 class AccessUser : IUser { public void Insert(User user) { Console.WriteLine("在Access中给User表中添加了一条记录"); } public User GetUser(int id) { Console.WriteLine("在Access中根据ID得到User表一条记录"); return null; } } //系别接口 interface IDepartment { void Insert(Department department); Department GetDepartment(int id); } //sqlserverDepartment class SqlserverDepartment:IDepartment { public void Insert (Department department) { Console.WriteLine("在sql server中给department表添加了一条记录"); } public Department GetDepartment(int id) { Console.WriteLine("在sql server中根据ID得到了一条department表的记录"); return null; } } class AccessDepartment:IDepartment { public void Insert(Department department) { Console.WriteLine("在Access中给department表插入了一条记录"); } public Department GetDepartment(int id) { Console.WriteLine("在access中根据ID在表department中查询了一条记录"); return null; } }
//作者:周丽同 //用户表 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; } } } //专业系别表 class Department { private int _id; public int ID { get { return _id; } set { _id = value; } } private string _deptname; public string DeptName { get { return _deptname; } set { _deptname = value; } } }
//作者:周丽同 class DataAcess { private static readonly string db = "Sqlserver";//数据库名称,可以替换成Access; //private static readonly string db="Access"; public static IUser CreateUser() { IUser result = null; switch(db )//由于db的事先设置,所以此处可以根据选择实例化出相应的对象; { case "Sqlserver": result = new SqlserverUser(); break; case "Access": result =new AccessUser (); break ; } return result ; } public static IDepartment CreateDepartment() { IDepartment result = null; switch (db ) { case "Sqlserver": result = new SqlserverDepartment(); break; case "Access": result = new AccessDepartment(); break; } return result; } }客户端代码部分:
//作者:周丽同 class Program { static void Main(string[] args) { User user = new User(); Department dept = new Department(); IUser iu = DataAcess.CreateUser();//直接得到实际的数据库访问实例,而不存在任何依赖; iu.Insert(user); iu.GetUser(1); IDepartment id = DataAcess.CreateDepartment(); id.Insert(dept); id.GetDepartment(1); Console.Read(); } }
上面的代码,很好的提高了复用性和后期维护性,贯彻了开——闭原则。但是如果下一个公司做网站要用Oracle数据库访问,那该怎么办?按着上面的代码思路只能改DataAccess类,在每个方法的switch中加case了。
这里如果用到一个简单的.net技术就完美解决了。
格式是:
//Assembly.Load ("程序集名称".CreateInstance ("命名空间.类名称"))//只有在程序顶端协商using System.Reflection;来引用Reflection。有了反射,我们获得实例可以用下面两种写法:
//常规写法 // IUser result=new SqlserverUser(); //反射的写法 //using System.Reflection; // IUser result =(IUser)Assembly.Load("抽象工厂模式(当前程序的名称)").CreateInstance("抽象工厂模式(当前命名空间名称).SqlserverUser(要实例化的类名)");
修改代码部分:
//作者:周丽同 class DataAccess { private static readonly string AssemblyName = "抽象工厂模式"; private static readonly string db = "Sqlserver";//数据库名称,可替换成Access; 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); } }
比以前,代码是漂亮多了,但是在更换数据库访问时,还是需要去改程序(改db这个字符串的值)重编译,不完全符合真正的开放——封闭原则。反射+配置文件实现数据库访问程序可以解决更改DataAccess问题。
我们可以通过读文件来给DB字符串赋值,在配置文件中写明是Sql Server还是Access,这样DataAccess类也不用更改了。
添加一个App.config文件:
//作者:周丽同 <?xml version ="1.0" endcoding ="utf-8"?> <configuration> <appSetttings> <add key="DB" value="Sqlserver"/>//配置文件可以换成Access或者Oracle; </appSettings> <configuration>再添加引用 System.configuraiton,并在程序开头增加 using System.Configuration;,然后更改DataAccess类字段DB的赋值代码。
#region private static readonly string db=ConfigurationManager.AppSettings["DB"]; #endregion
这样看来,其实在一些用到“switch~case或者If”的语句,都可以考虑用反射技术来解除分支判断带来的耦合。三大工厂模式,自认为,抽象工厂最为重要,而且里面的反射思想用的很棒!
感谢您的宝贵时间~~~