设计模式中的工厂模式是我们编写代码时常用的一种构造模式,用于创建指定类的实例。我们如何在不使用设计模式的情况下创建类的实例?别想太多,这个问题没有错,就是我们在写代码的时候,直接使用new关键字,直接创建实例。比如在Java语言中,通过new关键字直接调用类的构造函数来完成实例的创建。人类{}人p1=newPerson();像Go这样的语言,虽然它们是非面向对象的语言,但也提供了创建指向类型实例的指针的新方法。typePersonstruct{}varp1Personp1=new(Person),既然可以直接new,肯定有人会问,“那为什么要用设计模式呢?而且听说工厂模式里面有好几个工厂”。这个问题不难回答,因为我们在各种项目中总是将一些既定的业务处理逻辑抽象到公共类库中,这样我们就可以找到一个切入点,让产品开发形成一个闭环。。。你一定学不会以上俚语,因为在公司很容易出不好的结果。。。我来这里只是为了增强欢乐感,再认真的说一遍。刚才讲了把一些执行过程清晰但流程细节不同的业务处理逻辑抽象成一个公共类库。这时候,一组行为相同的类会实现一个固定流程的接口,但是在程序中创建哪个类实例是事先不知道的,只能根据运行参数动态判断。除了类实例化的过程之外,我们可能还需要收敛,以确保产生的实例能够满足我们的预期。所以,如果你聪明的话,你肯定会想到,这个时候,我让类库暴露了另一个方法,比如NewXXX。这个NewXXX方法可以根据条件产生特定类型的实例返回给业务程序。如果你能想到这一点,恭喜你,此时你已经使用工厂模式了。有时是这种情况。在编写代码时,您会不自觉地使用设计模式,尽管您可能不知道。当我们熟悉了设计模式后,自然就能自觉地写出更好的代码。在设计模式中,工厂模式可以细化为三种类型的工厂:简单工厂工厂方法和抽象工厂。这三类工厂模式的抽象依次递增。就像上面给出的例子,你在不知不觉中使用的是“简单工厂”。有了这种设计模式,随着这类流程的类库越来越复杂,对抽象的要求也越来越高,我们可能还会使用后两种工厂模式。下面我将通过一些例子来分析这三种工厂模式。简单工厂Go语言中没有构造函数,所以一般定义NewXXX函数来初始化相关类。当NewXXX函数返回一个接口时,就是一个简单的工厂模式。考虑一个简单的应用场景,提供了多种语言的打印机,它们都源自一个Printer接口。//Printer简单工厂返回的接口类型Printerinterface{Print(namestring)string}程序通过简单工厂为客户端提供所需语言的打印机。funcNewPrinter(langstring)Printer{switchlang{case"cn":returnnew(CnPrinter)case"en":returnnew(EnPrinter)default:returnnew(CnPrinter)}}在这个场景中,我们首先提供两种语言的打印机,它们是打印机接口的具体实现类型。//CnPrinter是Printer接口的实现,讲中文typeCnPrinterstruct{}func(*CnPrinter)Print(namestring)string{returnfmt.Sprintf("Hello,%s",name)}//EnPrinter是Printer接口的实现,说的是ChinesetypeEnPrinterstruct{}func(*EnPrinter)Print(namestring)string{returnfmt.Sprintf("Hello,%s",name)}这个场景下,工厂,产品接口,具体product类之间的关系可以用下图表示。客户只需要告诉工厂打印机产品是哪种语言,工厂就会将产品退还给客户。《实例源码运行Demo发送go-factory到公众号「网管bi唯」接收》printer:=NewPrinter("en")fmt.Println(printer.Print("Bob"))简单工厂模型主要包括3个角色。简单工厂:是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类中创建产品类的方法可以被外界直接调用,创建需要的产品对象。抽象产品:是简单工厂创建的所有对象的抽象父类/接口,负责描述所有实例的行为。具体产品:是简单工厂模式的创建对象。简单工厂的优点是简单,缺点是如果扩大具体产品的生产,必须修改工厂内部,增加Case。一旦产品太多,简单工厂就会显得过于臃肿。为了解决这个问题,出现了下一级工厂模式——工厂方法。FactoryMethod工厂方法模式(FactoryMethodPattern),也称为多态工厂模式,是指定义一个用于创建对象的接口,但是由实现这个接口的工厂类来决定实例化哪个产品类。实例化被推迟到子类。在工厂方法模式中,产品不再由单一的工厂类来生产,而是由工厂类的子类来实现具体产品的创建。因此,在添加产品时,只需要添加对应工厂类的子类即可解决简单工厂生产产品过多时内部代码臃肿(switch...case分支过多)的问题。下面通过一个简单的例子来理解工厂方法的设计思想。考虑一家生产计算器的工厂。每种类型的计算器产品都由一个子工厂生产。注意:Go中没有继承,所以这里说的工厂子类实际上是一个具体的工厂类,直接实现了工厂接口。//OperatorFactory工厂接口,由具体的工厂类实现typeOperatorFactoryinterface{Create()MathOperator}//MathOperator实际产品实现的接口——表示数学运算符应该有什么行为typeMathOperatorinterface{SetOperandA(int)SetOperandB(int)ComputeResult()int}现在我们假设程序可以产生两种类型的计算器,加法计算器和乘法计算器,即在工厂方法模式中,有两个子类工厂。//PlusOperatorFactory是PlusOperator加法运算符的工厂类typePlusOperatorFactorystruct{}func(pf*PlusOperatorFactory)Create()MathOperator{return&PlusOperator{BaseOperator:&BaseOperator{},}}//MultiOperatorFactory是乘积struct的工厂类型MultiOperatorFactory{}func(mf*MultiOperatorFactory)Create()MathOperator{return&MultiOperator{BaseOperator:&BaseOperator{},}}这两个子类工厂分别用于生产加法和乘法计算器。注意:为了便于理解,这里的例子都非常简单,在真实场景中,每个子类工厂创建产品实例的时候,可以放入复杂的逻辑,不能简单的New。//BaseOperator是所有Operators的基类//封装了公共方法,因为Go不支持继承,所以具体的Operator类//只能组合起来实现类似继承的行为。typeBaseOperatorstruct{operandA,operandBint}func(o*BaseOperator)SetOperandA(operandint){o.operandA=operand}func(o*BaseOperator)SetOperandB(operandint){o.operandB=operand}//PlusOperatorFactory是PlusOperatorAdder工厂类typePlusOperatorFactorystruct{}func(pf*PlusOperatorFactory)Create()MathOperator{return&PlusOperator{BaseOperator:&BaseOperator{},}}//PlusOperator实际产品类--AddertypePlusOperatorstruct{*BaseOperator}//ComputeResult计算并得到结果func(p*PlusOperator)ComputeResult()int{returnp.operandA+p.operandB}//MultiOperatorFactory是乘积的工厂类型MultiOperatorFactorystruct{}func(mf*MultiOperatorFactory)Create()MathOperator{return&MultiOperator{BaseOperator:&BaseOperator{},}}//MultiOperator的实际乘积类--multipliertypeMultiOperatorstruct{*BaseOperator}func(m*MultiOperator)ComputeResult()int{returnm.operandA*m.operandB}在这个场景中,工厂、产品接口、具体产品类别之间的关系可以用下图表示。类图--工厂方法试运行--客户端使用子类工厂创建Product实例。//测试运行示例源码运行Demo发送go-factory到公众号「网管口bi唯」接收funcmain(){varfactoryOperatorFactoryvarmathOpMathOperatorfactory=&PlusOperatorFactory{}mathOp=factory.Create()数学运算。SetOperandB(3)mathOp.SetOperandA(2)fmt.Printf("Plusoperationreuslt:%d\n",mathOp.ComputeResult())factory=&MultiOperatorFactory{}mathOp=factory.Create()mathOp.SetOperandB(3)mathOp.SetOperandA(2)fmt.Printf("Multipleoperationreuslt:%d\n",mathOp.ComputeResult())}工厂方法模式的优点增强了灵活性,对于新产品的创建,只需要写一个对应的工厂种类。典型的解耦框架。高层模块只需要知道产品的抽象类,不需要关心其他的实现类,满足迪米特定律、依赖倒置原则和里氏替换原则。工厂方法模式的缺点是类的数量太多,增加了复杂度。增加了系统的抽象性和理解难度。只能生产一种产品。这个缺点可以通过使用抽象工厂模式来解决。无论是简单工厂还是工厂方法,都只能生产一种产品。如果工厂需要在生态中创建多个产品,则需要更进一步,使用三级工厂模型——抽象工厂。抽象工厂抽象工厂模式:用来创建一系列相关或相互依赖的对象。为了更清楚的理解工厂方法模式和抽象工厂模式的区别,我们举一个品牌产品生态的例子。比如在智能家居领域有很多公司,包括华为和小米。除了我们熟悉的手机,他们的工厂还生产电视、空调等家电产品。如果我们有幸成为他们工厂智能管理软件的供应商,想在软件系统中抽象出工厂,就不能再使用工厂方法作为设计模式了,因为工厂方法只能用来生产一个产品。我们先来看看这个用类图表示的抽象工厂的抽象多品牌多产品形态。类图--抽象工厂下面我们简单用代码实现一个抽象工厂。这个工厂可以生产智能电视和空调。当然产品的功能在代码中比较简单,就是输出一条相应的消息。目前抽象工厂有两个实际的工厂,一个是华为的工厂,一个是小米的工厂。它们用于实际生产自己的产品设备。//示例源代码运行Demo//给公众号「网管口bi口」发送工厂即可领取typeAbstractFactoryinterface{CreateTelevision()ITelevisionCreateAirConditioner()IAirConditioner}typeITelevisioninterface{Watch()}typeIAirConditionerinterface{SetTemperature(int)}typeHuaWeiFactorystruct{}func(hf*HuaWeiFactory)CreateTelevision()ITelevision{return&HuaWeiTV{}}func(hf*HuaWeiFactory)CreateAirConditioner()IAirConditioner{return&HuaWeiAirConditioner{}}typeHuaWeiTV结构{}func(ht*HuaWeiTV)Watch(){fmt.Println("看华为电视")}typeHuaWeiAirConditionerstruct{}func(ha*HuaWeiAirConditioner)SetTemperature(tempint){fmt.Printf("华为空调设置温度为%d℃\n",temp)}typeMiFactorystruct{}func(mf*MiFactory)CreateTelevision()ITelevision{return&MiTV{}}func(mf*MiFactory)CreateAirConditioner()IAirConditioner{return&MiAirConditioner{}}类型MiTVstruct{}func(mt*MiTV)Watch(){fmt.Println("看华为电视")}typeMiAirConditionerstruct{}func(ma*MiAirConditioner)SetTemperature(tempint){fmt.Printf("MiAirConditionersettemperatureto%d℃\n",temp)}funcmain(){varfactoryAbstractFactoryvartvITelevisionvarairIAirConditionerfactory=&HuaWeiFactory{}tv=factory.CreateTelevision()air=factory.CreateAirConditioner()tv.Watch()air.SetTemperature(25)factory=&MiFactory{}tv=factory.CreateTelevision()air=factory.CreateAirConditioner()tv.Watch()air.SetTemperature(26)}同样,抽象工厂也有将产品的创建推迟到工厂子类的工厂方法。如果以后增加了VIVO产品,我们可以重新创建VIVO工厂子类来扩展抽象工厂,我们可以总结出以下几点:当工厂提供的具体产品不是一个简单的对象,而是多个不同类型的具体产品时位于不同的产品层次结构中,需要使用抽象工厂模式。抽象工厂模式是所有工厂模式中最抽象和通用的形式。抽象工厂模式和工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品层次结构,而抽象工厂模式需要面对多个产品层次结构,一个工厂层次结构可以负责多个不同的产品层次结构。产品对象的创建。当一个工厂层次结构可以创建属于不同产品层次结构的产品系列中的所有对象时,抽象工厂模式比工厂方法模式更简单、更高效。抽象工厂模式的优点当需要一个产品族时,抽象工厂可以保证客户端始终只使用同一产品的产品族。抽象工厂增强了程序的可扩展性。对于新产品族的加入,只需要实现一个新的具体工厂,无需修改现有代码,符合开放封闭原则。抽象工厂模式的缺点是指定所有可能创建的产品集合。产品族难以扩展新产品,需要修改抽象工厂的接口。增加了系统的抽象性和理解难度。最后,我们通过几个比较简单的例子来了解下三种工厂模式各自的场景和优缺点。在实际使用中,当一开始项目的需求不是那么明确时,建议先使用简单工厂,等我们的业务。如果你真的需要更透彻地理解工厂方法,现在升级到工厂方法还为时不晚。抽象工厂也是如此。如果确定可以将产品生态的概念引入到更好的模型领域,那么现在开始使用抽象工厂还为时不晚。
