设计模式——抽象工厂模式的实现[备忘录]
业务场景:实现系统数据库的切换,运用抽象工厂模式(Abstract Factory)。
首先呢,我们利用工厂方法模式来实现,温习一下工厂方法模式。
业务场景条件:数据表:user(id,name);
department(id,name);
业务场景UML图:
思路:
1、定义实体类User、Department。
2、定义接口IUser、IDepartment,并声明void Insert(User user)、GetUser(int id);void Insert(Department department)、GetDepartment(int id)方法;
定义接口IUser、IDepartment用于客户端的访问,解除与具体数据库访问的耦合。
3、分别编写AccessUser(继承IUser,并实现该接口),SQLserverUser(继承IUser,并实现该接口);AccessDepartment(继承IDepartment,并实现该接口),SQLserverDepartment(继承IDepartment接口,并实现该接口)。
4、定义工厂接口IFactory(createUser、createDepartment)。
5、分别定义具体工厂AccessFactory、SQLserverFactory分别继承IFactory接口并实现该接口。
实现代码:
1、
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { public 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; } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { public class Department { private int _id; public int Id { get { return _id; } set { _id = value; } } private string _name; public string Name { get { return _name; } set { _name = value; } } } }
2、
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal interface IUser { void Insert(User user); User GetUser(int id); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal interface IDepartment { void Insert(Department deparment); Department GetDepartment(int id); } }
3、
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal class AccessUser:IUser { #region IUser 成员 public void Insert(User user) { Console.WriteLine("Access正在为您添加一个用户……"); Console.WriteLine("Access已经成功添加一个用户。"); } public User GetUser(int id) { Console.WriteLine("Access正在为您查找id为" + id + "的用户……"); Console.WriteLine("Access返回" + id + "用户"); return null; } #endregion } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal class AccessDepartment:IDepartment { #region IDepartment 成员 public void Insert(Department deparment) { Console.WriteLine("Access正在为您添加一个部门……"); Console.WriteLine("Access已经成功添加一个部门。"); } public Department GetDepartment(int id) { Console.WriteLine("Access正在为您查找id为" + id + "的部门……"); Console.WriteLine("Access返回" + id + "部门"); return null; } #endregion } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal class SqlserverUser:IUser { #region IUser 成员 public void Insert(User user) { Console.WriteLine("SQLserver正在为您添加一个用户……"); Console.WriteLine("SQLserver已经成功添加一个用户。"); } public User GetUser(int id) { Console.WriteLine("SQLserver正在为您查找id为"+id+"的用户……"); Console.WriteLine("SQLserver返回"+id+"用户"); return null; } #endregion } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal class SQLserverDepartment:IDepartment { #region IDepartment 成员 public void Insert(Department deparment) { Console.WriteLine("SQLserver正在为您添加一个部门……"); Console.WriteLine("SQLserver已经成功添加一个部门。"); } public Department GetDepartment(int id) { Console.WriteLine("SQLserver正在为您查找id为" + id + "的部门……"); Console.WriteLine("SQLserver返回" + id + "部门"); return null; } #endregion } }
4、
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal interface IFactory { IUser CreateUser(); IDepartment CreateDepartment(); } }
5、
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal class AccessFactory:IFactory { #region IFactory 成员 public IUser CreateUser() { return new AccessUser(); } public IDepartment CreateDepartment() { return new AccessDepartment(); } #endregion } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { internal class SQLserverFactory:IFactory { #region IFactory 成员 public IUser CreateUser() { return new SqlserverUser(); } public IDepartment CreateDepartment() { return new SQLserverDepartment(); } #endregion } }
客户端调用代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { class Program { static void Main(string[] args) { User user = new User(); Department department = new Department(); //IFactory factory = new SQLserverFactory();//根据实例化具体的数据库,赋给factory对象。 IFactory factory = new AccessFactory(); IUser iuser = factory.CreateUser(); iuser.Insert(user); iuser.GetUser(1); IDepartment idepartment = factory.CreateDepartment(); idepartment.Insert(department); idepartment.GetDepartment(1); Console.ReadLine(); } } }
上述代码属于抽象工厂模式。但是觉得很是带着许多工厂方法模式的影子。
抽象工厂的定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
这样设计能实现Access与SQLserver灵活的切换,但是如果新增Oracle话,我们需要创建OracleFactory、OracleUser、OracleDepartment等等。
更糟糕的情况是:我们有新添一张Project表,我们不得不添加IProject、SQLserverProject、AccessProject,还需要改动IFactory、SQLserverFactory、AccessFactory。
这样我们为了解决这个问题,我们先利用反射+抽象工厂来实现数据访问程序。
反射可以有效的解决switch case分支。
反射听起来不好理解,用起来却很神奇。
基本格式:Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")。需引用System.Reflection命名空间。
接下来我们用反射来改造一下程序:
UML图:
这样,我们就DataAccess取代了IFactory、SqlserverFactory、AccessFactory三个工厂。
DataAccess类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; //引入反射命名空间 namespace AbstractFactory { public class DataAccess { private static readonly string AssemblyName = "AbstractFactory"; private static readonly string database = "Sqlserver"; public static IUser CreateUser() { string classname = AssemblyName + "." + database + "User"; return (IUser)Assembly.Load(AssemblyName).CreateInstance(classname); //Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")。需引用System.Reflection命名空间。 } public static IDepartment CreateDepartment() { string classname = AssemblyName + "." + database + "Department"; return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(classname); } } }
如果我们要实现从Sqlserver切换到Access,只需要将
private static readonly string database = "Sqlserver";
改写成:private static readonly string database = "Access"; 即可。
客户端调用:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AbstractFactory { class Program { static void Main(string[] args) { //User user = new User(); //Department department = new Department(); ////IFactory factory = new SQLserverFactory();//根据实例化具体的数据库,赋给factory对象。 //IFactory factory = new AccessFactory(); //IUser iuser = factory.CreateUser(); //iuser.Insert(user); //iuser.GetUser(1); //IDepartment idepartment = factory.CreateDepartment(); //idepartment.Insert(department); //idepartment.GetDepartment(1); //对比一下抽象工厂模式 //反射 User user = new User(); Department department = new Department(); IUser iuser = DataAccess.CreateUser(); iuser.Insert(user); iuser.GetUser(2); IDepartment idepartment = DataAccess.CreateDepartment(); idepartment.Insert(department); idepartment.GetDepartment(2); Console.ReadLine(); } } }
在DataAccess类中我们需要改写代码,才能完成数据库的切换。如果我们使用配置文件动态的读取,以实现数据库的切换,这样我们的程序才算完美。
接下来我们继续改造我们的程序。
1、添加“应用程序配置文件”---App.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="DB" value="Access"/> </appSettings> </configuration>
2、修改DataAccess类。引入System.Configuration,实现动态的读取App.config文件。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; //引入反射命名空间 using System.Configuration; //引入配置文件命名空间 namespace AbstractFactory { public class DataAccess { private static readonly string AssemblyName = "AbstractFactory"; //private static readonly string database = "Sqlserver"; private static readonly string database=ConfigurationSettings.AppSettings["DB"]; public static IUser CreateUser() { string classname = AssemblyName + "." + database + "User"; return (IUser)Assembly.Load(AssemblyName).CreateInstance(classname); //Assembly.Load("程序集名称").CreateInstance("命名空间.类名称")。需引用System.Reflection命名空间。 } public static IDepartment CreateDepartment() { string classname = AssemblyName + "." + database + "Department"; return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(classname); } } }
这样我们通过改写App.config来实现数据库的切换,不用在修改程序本身。
抽象工厂总结:
- 抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 优点:1、易于交换产品系列。
2、使得具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口来操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
- 缺点:就是我们上面所说的新增project表的情况:“我们有新添一张Project表,我们不得不添加IProject、SQLserverProject、AccessProject”,比较难以支持新的产品种类。
-
应用情景:
- 同一个产品族的产品在一起使用时,而且它们之间是相互依赖的,不可分离
- 系统需要由相互关联的多个对象来构成
- 你想提供一组对象而不显示它们的实现过程,只显示它们的接口
- 系统不应当依赖某一些具体产品类。
应用场景举例:
- 游戏开发中的多风格系列场景
- 系统更改皮肤
- 支持多种观感标准的用户界面工具箱(Kit)。
本文版权归本人和博客园共同所用,转载请注明出处。