竹影横扫窗

导航

 

主要参考《大话设计模式》

1. 引入

  前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场养动物、电视机厂生产电视等,然而,现实生活中,许多工厂是综合型工厂,能够生产各类产品,如大学包括各个系。

2. 定义

  抽象工厂模式,为创建一组相关或相互依赖的对象提供一个接口,且无需指定它们的具体类。是所有形态的工厂模式中最为抽象和最具一般性的一种形态。

3. 场景实现

3.1 场景描述 

  在框架开发中,对于同一个ORM,假如刚开始开发时使用SQL Server数据库,但是也希望能够连接Access数据库,或其他的数据库(如MySql、Oracle等)。SQL Server在.net中使用的System.Data.SqlClient命名空间下的SqlConnection、SqlCommand、SqlParameter、SqlDataReader、SqlDataAdapter,而Access要用System.Data.Olede命名空间下的相应对象,我们不可能修改命名空间,或者重新写相同的业务代码只是修改与数据库相关的代码,这就是两倍的工作量。因而,针对类似问题,可以采用抽象工厂模式解决。

  比如,现在要在业务逻辑代码相似的情况下,将SqlServer数据库改为使用Access数据库。 以“新增用户”和“获取用户”为例给出最基本的数据库访问程序。

3.2 最基本的数据访问程序

  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; }
        }
    }
  class SqlServer
    {
        public void insert(User user)
        {
            Console.WriteLine("在sqlserver中为User表添加一条记录");
        }
        public void select(int id)
        {
            Console.WriteLine("在sqlserver中根据用户id查找用户");
        }
    }
  static void Main(string[] args)
        {
            User user = new User();
            SqlServer sql = new SqlServer();
            sql.insert(user);
            sql.select(1);
            Console.Read();
        }

运行结果如下:

  上述实现中,无法灵活的把sqlserver数据库替换成其他数据库,原因就是SqlServer sql = new SqlServer()使得sql对象被框死在Sqlserver上,如果此处是灵活的(多态的)应用,在执行sql.insert(user)时,不需要关注数据库到底是sqlserver还是access,因此,我们可以采用工厂方法模式进行封装。工厂方法模式是定义一个用于创建对象的接口,让子类决定实例化哪个类。

3.3 使用工厂方法模式的数据访问程序

3.3.1 UML图

 

3.3.2 代码实现

class User
    {
        private int _id;
        public int ID
        {
            get { return _id;}
            set { this._id = value; }
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { this._name = value; }
        }
    }
interface IUser
    {
        void Insert(User user);
        User GeUser(int id);
    }
 class SqlserverUser:IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Sqlserver中新增一个用户");
        }
       
        public User GeUser(int id)
        {
            Console.WriteLine("在sqlserver中获取一个用户");
            return null;
        }
    }
class AccessUser:IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在Access中新增一个用户");
        }
       
        public User GeUser(int id)
        {
            Console.WriteLine("在Access中获取一个用户");
            return null;
        }
    }
  interface IFactory
    {
        IUser CreateUser();
    } 
class SqlserverFactory:IFactory
    {
        public IUser CreateUser()
        {
            return new SqlserverUser();
        }
    }
class AccessFactory:IFactory
    {
        public IUser CreateUser()
        {
            return new AccessUser();
        }
    }
static void Main(string[] args)
        {
            User user=new User();
            IFactory factory=new SqlserverFactory();
            IUser sqlUser = factory.CreateUser();
            sqlUser.Insert(user);
            sqlUser.GeUser(1);
            Console.WriteLine("Hello World!");
        }

  上述为使用工厂方法模式实现不同数据库中Insert、GetUser方法的实现,该实现中抽象接口与接口相关联,而不是与具体的类耦合,对代码进行了解耦,极大增加了代码的灵活性。在main方法中只需要根据自己的需求生成数据库实例即可,加入需要使用access数据库,只需main中修改为以下语句即可:

IFactory factory=new AccessFactory();

3.4 使用抽象工厂实现

  上述用工厂方法实现不同数据库新增或获取User表数据,虽然实现了具体类与类之间的耦合,但一个数据库中会存在很多表,比如Department表,如果需要获取Department数据,则需要新增一个Department、ServerDepartment、AccessDepartment 3个类并更改IFactory、SqlServerFactory、AccssFactory

