前言
在学习设计模式时,我接触到了简单工厂模式、工厂模式和抽象工厂模式,敲完三个模式的小例子,还是感觉抽象工厂模式比较好。代码与代码之间,类与类,接口与接口之间耦合显然降到了至今的最低。大大提高了复用性和后期软件的维护,方便了用户需求的更改。
内容
抽象工厂设计模式中,最为典型的是反射“+”思想。下面举例子说明:
背景
给一家企业做的电子商务网站,使用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”的语句,都可以考虑用反射技术来解除分支判断带来的耦合。三大工厂模式,自认为,抽象工厂最为重要,而且里面的反射思想用的很棒!
感谢您的宝贵时间~~~