抽象工厂模式

来源:互联网 发布:电脑机箱背板孔位数据 编辑:程序博客网 时间:2024/04/30 01:12

书中引例:项目用的数据库为 SQL Server 。后又需求换成 Access 。数据库都有读写查等共同的操作,


using UnityEngine;using System.Collections;public class AbstractFactoryStudy : MonoBehaviour {// Use this for initializationvoid Start () {    User user = new User();//        IFactory07 factory07 = new SqlSercerFactory(); 若要换数据库 这两句换一下就行了        IFactory07 factory07 = new AccessFactory();    IUser iu = factory07.CreatUser();        iu.Insert(user);    iu.GetUser(1);}}//IUser 接口 ,用于客户端访问,解除与具体数据库访问的耦合。public interface IUser{    void Insert(User user);    User GetUser(int id);}//SqlsercerUser 用于访问SqlServer的Userpublic class SqlsercerUser : IUser{    public void Insert(User user)    {        Debug.Log("在SQL中添加记录");    }    public User GetUser(int id)    {        Debug.Log("在SQL中获得记录");        return null;    }}//AccesssUser 用于访问Access的Userpublic class AccesssUser : IUser {    public void Insert(User user) {        Debug.Log("在AccesssUser中添加记录");    }    public User GetUser(int id) {        Debug.Log("在AccesssUser中获得记录");        return null;    }}//IFactory07 接口, 定义一个创建访问User表对象的抽象的工厂接口。 public interface IFactory07{    IUser CreatUser();}//SqlSercerFactory 类,实现 IFactory07 接口,实例化 SqlsercerUserpublic class SqlSercerFactory :IFactory07{    public IUser CreatUser()    {        return  new SqlsercerUser();    }}//AccessFactory 类,实现 IFactory07 接口,实例化 AccessUserpublic class AccessFactory : IFactory07 {    public IUser CreatUser() {        return new AccesssUser();    }}//User 表结构public class User{    public int ID { get; set; }    public string name { get; set; }}

以上想想要换数据库只需要改一句就行了,由于多肽的关系,使得声明的 IUSer 接口的对象 iu 事先根本不知道是在访问那个数据库,却可以在运行时很好的完成工作,实现了业务逻辑与数据访问的解耦。


需要加一个数据库表 Department:

using UnityEngine;using System.Collections;public class AbstractFactoryStudy : MonoBehaviour {// Use this for initializationvoid Start () {    User user = new User();//        IFactory07 factory07 = new SqlSercerFactory(); 若要换数据库 这两句换一下就行了        IFactory07 factory07 = new AccessFactory();            IUser iu = factory07.CreatUser();        iu.Insert(user);    iu.GetUser(1);        Department dept = new Department();    IDepartment id = factory07.CreatDepartment();        id.Insert(dept);        id.GetDepartment(1);}}public interface IDepartment{    void Insert(Department department);    Department GetDepartment(int id );}public interface IUser{    void Insert(User user);    User GetUser(int id);}public class SqlsercerDepartment : IDepartment {    public void Insert(Department department)    {        Debug.Log("在SQL中添加记录");    }    public Department GetDepartment(int id)    {        Debug.Log("在SQL中获得记录");        return null;    }}public class AccesssDepartment : IDepartment {    public void Insert(Department department) {        Debug.Log("在SQL中添加记录");    }    public Department GetDepartment(int id) {        Debug.Log("在SQL中获得记录");        return null;    }}public class SqlsercerUser : IUser{    public void Insert(User user)    {        Debug.Log("在SQL中添加记录");    }    public User GetUser(int id)    {        Debug.Log("在SQL中获得记录");        return null;    }}public class AccesssUser : IUser {    public void Insert(User user) {        Debug.Log("在AccesssUser中添加记录");    }    public User GetUser(int id) {        Debug.Log("在AccesssUser中获得记录");        return null;    }}public interface IFactory07{    IUser CreatUser();    IDepartment CreatDepartment();// 增加接口的方法}public class SqlSercerFactory :IFactory07{    public IUser CreatUser()    {        return  new SqlsercerUser();    }    public IDepartment CreatDepartment()//增加了 SqlsercerDepartment 工厂    {        return new SqlsercerDepartment();    }}public class AccessFactory : IFactory07 {    public IUser CreatUser() {        return new AccesssUser();    }    public IDepartment CreatDepartment()//增加了 AccesssDepartment 工厂    {        return new AccesssDepartment();    }}public class User{    public int ID { get; set; }    public string name { get; set; }}//Department 表结构public class Department {    public int ID { get; set; }    public string name { get; set; }}


抽象工厂模式:提供一个创建一系列相关或互相依赖对象的接口, 而无需指定他们具体的类。



AbstractProductA 和 AbstractProductB 是两个抽象产品,之所以为抽象,是因为它们都有可能有两种不同的实现,就刚才的例子来说就是User 和 Department ,而 ProductA1 ProductA2 和 ProductB1  ProductB2 就是对两个抽象产品的具体分类的实现,比如ProductA1可以理解为是 SqlServerUser ,而 ProductB1 是 AccessUser。

IFactory 是一个抽象工厂接口, 它里面应该包含所有的产品创建的抽象方法。而 ConcreteFactory1 和 ConcreteFactory2 就是具体的工厂了,就像 SqlserverFactory 和 AccessFactory 一样。 通常是子啊运行时刻在创建一个ConcreteFactory 类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说,为创建不同的产品对象,客户端应使用不同的具体工厂。

优点:易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。 它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。

缺点:如果现在又需要加入表 Project 的话,那么至少要增加三个类:IProject  SqlserverProject  AccessProject   还需要更改 IFactory  SqlserverFactory  和  AccessFactory 才可以完全实现。

客户端类并不只有一个,也就是调用IUser 或 IDepartment ,的地方都需要声明 IFactory factory = new AccessFactory(),要换数据库的话 每个地方都要改,所以 可以用简单工厂来改进抽象工厂, 去除 IFactory  SqlserverFactory  和 AccessFactory 三个工厂类。 取而代之的是 DataAccess 类, 用一个简单工厂模式来实现。


using UnityEngine;public class AbstractFactoryStudy : MonoBehaviour {// Use this for initializationvoid Start () {    User user = new User();        Department dept = new Department();    //直接得到实际的数据库访问实例,而不存在任何依赖。        IUser iu = DataAccess.CreateUser();        iu.Insert(user);    iu.GetUser(1);    //直接得到实际数据库访问实例,而不存在任何依赖        IDepartment id = DataAccess.CreateDepartment();        id.Insert(dept);    id.GetDepartment(1);}}public class DataAccess{    private static readonly string db = "Sqlserver";//    private static readonly string db = "Access"; 数据库名称可替换    public static IUser CreateUser()    {        IUser result = null;        switch (db)        {            case "Sqlserver":                result = new SqlsercerUser();                break;            case "Access":                result = new AccesssUser();                break;        }        return result;    }    public static IDepartment CreateDepartment()    {        IDepartment result = null;        switch (db)        {            case "Sqlserver":                result = new SqlsercerDepartment();                break;            case "Access":                result = new AccesssDepartment();                break;        }        return result;    }}public interface IDepartment{    void Insert(Department department);    Department GetDepartment(int id );}public interface IUser{    void Insert(User user);    User GetUser(int id);}public class SqlsercerDepartment : IDepartment {    public void Insert(Department department)    {        Debug.Log("在SQL中添加记录");    }    public Department GetDepartment(int id)    {        Debug.Log("在SQL中获得记录");        return null;    }}public class AccesssDepartment : IDepartment {    public void Insert(Department department) {        Debug.Log("在SQL中添加记录");    }    public Department GetDepartment(int id) {        Debug.Log("在SQL中获得记录");        return null;    }}public class SqlsercerUser : IUser{    public void Insert(User user)    {        Debug.Log("在SQL中添加记录");    }    public User GetUser(int id)    {        Debug.Log("在SQL中获得记录");        return null;    }}public class AccesssUser : IUser {    public void Insert(User user) {        Debug.Log("在AccesssUser中添加记录");    }    public User GetUser(int id) {        Debug.Log("在AccesssUser中获得记录");        return null;    }}public class User {    public int ID { get; set; }    public string name { get; set; }}//Department 表结构public class Department {    public int ID { get; set; }    public string name { get; set; }}

上述代码抛弃了 IFactory  SqlserverFactory  和 AccessFactory  三个工厂类,取而代之的是 DataAccess类,由于事先设置了db的值(Sqlserver 或 Access),所以简单工厂的方法都不需要传入参数,客户端没有出现任何一个 SQLServer 或 Access 的字样,达到了解耦的目的。

如果再需要加入Oracle数据库的访问的话 需要在DataAccess 类中每个方法的switch 中加case 也比较繁琐,可以利用反射来改进:

Assembly.Load("程序集名称").CreatInstance("命名空间.类名称")

反射引用的命名空间: using System.Reflection;


备注: ReadOnly与Const区别

using UnityEngine;using System.Reflection;public class AbstractFactoryStudy : MonoBehaviour {void Start () {    User user = new User();        Department dept = new Department();        IUser iu = DataAccess.CreateUser();        iu.Insert(user);    iu.GetUser(1);        IDepartment id = DataAccess.CreateDepartment();        id.Insert(dept);    id.GetDepartment(1);}}public class DataAccess{    private static readonly string AssemblyName = "DaHuaSheJi";    private static readonly string db = "Sqlserver";//可换成 Access。 变化时 之变化这一句就可以了    public static IUser CreateUser()    {        string className = AssemblyName + "." + db + "User";        return (IUser) Assembly.Load(AssemblyName).CreateInstance(className);/* Unity 中这样写  Type t = Type.GetType("SqlsercerUser");        return (IUser) Activator.CreateInstance(t);*/    }
    public static IDepartment CreateDepartment()    {        IDepartment result = null;        switch (db)        {            case "Sqlserver":                result = new SqlsercerDepartment();                break;            case "Access":                result = new AccesssDepartment();                break;        }        return result;    }}public interface IDepartment{    void Insert(Department department);    Department GetDepartment(int id );}public interface IUser{    void Insert(User user);    User GetUser(int id);}public class SqlsercerDepartment : IDepartment {    public void Insert(Department department)    {        Debug.Log("在SQL中添加记录");    }    public Department GetDepartment(int id)    {        Debug.Log("在SQL中获得记录");        return null;    }}public class AccesssDepartment : IDepartment {    public void Insert(Department department) {        Debug.Log("在SQL中添加记录");    }    public Department GetDepartment(int id) {        Debug.Log("在SQL中获得记录");        return null;    }}public class SqlsercerUser : IUser{    public void Insert(User user)    {        Debug.Log("在SQL中添加记录");    }    public User GetUser(int id)    {        Debug.Log("在SQL中获得记录");        return null;    }}public class AccesssUser : IUser {    public void Insert(User user) {        Debug.Log("在AccesssUser中添加记录");    }    public User GetUser(int id) {        Debug.Log("在AccesssUser中获得记录");        return null;    }}public class User {    public int ID { get; set; }    public string name { get; set; }}//Department 表结构public class Department {    public int ID { get; set; }    public string name { get; set; }}

以上 ,如果在需要增加 Project 产品时,只需要增加三个与 Project 相关的类,在修改 DataAccess ,在其中增加一个 public static IProject CreatProject() 方法就可以了。


在追求完美一下, 就是将要读的数据库名称字符串放到配置文件中,添加一个 App.config文件,内容下:

<?xml version="1.0" encoding="utf-8" ?><configuration>  <addSettings>      <add key="DB" value="Sqlserver"/>  </addSettings></configuration>


在程序中去读取这个value值就可以了。

所有的简单工厂都可以考虑用反射技术来去除 switch 或 if 解除分支判断带来的耦合。



原创粉丝点击