重庆新闻频道:设计模式——抽象工厂模式

admin 2个月前 (08-26) 科技 64 1

目录

  • 设计模式——抽象工厂模式
    • 1. 模式简介
    • 2. 示例1-使用工厂模式实现对差别数据库的操作
    • 3. 示例2-多数据库且多表操作
    • 4. 重构示例2-使用简朴工厂改善抽象工厂
    • 5. 重构示例2-反射+简朴工厂
    • 6. 重构示例2-反射+设置文件+简朴工厂
shanzm-2020年5月1日 23:20:41

1. 模式简介

抽象工厂模式(Abstract Factory Pattern):为确立一组相关或相互依赖的工具提供一个接口,而且无须指定它们的详细类。

产物族(产物系列)统一个详细工厂确立的差别品级的产物称为统一产物族,或是称为统一产物系列。

注重:统一个产物族的产物是继续于差别的产物抽象类

在抽象工厂模式中有产物族的观点,而在工厂方式模式中是没有这个观点的,由于工厂方式模式中一个详细工厂只确立一种详细产物。

产物品级又称为产物系列,指的是继续与统一个抽象产物类的所有详细产物称之为统一个产物品级

为了利便明了产物族和产物品级,举一个小栗子:

抽象工厂模式主要类:

  • AbstractProductA抽象产物A类(或是接口),派生出所有的详细产物类ConcreteProductA1、ConcreteProductA2 ……

  • AbstractProductB抽象产物B类(或是接口),派生出所有的详细产物类ConcreteProductB1、ConcreteProductB2 ……

  • AbstractFactory 抽象工厂接口,所有的详细工厂类都是实现该接口

  • ConcreteFactory1 详细工厂1,实现了IFactory接口,确立详细的产物工具ConcreteProductA1和ConcreteProductB1

  • ConcreteFactory2 详细工厂2,实现了IFactory接口,确立详细的产物工具ConcreteProductA2和ConcreteProductB2

注重: 两个抽象产物类可以有关系,好比:配合继续或实现一个抽象类或接口

抽象工厂模式的UML:

注:原图片来自《设计模式实训教程-第二版》
仔细查看UML,可以发现:当系统中只存在一个产物品级时,抽象工厂模式将退化到工厂方式模式。

2. 示例1-使用工厂模式实现对差别数据库的操作

2.1 靠山说明

在现实开发中,有可能会泛起替换差别的数据库,或是一个项目就使用多个类型的数据库。
以是为便于替换差别的数据库,我们使用工厂模式,界说差别的详细工厂确立差别数据库的操作类

示例泉源《鬼话设计模式》,假设某个项目同时详细MSSQL数据库和Oracle数据库,两个数据库只是类型差别,其中的表以及表的字段都是一样的。

我们需要对两个数据库中的User表举行操作。

根据工厂模式的设计思绪,依次实现以下接口和类:

抽象产物:IUserService-声明查询和添加User表数据的方式
详细产物:MSSQLUserServiceOracleUserService-划分针对MSSQ和Oracle数据库的实现IUserService接口
抽象工厂:IDatabaseFactory-声明确立IUserService工具的方式
详细工厂:MSSQLFactoryOracleFactory-实现IDatabaseFactory接口,划分确立MSSQLUserService工具和OracleUserService工具

2.2 代码实现

①确立User类

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

②确立产物总接口IUserService和详细产物MSSQLUserService、OracleUserService

//抽象产物
public interface IUserService
{
    void Insert(User user);
    User GetUser(int id);
}

//详细产物:模拟对MSSQL数据库中的User表的查询和添加操作
public class MSSQLUserService : IUserService
{
    public void Insert(User user)
    {
        Console.WriteLine($"MSSQL数据库User表中中-添加新的用户,Id:{user.Id },Name{user.Name}");
    }
    public User GetUser(int id)
    {
        Console.WriteLine($"MSSQL数据库User表中-查询到用户,Id:{id}");
        return null;
    }
}

//详细产物:模拟对Oracle数据库中的User表的查询和添加操作
public class OracleUserService : IUserService
{
    public void Insert(User user)
    {
        Console.WriteLine($"Oracle数据库User表中-添加新的用户,Id:{user.Id },Name{user.Name}");
    }
    public User GetUser(int id)
    {
        Console.WriteLine($"Oracle数据库User表中-查询到用户,Id:{id}");
        return null;
    }
}

③确立抽象工厂IDatabaseFactory和详细工厂MSSQLFactory、OracelFactory

//抽象工厂
public interface IDatabaseFactory
{
    IUserService CreateUserService();
}

//详细工厂:确立MSSQLUserService工具
public class MSSQLFactory : IDatabaseFactory
{
    public IUserService CreateUserService()
    {
        return new MSSQLUserService();
    }
}