3.4.1 UML图

  其中,IDepartment接口,用于客户端访问,解除具体数据库访问的耦合。

3.4.2 代码实现  

//IDepartment接口,用于客户端访问,解除具体数据库访问的耦合。
interface
IDepartment { void Insert(Department depement); User GeUser(int id); } class Department { private int _id; public int ID { get { return _id;} set { this._id = value; } } private string _name; public string Name { get { return _name; } set { this._name = value; } } }
//SqlserverDepartment类,用于访问sqlserver的Department
class SqlserverDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Sqlserver中新增一个部门"); } public Department GetDepartment(int id) { Console.WriteLine("在sqlserver中获取一个部门"); return null; } }
//AccessDepartment类,用于访问access数据库的Department
class AccessDepartment : IDepartment { public void Insert(Department department) { Console.WriteLine("在Access中新增一个部门"); } public Department GetDepartment(int id) { Console.WriteLine("在Access中获取一个部门"); return null; } }
 interface IFactory
    {
        IUser CreateUser();
        IDepartment CreateDepartment();
    } 
// AccessFactory,实现IFactory接口,实例化AccessUser和AccessDepartment
class AccessFactory:IFactory
    {
        public IUser CreateUser()
        {
            return new AccessUser();
        }

        public IDepartment CreateDepartment()
        {
            return new AccessDepartment();
        }
    }
// SqlserverFactory类,实现IFactory接口,实例化SqlserverUser和SqlserverDepartment
class SqlserverFactory:IFactory
    {
        public IUser CreateUser()
        {
            return new SqlserverUser();
        }

        public IDepartment CreateDepartment()
        {
            return  new SqlserverDepartment();
        }
    }
static void Main(string[] args)
        {
            User user=new User();
            Department dep=new Department();
            //IFactory factory=new SqlserverFactory();
            IFactory factory=new AccessFactory();
            IUser sqlUser = factory.CreateUser();
            sqlUser.Insert(user);
            sqlUser.GeUser(1);

            IDepartment id = factory.CreateDepartment();
            id.Insert(dep);
            id.GetDepartment(1);
            Console.WriteLine("Hello World!");
        }

  客户端只需确定实例化哪个数据库访问对象给factory。

  只有一个User类和User操作类的时候,只需要工厂方法模式,但由于数据库中会有很多表,sqlserver和access又是两个不同的分类,因此,该问题中涉及多个产品系列的问题,对其实现,采用了抽象工厂模式。

4. 抽象工厂模式UML图

 

  上图中,AbstractProductA和AbstractProductB是两个抽象产品,对应于第3.4节场景分析中的User类和Department类,它们又拥有自己不同的实现,因此,ProductA1、ProductA1、ProductB1、ProductB2就是对两个抽象产品具体分类的实现,分别代表SqlserverUser、AccessUser、SqlserverDepartment、AccessDepartment。.IFactory作为一个抽象工厂接口,应该包含实现创建产品的方法(如:CreateSqlServer,CreateAccess).而CreateFactory1和CreateFactory2则代表具体的工厂(如:SqlServerFactory,AccessFactory)。通常,会在客户端创建具体工厂类的实例,这个工厂再创建具有特定实现的产品对象,即为创建不同的产品对象,客户端应使用不同的具体工厂。

5. 其他应用场景

  例如一个应用,需要在三个不同平台上运行:Windows、Linux、Android等,三个不同操作系统上的软件功能、应用逻辑、UI都应该是非常类似,唯一不同的是调用不同的工厂方法,由不同的产品类去处理与操作系统交互的信息。

 6. 总结

   抽象工厂模式的优点及缺点:

  优点:

  • (1)易于交换产品系列(如:上例中,使用SqlServer数据库还是Access数据库只需改变IFactury factory = new AccessFactory();/即可),只需改变具体工厂即可使用不同的产品配置
  • (2)具体的创建实例过程与客户端分离。客户端是通过抽象接口操作实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

   缺点:

  产品的扩展困难,如上例产品中需要添加一张表Project,此时需要增加IProject 、SqlServerProject、AccessProject,并更改IFactory、SqlServerFactory、AccssFactory,改动较大。因此,在使用抽象工厂模式时,产品的结构等级结构划分非常重要。

posted on 2019-07-13 20:48  竹影横扫窗  阅读(640)  评论(0编辑  收藏  举报