大话设计模式-抽象工厂模式

抽象工厂模式

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

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

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

 


 

访问数据库

数据类: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,接触分支判断带来的耦合。

posted @ 2018-12-05 10:30  田错  阅读(212)  评论(0编辑  收藏  举报