//详细工厂:确立OracleUserService工具
public class OracleFactory : IDatabaseFactory
{
    public IUserService CreateUserService()
    {
        return new OracleUserService();
    }
}

④客户端挪用

static void Main(string[] args)
{
    User user = new User() { Id = 0001, Name = "shanzm" };

    IDatabaseFactory msSQlFactory = new MSSQLFactory();
    IDatabaseFactory oracleFactory = new OracleFactory();

    //若是对MSSQL数据库中的User表操作
    IUserService msUserService = msSQlFactory.CreateUserService();
    msUserService.Insert(user);
    msUserService.GetUser(00001);//print:查询到用户,Id:00001

    //若是对Oracle数据库中User表操作
    IUserService oracleUserService = oracleFactory.CreateUserService();
    oracleUserService.Insert(user);
    oracleUserService.GetUser(00001);//print:查询到用户,Id:00001
}

2.3 程序类图


3. 示例2-多数据库且多表操作

3.1 靠山说明

在示例1中,有两个差别的数据库,每个数据库中都有一张User表,我们实现了对每个数据库的User表查询和添加数据

我们使用了工厂方式模式,即有一个抽象产物接口(IUserService),有2个详细产物类(MSSQLUserServiceOracleUserService)实现该接口。

有一个抽象工厂接口(IDatabaseFactory),有两个详细产物工厂类(MSSQLFactoryOracleFactory)实现该接口。

而现在,若是在两个数据库中另有一个部门表Department表,需要对Department表操作。

则需要根据以下修改和添加代码:

  • 添加一个抽象产物接口(IDepService),和实现该接口的有两个详细产物类(MSSQLDepServiceOracleDepService)。

  • 在原有的抽象工厂接口和详细工厂类中添加确立MSSQLDepService工具和OracleDepService工具的方式。注重工厂方式是在原有的工厂中举行扩展。

3.2 代码实现

①在示例1的基础上,添加一个Department类

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
}

