说在前面
上篇文章介绍了简单工厂模式,对类的实例化进行解耦,但它毕竟是简单的工厂模式,使用范围很小,今天就介绍新的工厂模式-抽象工厂模式。
使用场景:
抽象工厂模式一般用于同一功能(或者说产品吧,想不到更好的说法),比如操作系统,数据库,Redis集群等这些产品都有不同的类型,当我们需要在不同产品间切换时,我们就要去兼容他们的接口,以数据库为例,当你的系统需要同时使用SQLServer,MySQL等数据库时,抽象工厂模式就可以解决,使得系统在数据库切换上解耦,可扩展等待好处,具体下面慢慢说…
抽象工厂模式咋理解?
对产品的类似接口进行抽象(不同类型接口需要做适配,这里就不多说了),在切换产品时,只要去获取需要的产品接口即可。
示例
以数据库中的SQLServer,MySQL兼容为例:
1. 基础实体类及使用类与数据库交互的方法接口:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public interface IUser
{
void Insert(User user);
User GetUser(int id);
}
2. 分别用SQLServer和MySQL两个数据来实现实体的Insert和Get接口:
//SQLServer处理数据的方式
public class SQLServerUser : IUser
{
public User GetUser(int id)
{
Console.WriteLine("从SQLServer中获取一条User数据,Id为{0}", id);
User user = new User
{
Id = id,
Name = ""
};
return user;
}
public void Insert(User user)
{
Console.WriteLine("在SQLServer中增加一条User数据,Id为{0},Name为{1}", user.Id, user.Name);
}
}
//MySQL处理数据的方式
public class MySQLUser : IUser
{
public User GetUser(int id)
{
Console.WriteLine("从MySQL中获取一条User数据,Id为{0}", id);
User user = new User
{
Id = id,
Name = ""
};
return user;
}
public void Insert(User user)
{
Console.WriteLine("在MySQL中增加一条User数据,Id为{0},Name为{1}", user.Id, user.Name);
}
}
3.实现工厂来统一管理这些类的获取,对类进行解耦
//工厂接口
public interface IFactory
{
IUser CreatUser();
}
//MySQL 工厂
public class MySQLFactory : IFactory
{
public IUser CreatUser()
{
return new MySQLUser();
}
}
//SQLServer 工厂
public class SQLServerFactory : IFactory
{
public IUser CreatUser()
{
return new SQLServerUser();
}
}
4.基础工作好了,下面看怎么用:
static void Main(string[] args)
{
string db = "SQLServer";
IFactory factory = null;
switch (db)
{
case "SQLServer":
factory = new SQLServerFactory();
break;
case "MySQL":
factory = new MySQLFactory();
break;
default:
throw new Exception("暂不兼容其他数据库!");
}
IUser user = factory.CreatUser();
user.Insert(new User
{
Id = 1,
Name = ""
});
user.GetUser(1);
}
5.这里我们只要去指定数据库,程序就可以按指定数据库执行了,这里的执行的操作数据库操作可以使用简单工厂再次解耦,添加一个管理工厂的工厂中心,如下:
public static IFactory GetDataDBFactory() {
//可以优化为配置文件,我这里就偷懒不做了
string db = "SQLServer";
IFactory factory = null;
switch (db)
{
case "SQLServer":
factory = new SQLServerFactory();
break;
case "MySQL":
factory = new MySQLFactory();
break;
default:
throw new Exception("暂不兼容其他数据库!");
}
return factory;
}
再次使用:
static void Main(string[] args)
{
IFactory factory = DataFactory.GetDataDBFactory();
IUser user = factory.CreatUser();
user.Insert(new User
{
Id = 1,
Name = ""
});
user.GetUser(1);
}
到这里,当我们需要切换数据库,只需要把简单工厂中数据库字段修改掉即可,进一步优化,可以将数据库名称的配置到配置文件中,我们只需要修改配置文件即可切换数据库,这样是不是很nice。
以上,我们已经把抽象工厂和简单工厂都用上了,程序的确实现了解耦,也遵循了单一职责,开闭原则,数据库切换改配置即可,代码需要改动迭代,只要改对应功能即可。但是呢,
如果我们再添加或修改一个实体类,需要同时在工厂中加上它的方法,代码量很大,且在简单工厂中心需要使用cese语句,这里不符合低耦合标准。怎么解决呢,往下看:
下面我们使用反射技术来解脱工厂的束缚:
public class DataAcess
{
//数据库:可配置在配置文件中
static string db = "SQLServer";
//程序集名称
static string AssemblyName = "SQLFactoryDemo";
//User类
public static IUser GetUser()
{
//类名称
string ClassName = "SQLFactoryDemo.Interface." + db + "User";
IUser user = (IUser)Assembly.Load(AssemblyName).CreateInstance(ClassName);
return user;
}
}
通过这个反射我们直接获得了类的实体对象,不再需要工厂处理,直观上看代码量少了几乎一半,效果同样达到了。