zoukankan      html  css  js  c++  java
  • 设计模式中的那些工厂

    设计模式中的那些工厂

    Intro

    设计模式中有几个工厂模式,聊一聊这几个工厂模式的各自用法和使用示例,工厂模式包含简单工厂,抽象工厂,工厂方法,这些均属于创建型模式,
    所谓创建型模式,就是说这几个设计模式是用来创建对象的。

    简单工厂

    首先来说一说,最简单的简单工厂

    简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例

    严格的来说,简单工厂模式是工厂模式家族中最简单实用的模式,但不属于23种 GOF 设计模式之一。因为每次要新增类型的时候必须修改工厂内部代码,不符合开闭原则。

    来看一个例子:

    public class OperationFactory
    {
        public static Operation CreateOperation(string operate)
        {
            Operation operation = null;
            switch (operate)
            {
                case "+":
                    operation = new OperationAdd();
                    break;
    
                case "-":
                    operation = new OpertaionSub();
                    break;
    
                case "*":
                    operation = new OperationMul();
                    break;
    
                case "/":
                    operation = new OperationDiv();
                    break;
            }
            return operation;
        }
    }
    

    这是一个简单的计算器的示例,支持简单的加减乘除操作,如果要增加一个操作的话就必须要有增加一个 switch ... case 分支,需要修改 CreateOperation 方法不能满足对扩展开放对修改关闭的开闭原则,所以普遍地认为简单工厂不属于设计模式之一,但是我觉得有时候简单的业务处理用简单工厂还是比较方便的。

    抽象工厂

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

    实现抽象工作模式所需要的组件,主要部分:

    • 抽象工厂/抽象产品
    • 具体工厂1/具体产品1
    • 具体工厂2/具体产品2
    • ...

    在客户端根据不同的配置选择不同的工厂,例如根据配置的数据库类型的不同选择使用 Access 数据库仓储的工厂还是使用 SqlServer 数据库的仓储工厂

    示例:

    IDbFactory factory = new AccessFactory();
    var userRepo = factory.CreateUserRepo();
    userRepo.Insert(null);
    var departmentRepo = factory.CreateDepartmentRepo();
    
    factory = new SqlServerFactory();
    userRepo = factory.CreateUserRepo();
    userRepo.Insert(null);
    

    工厂方法

    工厂方法模式(Factory Method)定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到子类。

    工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现客户端的操作,也会存在着选择判断的问题,不过和简单工厂相比,简单工厂的选择判断是在工厂内部,而工厂方法则将选择判断转移到了客户端。

    示例:

    ILeifengFactory factory = new UndergraduteFactory();
    var studentLeifeng = factory.CreateLeifeng();
    studentLeifeng.BuyRice();
    
    factory = new VolunteerFactory();
    var leifeng1 = factory.CreateLeifeng();
    leifeng1.Sweep();
    

    More

    工厂模式的作用无外乎下面这四个。这也是判断要不要使用工厂模式的最本质的参考标准。

    • 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
    • 代码复用:创建代码抽离到独立的工厂类之后可以复用。
    • 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
    • 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。

    工厂方法和抽象工厂的区别

    工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类
    抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类
    区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。再通俗深化理解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。

    抽象工厂关键在于产品之间的抽象关系,所以一般至少要两个产品;工厂方法在于生成产品,不关注产品间的关系,所以可以只生成一个产品。

    抽象工厂更像一个复杂版本的策略模式,策略模式通过更换策略来改变处理方式或者结果;而抽象工厂的客户端,通过更换工厂而改变结果。

    工厂方法目的是生产产品,所以能看到产品,而且还要使用产品。当然,如果产品在创建者内部使用,那么工厂方法就是为了完善创建者,从而可以使用创建者。另外创建者本身是不能更换所生产产品的。

    抽象工厂的工厂是类;工厂方法的工厂是方法。抽象工厂的工厂类就做一件事情生产产品。生产的产品给客户端使用,绝不给自己用。工厂方法生产产品,可以给系统用,可以给客户端用,也可以自己这个类使用。自己这个类除了这个工厂方法外,还可以有其他功能性的方法。

    选择的优化

    简单工厂因为选择是在工厂内部的,不符合开闭原则,抽象工厂和工厂方法是将选择权交给客户端,由客户端根据需要自己决定要实例化的工厂。
    在实际应用的时候大部分情况是只会使用一种工厂,这种情况我们一般可以借助反射+配置来优化选择,如果使用依赖注入,可以直接注入需要的服务即可。

    使用反射+配置优化

    private static readonly string AssemblyName = "AbstractFactoryPattern";
    private static readonly string DbName = ConfigurationHelper.AppSetting("DbName");
    
    public static IUserRepo CreateUserRepo()
    {
        return (IUserRepo)typeof(DataAccess).Assembly.CreateInstance($"{AssemblyName}.{DbName}UserRepo");
    }
    
    public static IDepartmentRepo CreateDepartmentRepo()
    {
        return (IDepartmentRepo)typeof(DataAccess).Assembly.CreateInstance($"{AssemblyName}.{DbName}DepartmentRepo");
    }
    

    使用依赖注入

    依赖注入可以使得我们的代码变得更加良好,扩展性更强。

    // 依赖注入
    var builder = new ContainerBuilder();
    builder.RegisterType<VolunteerFactory>().As<ILeifengFactory>();
    builder.RegisterType<SqlServerFactory>().As<IDbFactory>();
    var container = builder.Build();
    
    var leifengFactory = container.Resolve<ILeifengFactory>();
    var volunteer = leifengFactory.CreateLeifeng();
    volunteer.Wash();
    
    var dbFactory = container.Resolve<IDbFactory>();
    dbFactory.CreateDepartmentRepo().CreateDepartment(null);
    

    Reference

  • 相关阅读:
    kafka学习默认端口号9092
    kafka搜索介绍
    进程线程区别
    linux下的mysql修改默认编码
    [LeetCode] #19 Remove Nth Node From End of List
    [LeetCode] #18 4Sum
    [LeetCode] #17 Letter Combinations of a Phone Number
    [LeetCode] #16 3Sum Closest
    编程之美2015 #1 2月29日
    编程之美2015 #2 回文字符序列
  • 原文地址:https://www.cnblogs.com/weihanli/p/factory-patterns.html
Copyright © 2011-2022 走看看