②添加一个新的抽象产物接口(IDepService),和实现该接口的有两个详细产物(MSSQLDepServiceOracleDepService

//抽象产物
public interface IDepartmentService
{
    void Insert(Department dep);
    Department GetDepartment(int id);
}

//详细产物:模拟对MSSQL数据库中的Department表的查询和添加操作
public class MSSQLDepService : IDepartmentService
{
    public Department GetDepartment(int id)
    {
        Console.WriteLine($"MSSQL数据库的Department表中-查询到部门,Id:{id}");
        return null;
    }
    public void Insert(Department dep)
    {
        Console.WriteLine($"MSSQL数据库的Department表中-添加新的部门,Id:{dep.Id }Name:{dep.Name}");
    }
}

//详细产物:模拟对Oracle数据库中的Department表的查询和添加操作
class OracleDepService : IDepartmentService
{
    public Department GetDepartment(int id)
    {
        Console.WriteLine($"Oracle数据库的Department表中-查询到部门,Id:{id}");
        return null;
    }
    public void Insert(Department dep)
    {
        Console.WriteLine($"Oracle数据库的Department表中-添加新的部门,Id:{dep.Id }Name:{dep.Name}");
    }
}

③在示例1的基础上,在原有的抽象工厂接口和详细工厂类中添加确立MSSQLDepService工具和OracleDepService工具的方式

public interface IDatabaseFactory
{
    IUserService CreateUserService();
    IDepartmentService CreateDepService();//在接口中添加新的方式
}

public class MSSQLFactory : IDatabaseFactory
{
    public IDepartmentService CreateDepService()
    {
        return new MSSQLDepService();
    }
    public IUserService CreateUserService()
    {
        return new MSSQLUserService();
    }
}

public class OracleFactory : IDatabaseFactory
{
    public IDepartmentService CreateDepService()
    {
        return new OracleDepService();
    }
    public IUserService CreateUserService()
    {
        return new OracleUserService();
    }
}

④在客户端挪用

static void Main(string[] args)
{
    User user = new User() { Id = 0001, Name = "shanzm" };
    Department dep = new Department() { Id = 1000, Name = "Development" };
   
    IDatabaseFactory DatabaseFactory = new MSSQLFactory();

    //对MSSQL数据库中的User表操作
    IUserService UserService = DatabaseFactory.CreateUserService();
    UserService.Insert(user);
    UserService.GetUser(00001);

    //对MSSQL数据库中的Del表操作
    IDepartmentService DepService = DatabaseFactory.CreateDepService();
    DepService.Insert(dep);
    DepService.GetDepartment(1000);
    Console.ReadKey();
}

运行效果:

重庆新闻频道:设计模式——抽象工厂模式 第1张


若是需要改换为Oracle数据库,则你只需要将确立详细工厂工具new MSSQLFactory()修改为new OracleFactory(),其他的代码无需修改
static void Main(string[] args)
{
    User user = new User() { Id = 0001, Name = "shanzm" };
    Department dep = new Department() { Id = 1000, Name = "Development" };
   
    IDatabaseFactory DatabaseFactory = new MSSQLFactory();

    //对MSSQL数据库中的User表操作
    IUserService UserService = DatabaseFactory.CreateUserService();
    UserService.Insert(user);
    UserService.GetUser(00001);

    //对MSSQL数据库中的Del表操作
    IDepartmentService DepService = DatabaseFactory.CreateDepService();
    DepService.Insert(dep);
    DepService.GetDepartment(1000);
    Console.ReadKey();
}

运行效果:

重庆新闻频道:设计模式——抽象工厂模式 第2张

3.3 程序类图

重庆新闻频道:设计模式——抽象工厂模式 第3张

【说明】:

  • MSSQLUserServiceMSSQLDepService是由统一个详细工厂MSSQLFactory确立的,即二者属于统一产物族。

  • OracleUserServiceOracleDepService是由统一个详细工厂OracleFactory确立的,即二者属于统一产物族。

  • 而我们需要切换数据库的时刻(即切换产物族),只需要修改确立详细工厂工具:MSSQLFactory工具或OracleFactory工具。这就是抽象工厂模式的最大优点!


4. 重构示例2-使用简朴工厂改善抽象工厂

上述示例项目中,若是再添加一个新的表Student,添加对该表的操作类,则先需要界说一个抽象接口IStudentService接口,派生针对差别数据库操作的两个类:MSSQLStudentServiceOracleStudentService,这之后再在IDatabaseFactory接口中添加一个CreateStudentService()方式,接着在两个详细的工厂类中实现该接口。

我们可以使用简朴工厂模式实现上述的示例2中的项目:

完整演示Demo代码下载

①以下接口和类和示例2中一样
抽象产物A:IUserService ,派生出详细产物:MSSQLUserServiceOracleUserService
抽象产物B:IDepService,派生出详细产物:MSSQLDepServiceOracleDepService

②界说简朴工厂类:
由于这里有两个抽象产物,以是和之前的一样平常的简朴工厂差别的地方就是要确立两个工厂方式:

public class DatabaseFactory
{
    private static readonly string db = "MSSQL";//若是需要替换数据库则将字符串改"Oracle"

    //针对抽象产物IUserService的工厂方式
    public static IUserService CreateUserService()
    {
        IUserService userService = null;
        switch (db)
        {
            case "MSSQL":
                userService = new MSSQLUserService();
                break;
            case "Oracle":
                userService = new OracleUserService();
                break;
        }
        return userService;
    }

    //针对抽象产物IDepService的工厂方式
    public static IDepartmentService CreateDeprService()
    {
        IDepartmentService depService = null;
        switch (db)
        {
            case "MSSQL":
                depService = new MSSQLDepService();
                break;
            case "Oracle":
                depService = new OracleDepService();
                break;
        }
        return depService;
    }
}

若是需要替换数据库,则只需要简朴的将 private static readonly string db该字段赋值改为"Oracle"

③客户端挪用

static void Main(string[] args)
{
    User user = new User() { Id = 0001, Name = "shanzm" };
    Department dep = new Department() { Id = 1000, Name = "Development" };

    IUserService userService = DatabaseFactory.CreateUserService();
    userService.Insert(user);

    IDepartmentService depService = DatabaseFactory.CreateDeprService();
    depService.Insert(dep);

    Console.ReadKey();
}

运行效果:

重庆新闻频道:设计模式——抽象工厂模式 第4张

【说明】

  • 在这里使用简朴该厂模式对比使用抽象工厂模式,简化了许多的类和接口,所有的修改都可以在工厂类中举行修改添加

  • 同样也实现了客户端和确立实例历程的星散

④程序类图

重庆新闻频道:设计模式——抽象工厂模式 第5张

对比抽象工厂模式,只是将所有的抽象工厂和详细工厂所有简化为一个工厂类,该工厂类中有两个工厂方式

5. 重构示例2-反射+简朴工厂

通过使用反射我们可以免去在工厂方式中使用switch语句,
通过反射获取需要确立实例的工具名,然后确立该类的实例工具(本质上就是依赖注入
看上去似乎并没有变得加倍利便,但其实是若有产物族比较多的情况下,switch语句的case语句也响应的变多
以是使用反射,可以省略使用switch照样不错的。

代码实现,在 4. 重构示例2-使用简朴工厂改善抽象工厂的基础上,修改工厂类:

完整演示Demo代码下载

public class DatabaseFactory
{
    //详细产物所在的程序集名称
    private static readonly string AssemblyName = "04抽象工厂模式-多数据库毗邻-反射+简朴工厂";
    private static readonly string db = "MSSQL";//若是需要替换数据库则将字符串改为"Oracle"

    public static IUserService CreateUserService()
    {
        //详细产物的完全限命名
        //注重由于我们的这个项目中有特殊字符,以是程序集的名字和项目名不一致,查看程序集名和命名空间名可以右键项目属性
        string className = "_04抽象工厂模式_多数据库毗邻_反射_简朴工厂" + "." + db + "UserService";
        return (IUserService)Assembly.Load(AssemblyName).CreateInstance(className);
      
    }
    public static IDepartmentService CreateDeprService()
    {
        string className = "_04抽象工厂模式_多数据库毗邻_反射_简朴工厂" + "." + db + "DepService";
        return (IDepartmentService)Assembly.Load(AssemblyName).CreateInstance(className);
    }
}

6. 重构示例2-反射+设置文件+简朴工厂

5. 重构示例2-反射+简朴工厂若是需替换数据,照样需要修改private static readonly string db = "MSSQL"字段
即任然需要修改代码后在重新编译,我们可以将需要修改的字段值放在设置文件中

完整演示Demo代码下载

修改5. 重构示例2-反射+简朴工厂如下:

①首先本项目添加引用"System.Configuration"

②在设置文件App.Config中添加如下设置

<configuration>
  <appSettings>
    <add key="db" value="MSSQL"/><!--替换数据则<add key="db" value="Oracle"/>-->
  </appSettings>
</configuration>

③修改工厂类中的db字段

private static readonly string db = ConfigurationManager.AppSettings["db"];//db字段的值从设置文件中读取

【说明】:其实在所有在用到简朴工厂的地方,都可以思量使用反射手艺去除去switch或if,排除分支判断带来的耦合

7. 总结剖析

整个示例项目,由工厂方式模式-->抽象工厂模式-->简朴工厂模式,你可以仔细的查看三个实现方式的程序类图,值得琢磨!

7.1 优点

从UML类图中就可以发现:

  1. 便于交流产物系列,每一个详细的工厂工具都只是在客户端中初始化时刻实现一次,以是改变一个详细的工厂工具是十分简朴的,以是替换一个产物序列也就变得简朴了。

    简朴的说,就是由于详细产物都是由详细的工厂确立的,以是在替换产物族的时刻只需要简朴的修改详细工厂工具即可

  2. 确立详细产物工具的历程和客户端星散(可以从UML中显著看出),客户端通过操作抽象产物接口实现操作详细产物实例,详细产物的类名不会泛起在客户端中。

7.2 瑕玷

  1. 添加新的产物族是异常简朴的,首先在响应的产物品级结构中添加新的详细产物,然后添加一个详细工厂即可。

  2. 添加新的产物品级是异常贫苦的,首先要添加抽象产物接口,接着派生所有的详细产物,还要在抽象工厂中添加方式,以及所有的详细工厂中实现该方式。

对比以上就明了:
抽象工厂模式的扩展有一定的“开闭原则”倾斜性
当增添一个新的产物族时只需增添一个新的详细工厂,不需要修改原代码,知足开闭原则。
当增添一个新的产物品级时,则所有的工厂类都需要举行修改,不知足开闭原则。

7.3 顺应场所

系统中有多个产物族,但每次只使用其中的某一族产物。切换产物族只需要修改一下详细工厂工具即可。

好比本文示例中,针对差别数据库操作我们可以实现差别的产物族,切换数据库只需要简朴的修改详细工厂工具。


8. 参考及源码

  • 示例中源代码下载

  • 设计模式——面向工具的设计原则

  • 设计模式——简朴工厂模式

  • 设计模式——工厂方式模式

  • 设计模式——单例模式

,

Sunbet

Sunbet www.ningyanganews.com Sunbet以著名的服务态度及优秀的网络环境,Sunbet客服24小时在线让你玩得过瘾,赢得开心。

Sunbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:重庆新闻频道:设计模式——抽象工厂模式

网友评论

  • (*)

最新评论

  • AllbetGmaing代理 2020-08-26 00:01:48 回复

    联博统计www.326681.com采用以太坊区块链高度哈希值作为统计数据,联博以太坊统计数据开源、公平、无任何作弊可能性。联博统计免费提供API接口,支持多语言接入。跟别的文不一样

    1

文章归档

站点信息

  • 文章总数:794
  • 页面总数:0
  • 分类总数:8
  • 标签总数:1433
  • 评论总数:425
  • 浏览总数:34829