zoukankan      html  css  js  c++  java
  • 工厂模式之抽象工厂

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

    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,改动较大。因此,在使用抽象工厂模式时,产品的结构等级结构划分非常重要。

  • 相关阅读:
    Spring Security OAuth2 源码分析
    Spring Security OAuth2 token权限隔离
    Spring Cloud Feign 使用OAuth2
    Spring Security OAuth2 授权码模式
    全链路追踪spring-cloud-sleuth-zipkin
    Spring Security OAuth2 授权失败(401) 问题整理
    使用ShardingJdbc分表
    Kubectl常用命令
    Spring Cloud Zuul实现IP访问控制
    一次非核心接口(信息提示类)被刷引发的思考
  • 原文地址:https://www.cnblogs.com/mo-lu/p/11117573.html
Copyright © 2011-2022 走看看