软件设计 2017-01-24
今天呢,给大家来讲一下抽象工厂模式,说到这里,大家会想到好多种关于工厂的模式,前面已经讲了两种了 简单工厂模式和工厂方法模式。好,下面我们来看一下抽象工厂模式。
同样,我们来举一个案例
一、案例
我们在做项目的时候,肯定会与数据库打交道,那么我们用简单的控制台应用程序来模拟一个向SqlServer数据库表中插入和读取的情况。
/// <summary>
/// User中的字段
/// </summary>
class User
{
private int _id;
public int Id
{
get
{
return _id;
}
set
{
_id = value;
}
}
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
private string _name;
}
/// <summary>
/// SqlServer类
/// </summary>
class SqlserverUser
{
public void Insert(User user)
{
Console.WriteLine("在Sql server 中 给User 表增加一条记录");
}
public User GetUser(int id)
{
Console.WriteLine($"在Sql server 中根据ID获取User表中的一条记录");
return null;
}
} 客户端调用:
1 internal class Program
2 {
3 public static void Main()
4 {
5 User user = new User();
6 SqlserverUser su = new SqlserverUser();
7 su.Insert(user);
8 su.GetUser(1);
9 Console.ReadKey();
10 } 二、演绎
1、第一步演绎
那么问题来了,如果我这个项目数据库换成Oracle,那么我岂不是都要改了,如果想实现自由的切换数据库,我们想到了一个不错的模式,对,工厂方法模式。
让他们都依赖于抽象,所以我们在这里增加两个接口
interface IUser
{
void Insert(User user);
User GetUser(int id);
}
interface IFactory
{
IUser CraeteUser();
} 让User 和 SqlServer 等类继承相应的接口
/// <summary>
/// SqlServer类
/// </summary>
class SqlserverUser :IUser
{
public void Insert(User user)
{
Console.WriteLine("在Sql server 中 给User 表增加一条记录");
}
public User GetUser(int id)
{
Console.WriteLine($"在Sql server 中根据ID获取User表中的一条记录");
return null;
}
}
class OracleUser:IUser
{
public void Insert(User user)
{
Console.WriteLine("在Oracle 中 给User 表增加一条记录");
}
public User GetUser(int id)
{
Console.WriteLine($"在Oracle 中根据ID获取User表中的一条记录");
return null;
}
}class SqlServerFactory:IFactory
{
public IUser CraeteUser()
{
return new SqlserverUser();
}
}
class OracleFactory : IFactory
{
public IUser CraeteUser()
{
return new OracleUser();
}
} 客户端
1 public static void Main()
2 {
3 User user = new User();
4 IFactory factory = new SqlServerFactory();
5 IUser su = factory.CraeteUser();
6 su.Insert(user);
7 su.GetUser(1);
8 Console.ReadKey();
9 } 以上,我们将案例用工厂方法模式写出来了。
2、第二步演绎
我们的数据库中,不可能之后User表,还有很多其他的表,这样就会产生好多的类哦。
下面,我们在增加一个表(Department)
那么,按照上面的模式就会产生一下面的一坨代码
class Department
{
private int _id;
public int Id
{
get
{
return _id;
}
set
{
_id = value;
}
}
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
private string _name;
}
interface IDepartment
{
void Insert(Department department);
Department GetDepartment(int id);
}
class SqlServerDepartment:IDepartment
{
public void Insert(Department department)
{
Console.WriteLine("在SqlServer 中 给Department 表增加一条记录");
}
public Department GetDepartment(int id)
{
Console.WriteLine("在SqlServer 中 根据ID 获取Department 表一条记录");
return null;
}
}
class OracleDepartment : IDepartment
{
public void Insert(Department department)
{
Console.WriteLine("在Oracle 中 给Department 表增加一条记录");
}
public Department GetDepartment(int id)
{
Console.WriteLine("在Oracle 中 根据ID 获取Department 表一条记录");
return null;
}
} 工厂接口与实现中也新增了方法
interface IFactory
{
IUser CraeteUser();
IDepartment CreateDepartment();
}
class SqlServerFactory:IFactory
{
public IUser CraeteUser()
{
return new SqlserverUser();
}
public IDepartment CreateDepartment()
{
return new SqlServerDepartment();
}
}
class OracleFactory : IFactory
{
public IUser CraeteUser()
{
return new OracleUser();
}
public IDepartment CreateDepartment()
{
return new OracleDepartment();
}
} 经过我们这么一步一步的演化,我们重构出了一个非常重要的设计模式,抽象工厂模式。小伙伴们会说,刚刚这不是工厂方法模式吗。
只有一个User表的时候,是只需要工厂方法模式的,但是我们数据库中显然有非常多的表,还SqlServer 和Oracle 这两大分支,所以涉及到解决多种产品分支的问题,有一个专门的工厂模式,叫抽象工厂模式。
什么叫抽象工厂模式呢?提供一系列相关或相互依赖的接口,而无需指定他们具体的类。
这就是抽象工厂模式,他的优缺点很明显。
优点:灵活,产品系列零活切换。
缺点:如果我们再增加一个表,需要改动的地方太多了,需要增加三个类,需要修改三个类,靠,太麻烦了吧。
客户端每一次使用时,需要new 一下,如果客户端有好多处调用的,那么就需要new 好多次。 这些都是他的缺点。
编程是一门艺术,这样大批量的改动,显然是非常丑陋的做法。
我们这里有一个改进的方法,就是用简单工厂方法改进抽象工厂模式。
我们新增加一个类
class DataAccess
{
private static readonly string db = "SqlServer";
// private static readonly string db = "Oracle";
public static IUser CreateUser()
{
IUser result = null;
switch (db)
{
case "SqlServer":
result = new SqlserverUser();
break;
case "Oracle":
result = new OracleUser();
break;
}
return result;
}
public static IDepartment CreateDepartment()
{
IDepartment result = null;
switch (db)
{
case "SqlServer":
result = new SqlServerDepartment();
break;
case "Oracle":
result = new OracleDepartment();
break;
}
return result;
}
} 嗯,这样,客户端调用的时候就好调用了。
1 public static void Main()
2 {
3 User user = new User();
4 Department dept = new Department();
5 IUser iu = DataAccess.CreateUser();
6 iu.Insert(user);
7 iu.GetUser(1);
8 IDepartment d = DataAccess.CreateDepartment();
9 d.Insert(dept);
10 d.GetDepartment(1);
11 Console.ReadKey();
12 } ok,那么如果我还想增加一个数据库类型的分支,比如Access数据库,那么需要在DataAccess类的方法中增加一个case 分支。
之前我们也曾提到过相关的问题,就是用 反射 来解决,但一直没有展开来讲,那么下篇博文,我们来给大家讲一讲如何用反射的技术来解决这些问题。
反射反射,程序员的快乐~
好了,今天我们先讲到这里,下一篇我们将用反射技术来解决相关的问题~
本系列将持续更新,喜欢的小伙伴可以点一下关注和推荐,谢谢大家的支持。