zoukankan      html  css  js  c++  java
  • C#设计模式之:抽象工厂模式与反射

    抽象工厂模式【实例】:
    定义一个用于创建对象的接口,让子类决定实例化哪一个类

    UML

    代码
    class User
    {
        private int _id;
        public int Id { get => _id; set => _id = value; }

        private string _name;
        public string Name { get => _name; set => _name = value; }
    }

    interface IUser
    {
       void Insert(User user);
       User GetUser(int id);
    }

    class SqlserverUser : IUser
    {
        public void Insert(User user)
        {
            Console.WriteLine("在SQL Server中给User表增加一条记录");
        }

       public User GetUser(int id)
       {
          Console.WriteLine("在SQL Server中根据ID得到User表一条记录");
          return null;
       }
    }

    class SqlServerFactory : IFactory
    {
        public IUser CreateUser()
        {
            return new SqlserverUser();
        }
    }


    class AccessUser : IUser
    {
        public User GetUser(int id)
       {
           Console.WriteLine("在Access中根据ID得到User表一条记录");
           return null;
       }

      public void Insert(User user)
       {
           Console.WriteLine("在Access中给User表增加一条记录");
       }
    }

    class AccessFactory : IFactory
    {
        public IUser CreateUser()
       {
            return new AccessUser();
       }
    }

    // test
    User user = new User();
    //IFactory factory = new SqlServerFactory();
    IFactory factory = new AccessFactory();
    IUser iu = factory.CreateUser();
    iu.Insert(user);
    iu.GetUser(1);

    // result
    在Access中给User表增加一条记录
    在Access中根据ID得到User表一条记录

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

    UML


    代码
    class Department
    {
          private string name;
          private string id;

          public string Name { get => name; set => name = value; }
          public string Id { get => id; set => id = value; }
    }

    interface IDepartment
    {
         void Insert(Department department);
         Department GetDepartment(int id);
    }

    class SqlserverDepartment : IDepartment
    {
        public void Insert(Department department)
        {
             Console.WriteLine("在SQL Server中给Department表增加一条记录");
        }

       public Department GetDepartment(int id)
       {
           Console.WriteLine("在SQL Server中根据ID得到Department表一条记录");
           return null;
       }
    }

    class AccessDepartment : IDepartment
    {
        public Department GetDepartment(int id)
       {
            Console.WriteLine("在Access中根据ID得到Department表一条记录");
            return null;
       }

       public void Insert(Department department)
       {
            Console.WriteLine("在Access中给Department表增加一条记录");
       }
    }

    class SqlServerFactory : IFactory
    {
        public IUser CreateUser()
         {
            return new SqlserverUser();
         }

       public IDepartment CreateDepartment()
       {
          return new SqlserverDepartment();
       }
    }
    class AccessFactory : IFactory
    {
       public IUser CreateUser()
       {
           return new AccessUser();
       }

       public IDepartment CreateDepartment()
       {
            return new AccessDepartment();
       }
    }


    // test
    User user = new User();
    Department dept = new Department();

    //IFactory factory = new SqlServerFactory();
    IFactory factory = new AccessFactory();
    IUser iu = factory.CreateUser();
    iu.Insert(user);
    iu.GetUser(1);

    IDepartment id = factory.CreateDepartment();
    id.Insert(dept);
    id.GetDepartment(1);


    // result
    在Access中给User表增加一条记录
    在Access中根据ID得到User表一条记录
    在Access中给Department表增加一条记录
    在Access中根据ID得到Department表一条记录

    IFactory 就是一个抽象工厂接口,它里面应该包含所有的产品创建的抽象方法
    通常在运行时再创建一个ConcreteFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说为创建不同的产品对象,客户商应使用不同的具体工厂

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

    缺陷
    1,添加新的表,要更改多个工厂类
    2,程序中有多个地方使用了

    IFactory factory = new AccessFactory();
    1
    现在换个数据库

    IFactory factory = new SqlServerFactory();
    1
    你也要更改多个地方

    总结:

    简单工厂模式:管理对象类 调用接口

    抽象工厂模式:接口实现类 调用接口

    用简单工厂改进其缺陷
    去除IFactory、SqlServerFactory和AccessFactory三个工厂类
    使用DataAccess类取代它们

    UML


    代码
    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 SqlserverUser();
             break;
             case "Access":
             result = new AccessUser();
             break;
         }
          return result;
    }

    public static IDepartment CreateDepartment()
    {
          IDepartment result = null;
          switch (db)
         {
            case "SqlServer":
            result = new SqlserverDepartment();
            break;
            case "Access":
            result = new AccessDepartment();
            break;
         }
          return result;
       }
    }
    // test
    User user = new User();
    IUser iu = DataAccess.CreateUser();
    iu.Insert(user);
    iu.GetUser(1);

    Department dept = new Department();
    IDepartment id = DataAccess.CreateDepartment();
    id.Insert(dept);
    id.GetDepartment(1);

    // result
    在SQL Server中给User表增加一条记录
    在SQL Server中根据ID得到User表一条记录
    在SQL Server中给Department表增加一条记录
    在SQL Server中根据ID得到Department表一条记录

    这里使用了简单工厂的方法来解决了上面的缺陷,但同时又产生了新的问题(switch问题)
    如果要新增Oracle数据库,抽象工厂本来只要增加一个OracleFactory工厂即可,这里简单工厂要修改switch增加case

    .NET中的依赖注入( Dependency Injection)
    using System.Reflection;

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


    // 常规写法
    IUser result = new SqlServerUser();

    // 反射写法
    using System.Reflection;

    IUser result = (IUser)Assembly.Load("抽象工厂模式").CreateInstance("抽象工厂模式.SqlServerUser");

    使用反射的具体代码

    using System.Reflection;

    class DataAccess
    {
         private static readonly string AssemblyName = "PatternTest";

         private static readonly string db = "AbstractPattern.Sqlserver";
         // private static readonly string db = "AbstractPattern.Access";

       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);
      }
    }

    上述代码同样得到了结果

    如果我们增加了Oracle数据访问,相关的类的增加是不可避免的,不过这叫扩展,开闭原则告诉我们对于扩展开放,对于修改关闭
    我们只要修改db字符串变量的值为

    // private static readonly string db = "AbstractPattern.Oracle";
    1
    每次更换数据库时还是要修改程序修改db值重编译,如果可以不修改程序,才是真正的开放-封闭原则

    解决方案:使用配置文件

  • 相关阅读:
    你的人生许多痛苦源于盲目较劲
    这些HTML、CSS知识点,面试和平时开发都需要 (转)
    拿什么拯救你,我的代码--c#编码规范实战篇 (转)
    最近的面试总结
    感恩和珍惜现在的生活
    我眼中的领域驱动设计(转)
    《生活就像练习》读书笔记(四)——意识状态和类型
    《生活就像练习》读书笔记(三)——发展路线
    .NET面试题解析(07)-多线程编程与线程同步 (转)
    C#进阶系列——WebApi身份认证解决方案:Basic基础认证 (转)
  • 原文地址:https://www.cnblogs.com/lvjy-net/p/10430762.html
Copyright © 2011-2022 走看看