C#设计模式之:抽象工厂模式与反射

版权声明:每天要问下自己:“昨天的自己与今天的自己有啥区别?” https://blog.csdn.net/u010921682/article/details/82687676

复习工厂模式:

定义一个用于创建对象的接口,让子类决定实例化哪一个类

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

现在换个数据库

IFactory factory = new SqlServerFactory();

你也要更改多个地方

用简单工厂改进其缺陷

去除IFactorySqlServerFactoryAccessFactory三个工厂类
使用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";

每次更换数据库时还是要修改程序修改db值重编译,如果可以不修改程序,才是真正的开放-封闭原则

解决方案:使用配置文件

猜你喜欢

转载自blog.csdn.net/u010921682/article/details/82687676
今日推荐