最近一直在总结设计模式,但总是总结了又改,改了再改,有一种雾里看花,抓不到重点的感觉。我感觉自己陷入了怪圈,不能跳出来宏观看问题!所以,最近几天一直不能发表文章,真真是山穷水尽!不管了,先从它们三个开始,希望柳暗花明。。
例子
在设计模式中有这样一个问题,一个软件公司要给两家企业做分别做一个薪资管理系统,这两家企业的需求都是很相似的。不同的是,一家用SQL Server作为数据库,另一家用Access作为数据库。如果你是开发员,怎样才能更高效地完成任务?!
不用着急,怎么一步步探索!
一、工厂方法——解决更换数据库问题
从问题中,我们可以分析出,它的关键点是在换数据库。现在以“新增用户”和“得到用户”为例,首先画一个工厂方法代码结构图,从宏观上进行分析:
按照图,实现代码
class program
{
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; }
}
//IUser接口,用于客户端访问,解除与具体数据库访问的耦合
interface IUser
{
void Insert(User user);
User GetUser(int id);
}
//SqlUser类,用于访问SQL的User
class SqlUser : IUser
{
public void Insert(User user)
{
Console.WriteLine("在SQL中给User表增加一条记录");
}
public User GetUser(int id)
{
Console.WriteLine("在SQL中根据ID得到User表的一条记录");
return null;
}
}
//AccessUser类,用于访问Access的User
class AccessUser : IUser
{
public void Insert(User user)
{
Console.WriteLine("在Access中给User表增加一条记录");
}
public User GetUser(int id)
{
Console.WriteLine("在Access中根据ID得到User表的一条记录");
return null;
}
}
//IFactory接口,定义一个创建访问User表对象的抽象的工厂接口
interface IFactory
{
IUser CreateUser();
}
//SqlFactory类,实现IFactory接口,实例化SqlUser
class SqlFactory : IFactory
{
public IUser CreateUser()
{
return new SqlUser();
}
}
//Access类,实现IFactory接口,实例化AccessUser
class AccessFactory : IFactory
{
public IUser CreateUser()
{
return new AccessUser();
}
}
//客户端
static void Main(string[] args)
{
User user = new User();
IFactory factory = new SqlFactory(); //若要更改成Access数据库,只需将SqlFactory()换成AccessFactory()
IUser iu = factory.CreateUser();
iu.Insert(user);
iu.GetUser(1);
Console.Read();
}
}
}
通过工厂方法,现在如果要换数据库,只需要把IFactory factory = new SqlFactory()改成IFactory factory =new AccessFactory()。此时,由于多态的关系,使得声明IUser接口的对象iu事先根本不知道是在访问哪个数据库,却可以在运行时很好地完成工作,这就是所谓的业务逻辑与数据访问的解耦。
二、抽象工厂——解决创建多个对象问题
由于是开发薪资管理系统,在数据库中不可能只有一个User表,可能会有很多表,比如增加一个部门表(Dpt),那我们应该怎么办?这时候就用到抽象工厂了
代码结构图
看到此图,再与之前的工厂方法结构图作对比,我们可以发现,它们之间没有结构上的变化,只是增加了一个部门表接口以及它的两个实现类。由此,我们可以得出一个结论:抽象工厂是工厂方法的拓展。当涉及到多个产品系列的问题时,我们就可以使用抽象工厂。
三、简单工厂——解耦
如果要使用抽象工厂,那么相应的就会增加三个类:IDpt、SqlDpt、AccessDpt,还需要更新IFactory、SqlFactory、AccessFactory才可以完全实现。俗话说,编程是一门艺术!这样大批量的改动,显然是非常丑陋的做法。我们可以用简单工厂来对抽象工厂进行改进。
代码结构图
咱们先不写代码,单是从图上来看,大道至简的气息跃然纸上!话不多说,看代码:
class DataAccess
//class DataSql
{
private static readonly string db = "Sql";
public static IUser CreateUser()
{
IUser result = null;
switch (db)
{
case "Sql":
result = new SqlUser();
break;
case "Access":
result = new AccessUser();
break;
}
return result;
}
public static IDpt CreateDpt()
{
IDpt result = null;
switch (db)
{
case "Sql":
result = new SqlDpt();
break;
case "Access":
result = new AccessDpt();
break;
}
return result;
}
}
从图和代码中我们可以看出,去除了IFactory、SqlFactory、AccessFactory三个工厂类,取而代之的DataAccess类。由于事先设置了db的值(Sql或Access),所以,简单工厂的方法都不需要输入参数,这样在客户端就只需要DataAccess.CreateUser()和DataAccess.CreateDpt()来生成具体的数据库访问类实例,而客户端没有出现任何一个SQL Server或Access的字样,达到了解耦的目的。。
四、反射——去除switch
通过上面的修改,我们的代码无论是在复杂程度上,还是在灵活性上,都得到了很大的改善。但是,,,,,在传统的23中设计模式中,并没有简单工厂模式!Why?????它使我们的代码更加清晰整洁,达到了解耦的目的,难道它还有不足吗?答案是肯定的。。
设计模式的六大原则中,最为核心的一项原则是开放-封闭原则。它的定义我不多说,它的重要性我也不多做解释。简单工厂违背了这项原则,因为它使用了switch
解决的办法就是反射!话不多说,先看代码:
using System.Reflection; //用来引用Reflection,帮助我们解决简单工厂的不足
class DataAccess
//class DataSql
{
private static readonly string AssemblyName = "抽象工厂模式"; //程序集
private static readonly string db = "Sql"; //数据库名称,可替换为Access
public static IUser CreateUser()
{
string className=AssemblyName + "."+db+"User";
return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
}
public static IDpt CreateUser()
{
string className=AssemblyName + "."+db+"Dpt";
return (IDpt)Assembly.Load(AssemblyName).CreateInstance(className);
}
}
通过反射,我们将程序由编写时转为运行时。由于CreateInstane(className)中的字符串是可以写成变量的,而变量的值到底是SQL Sever还是Access,完全可以由事先的那个db变量来决定。所以,就去除了switch判断的麻烦。
五、配置文件——不用更改DataAccess类(Perfect)
我们可以读文件来给db字符串赋值,在配置文件中写明是SQL Server还是Access,这样就连DataAccess类也不用更改了。
添加App.config文件
添加引用System.configuration
在开头增加using System.Configuration
using System.Configuration;
class DataAccess
//class DataSql
{
private static readonly string db = ConfigurationManager.AppSettings["DB"]; //表示配置文件
}
这样一来,咱们的软件就真正的完美了。
小结
整个过程下来,我们使用工厂方法达到了代码的可复用性,提高了效率;使用抽象工厂达到了软件的可拓展性;使用简单工厂改进抽象工厂,达到了解耦的目的;使用反射技术,去除了简单工厂带来的switch或if,更进一步解除了分之判断带来的耦合,是软件更易维护和拓展;最后,使用配置文件达到最完美的效果。
好了,总结完三工厂,果然又有了思路。。