工厂模式(简单工厂、工厂方法、抽象工厂)
工厂模式
我们一般常用的有简单工厂模式、工厂方法模式、还有抽象工厂模式,从业务上的不同,我们需要选择不同的工厂模式,不同的工厂模式复杂度有所不同。工厂模式就是把我们需要把产品(例如spring中我们所需要的bean)的创建隐藏起来,是我们客户(客户端、应用层)不可见,当我们是需要使用时,只管取,不需要知道是怎么生产出来的。这样的好处就是不需要每一个客户想要一个产品时不用自己去新建制造。
下面我们用一个足球队来举例,例如:我们需要创建一个指定足球队,足球队我们又有很多俱乐部的足球(列如巴塞罗那、皇家马德里、马德里竞技),这些足球队都会有两个方法,一个是训练,一个是比赛。当然,我们的巴塞罗那足球队、皇家马德里足球队、马德里竞技足球队都属于足球队(都会实现足球队这个接口),那么当我们创建足球队的时候,我们需要怎么样使用足球队工厂来创建我们制定的足球队呢?
首先定义一个足球队的接口,接口有一个比赛行为动作(当然也还有更多,比如训练,休息),这个方法是我们每个足球队所都会存在的:
我们需要一个创建足球队的工厂:
下面是我们需要创建的指定的足球队实例:
巴塞罗那足球队(需要实现足球队的接口):
皇家马德里足球队(需要实现足球队的接口):
下面我们就用简单工厂模式来进行对巴塞罗那足球队和皇家马德里足球队的创建。
简单工厂模式:
是指由一个工厂对象决定创建出哪一种产品类的实例。
我们需要创建指定足球队时,我们需要一个创建足球队的一个工厂。
那么我们这个工厂的方法应该怎么写呢?
创建足球队可以有3中创建方法请看代码演示:
第一种,通过我们需要创建足球队的具体名称来进行创建:
这种方法的缺点是,名字需要严格要求,参数名字且容易出错,这样容易触发空指针异常
第二种方式,通过我们足球队的类名来进行创建:
第三种比较保险,通过class对象:
几种创建方法的使用测试:
这种方法根据我们指定足球队的class对象来进行创建相对应足球队。
现在问题来了,如果我们又有一个足球队需要根据我们的足球队来进行创建呢?我们是不是还得新建一个新的足球队实例,例如我们新建一个马德里竞技的足球队,那我们的这个工厂类是不是还得在创建方法新加新建足球队的逻辑判断,这样是不是违反了我们系统的开闭原则(修改关闭、拓展开发)?那我们每次需要新加一个足球队,是不是都要修改创建工厂的类,这样就增加了我们系统的维护成本。
总结:
我们简单工厂负责创建的对象较少,客户端只要传入工厂类的参数,对于如何创建对象的过程逻辑不必关心。
使用场景:所需要创建的目标较少,业务拓展不是很多,我们可以使用简单工厂来进行创建。
缺点:工厂类的职责相对过重,增加新的产品时需要修改工厂类的判断逻辑,违背开闭原则,不易于扩展过于复杂的产品结构。
于是乎我们就出现了下面的工厂方法模式!
工厂方法模式:
是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。
工厂方法模式我们需要在最上层声明一个超级工厂,制造我们的足球队的方法我们写在这个超级工厂上面,然后具体哪一个足球队(巴塞罗那或者皇家马德里)要创建属于他们的足球队时,则声明一个属于他们的足球队制造工厂(实现超级工厂接口,实现了创建足球队的方法),这就符合我们系统软件的单一职责(某一个类或者某一个方法只做自己需要做的事)。
下面请看代码
超级工厂,以及足球队的制造方法
巴塞罗那足球队制造工厂:
皇家马德里足球队制造工厂:
使用方法:
我们假如需要创建一个巴塞罗那足球队,直接先实例化一个巴塞罗那的球队队制造工厂,然后使用工厂的createTeam方法。我们又要创建一个皇家马德里的足球队,则需要实例化一个皇家马德里的足球队制造工厂,然后我们又使用皇家马德里的制造工厂来进行皇家马德里足球队的创建。然后假如我们又要有一个新的足球队来加入进来,我们这里说马德里竞技,我们则需要新建一个马德里竞技足球队的制造工厂,实现我们的超级工厂的接口(FootballTeamFactory),实现我们的createTeam接口并且返回我们马德里竞技足球队的对象。我们就可以直接使用马德里竞技的创建工厂来进行对应的足球队创建了。
总结:
工厂方法模式符合我们的单一原则,客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。一个类通过其创建工厂来指定创建哪一个对象,用户只关心所需产品对应的工厂,无需关心创建细节。加入新产品符合开闭原则,提高了系统的可扩展性。
缺点:类的个性容易过多,增加了代码结构的复杂度以及系统的抽象性和理解难度。
抽象工厂模式:
是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。
假如现在根据我们的业务需要,现在每个俱乐部足球队又需要创建足球女队、足球青年队,那么我们应该怎么创建呢?如果根据工厂方法模式要通过每个俱乐部的创建来创建的话,是不是我们又要需要对工厂进行修改呢?
抽象工厂将一系列的产品族统一到一起创建,规定了所有可能被创建的产品集合。所以我们在设计抽象工厂模式的时候,需要把这个产品族所有可能会出现的产品考虑清楚。例如,我们在创建足球队的时候,就要想到,我们需要创建足球男队,足球女队,足球青年队,足球残疾人队等等,这些因素我们必须要提前考虑到。下面我们就用一个男队,女队,青年队做例子来看看抽象工厂模式是怎么创建的?
首先定义女队、青年队的接口:
定义女队、青年队的实例对象:
定义一个超级工厂,里面有创建足球男队,足球女队,足球青年队的方法:
我们巴塞罗那足球队制造工厂实现了这个接口后,自然也需要实现这几个方法,然后我们对里面的方法进行修改,返回我们对应所想要的足球队实例。
同理,我们的皇家马德里也需要定义一个女队和一个青年队的实例对象并且实现对应的接口,创建工厂也需要实现抽象工厂的接口实现createTeam()、createWomanTeam()、createYouthTeam()这三个方法。
这个时候,当我们想要创建巴塞罗那男队,和创建巴塞罗那女队以及青年队的时候,我们直接创建一个巴塞罗那的创建工厂,里面自然有我们所对应想创建具体什么足球队的方法。
请看演示:
总结:
客户端(应用层)不依赖于产品类型实例如何被创建、实现等细节。强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的代码,提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。 具体产品在应用层代码隔离,无须关心创建细节
好处:将一系列的产品族统一到一起创建
缺点:规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口,增加了系统的抽象性和理解难度,抽象工厂模式不符合开闭原则。
工厂模式总结:
我们所有的系统软件都是一代一代迭代优化而来,允许经过一段时间后对系统修改一次,对代码的升级,工厂模式常用的三种模式针对不同的业务需求可以选用不同的模式,但是个个模式都是利与弊,我们需要谨慎选用不同的模式方便业务的扩展。