zoukankan      html  css  js  c++  java
  • 大话设计模式-抽象工厂模式

    抽象工厂模式

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

    最大的好处是易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这使得改变一个应用的具体工厂变得非常容易,他只需要改变具体工厂即可使用不同的产品配置。

    他让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。


    访问数据库

    数据类:User/Department

    class User{
        public int ID { get; set; }
        public string Name { get; set; }
    }
    class Department{
        public int ID { get; set; }
        public string Name { get; set; }
    }

    数据接口:IUser/IDepartment

    用于客户端访问,解除与具体数据库访问的耦合。

    interface IUser{
        void Insert(User user);
        void GetUser(int id);
    }
    interface IDepartment{
        void Insert(Department department);
        void GetDepartment(int id);
    }

    接口实现类:MySQL/SQLServer User/Department

    分别实现MySQL和SQLServer两种数据库的对用户表和部门表的数据操作。每种表的操作封装在一个类中。

    class SQLServerUser : IUser
    {
        public void Insert(User user) => Console.WriteLine("在SQLServer中给User表插入一条记录");
        public void GetUser(int id) => Console.WriteLine("在SQLServer中根据ID查询User表得到一条记录");
    }
    class MySQLUser : IUser
    {
        public void Insert(User user) => Console.WriteLine("在MySQL中给User表插入一条记录");
        public void GetUser(int id) => Console.WriteLine("在MySQL中根据ID查询查询User表得到一条记录");
    }
    class SQLServerDepartment : IDepartment
    {
        public void Insert(Department department) => Console.WriteLine("在SQLServer中给Department表插入一条记录");
        public void GetDepartment(int id) => Console.WriteLine("在SQLServer中根据ID查询Department表得到一条记录");
    }
    class MySQLDepartment : IDepartment
    {
        public void Insert(Department department) => Console.WriteLine("在MySQL中给Department表插入一条记录");
        public void GetDepartment(int id) => Console.WriteLine("在MySQL中根据ID查询Department表得到一条记录");
    }

    工厂接口:IFactory

    声明了创建不同数据库都要实现的方法

    interface IFactory
    {
        IUser CreateUser();
        IDepartment CreateDepartment();
    }

    具体工厂类:MySQL/SQLServer Factory

    为不同数据库创建对应的对表的操作封装的类的实例。

    class SQLServerFactory : IFactory{
        public IUser CreateUser() => new SQLServerUser();
        public IDepartment CreateDepartment() => new SQLServerDepartment();
    }
    class MySQLFactory : IFactory{
        public IUser CreateUser() => new MySQLUser();
        public IDepartment CreateDepartment() => new MySQLDepartment();
    }

    测试类:Program

    //声明工厂,选择SQLServer工厂
    IFactory factory = new SQLServerFactory();
    //声明用户数据类,创建SQLServer的封装了创建对用户表操作的类的实例
    User user = new User();
    IUser iuser = factory.CreateUser();
    //执行类中封装的对用户表操作的方法
    iuser.Insert(user);
    iuser.GetUser(1);
    //部门数据类同上
    Department department = new Department();
    IDepartment idepartment = factory.CreateDepartment();
    idepartment.Insert(department);
    idepartment.GetDepartment(1);
    //测试结果
    在SQLServer中给User表插入一条记录
    在SQLServer中根据ID查询User表得到一条记录
    在SQLServer中给Department表插入一条记录
    在SQLServer中根据ID查询Department表得到一条记录

    使用简单工厂来改进抽象工厂

    去除IFactory、SQLServerFactory和MySQLFactory三个工厂类。取而代之的是DataAccess类,用一个简单工厂模式来实现。

    数据访问类:DateAccess

    数据库名称可以替换成需要的,由于数据库名称的实现设置,所以在静态方法中可以根据选择实例化出相应的对象。

    class DateAccess{
        private static readonly string db = "SQLServer";
        public static IUser CreateUser(){
            IUser result = null;
            switch (db){
                case "SQLServer":
                    result = new SQLServerUser();
                    break;
                case "MySQL":
                    result=new MySQLUser();
                    break;
            }
            return result;
        }
        public static IDepartment CreateDepartment(){
            IDepartment result = null;
            switch (db){
                case "SQLServer":
                    result = new SQLServerDepartment();
                    break;
                case "MySQL":
                    result = new MySQLDepartment();
                    break;
            }
            return result;
        }
    }

    测试类:Program

    直接使用DataAccess得到实际的数据库访问实例,不需要任何依赖。

    User user = new User();
    IUser iuser = DateAccess.CreateUser();
    iuser.Insert(user);
    iuser.GetUser(1);
    
    Department department = new Department();
    IDepartment idepartment = DateAccess.CreateDepartment();
    idepartment.Insert(department);
    idepartment.GetDepartment(1);

    用反射加抽象工厂的数据访问程序

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

    数据访问类:DataAccess

    如果增加Orac数据访问,相关类的增加是不可避免的,但是根据开放-封闭原则,应该开放扩展,关闭修改。现在只需修改private static readonly string db = "SQLServer";就可以创建不同的实例了。

    using System.Reflection;
    class DateAccess{
        private static readonly string AssemblyName = "FactoryTest";
        private static readonly string db = "SQLServer";
        public static IUser CreateUser(){
            string className = AssemblyName + "." + db + "User";
            return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
        }
        public static IDepartment CreateDepartment(){
            string className = AssemblyName + "." + db + "Department";
            return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);
        }
    }

    用反射加配置文件实现数据访问程序

    利用配置文件来解决更改DataAccess的问题。在配置文件中写明是哪个数据库。读取文件来给db字符串赋值。

    添加一个App.config文件

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
        </startup>
        <appSettings>
          <add key="DB" value="SQLServer"/>
        </appSettings>
    </configuration>

    数据访问类:DataAccess

    在程序开头增加using System.Configuration;然后更改DataAccess类的字段DB的赋值代码。

    class DateAccess{
        private static readonly string AssemblyName = "FactoryTest";
        private static readonly string db = ConfigurationSettings.AppSettings["DB"];
        public static IUser CreateUser(){
            string className = AssemblyName + "." + db + "User";
            return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
        }
        public static IDepartment CreateDepartment(){
            string className = AssemblyName + "." + db + "Department";
            return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);
        }
    }

    所有用在简单工厂的地方都可以考虑用反射技术来去除switc或if,接触分支判断带来的耦合。

  • 相关阅读:
    VMware虚拟机安装
    代码搜索的终极武器Ag
    模糊搜索神器fzf
    Python:json、xml、字典各种转换
    03-azkaban安装部署
    linux下环境变量PATH设置错误的补救
    01-编译azkaban
    VMware安装CentOS7
    PS(二)
    等待公交车的时间
  • 原文地址:https://www.cnblogs.com/errornull/p/10060193.html
Copyright © 2011-2022 走看看