设计模式之工厂模式
前言:
在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下, new操作符直接生成对象会带来一些问题。举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象。 在这些情况,新对象的建立就是一个 “过程”,不仅是一个操作,像一部大机器中的一个齿轮传动。
模式的问题:你如何能轻松方便地构造对象实例,而不必关心构造对象实例的细节和复杂过程呢?
解决方案:建立一个工厂来创建对象。
实现:
一、引言
(1)假如没有工厂时代,如果想生产创建宝马车。一般做法就是客户自己去创建,然后拿来用。
(2)简单工厂模式:后来出现工业革命。用户不必再自己创建宝马车。因为客户有工厂帮他来创建。比如客户想创建宝马320i,工厂就可以创建。
(3)工厂方法模式:客户需求越来越多,一个工厂无法满足客户需求。于是单独分出很多具体的工厂。每一个具体的工厂只生产一种系列的宝马车,但是宝马工厂还是抽象的,客户需要指定某个具体的工厂才能生产出来需要的宝马车。
(4)抽象工厂模式:随着客户的需求越来越高,宝马车须有配置指定空调和指定发动机,比如客户需要宝马532i的空调,需要320i的发动机,工厂只需将有该配置的车型生产出来供客户使用即可。
二、分类
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
工厂模式可以分为三类: 1)简单工厂模式 2)工厂方法模式 3)抽象工厂模式 。
这三种模式从上往下逐步抽象,并且更具一般性。
《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。
三、区别
工厂方法模式:一个抽象产品类可以派生出多个具体的产品类、一个抽象工厂类,可以派生出多个具体的工厂类。 每一个工厂类只能创建一个具体的产品类对象。
抽象工厂模式:一个大工厂包含多个小工厂,即一个抽象产品类中包含多个抽象产品类,而每一个抽象产品类都派生多个具体的产品类,每个具体的抽象产品类都可以创建多个具体的产品类实例。
四、简单工厂
public interface IFactoryService { void createBMW(); } @Service public class BMW320Impl implements IFactoryService { @Override public void createBMW() { System.out.println("制造……BMW320"); } } @Service public class BMW523Impl implements IFactoryService { @Override public void createBMW() { System.out.println("制造……BMW523"); } } public class Factory { public IFactoryService create(String type){ if ("BMW523".equals(type)){ return new BMW523Impl(); } if ("BMW320".equals(type)){ return new BMW320Impl(); } return null; } } @SpringBootApplication public class StartClass { public static void main(String[] args) { Factory factory = new Factory(); IFactoryService shapeService = factory.create("BMW523"); shapeService.createBMW(); } }
简单工厂模式又称静态工厂方法模式。重命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。
先来看看它的组成:
1) 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,用来创建产品
2) 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。
3) 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。
下面我们从开闭原则(对扩展开放;对修改封闭)上来分析下简单工厂模式。当客户不再满足现有的车型号的时候,想要一种速度快的新型车,只要这种车符合抽象产品制定的合同,那么只要通知工厂类知道就可以被客户使用了。所以对产品部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一种新型车,都要在工厂类中增加相应的创建业务逻辑(createBMW(int type)方法需要新增case),这显然是违背开闭原则的。可想而知对于新产品的加入,工厂类是很被动的。对于这样的工厂类,我们称它为全能类或者上帝类。
五、工厂方法模式
产品类:
public interface IBMWService { void createBMW(); } @Service public class BMW523Impl implements IBMWService { @Override public void createBMW() { System.out.println("制造BMW523"); } } @Service public class BMW320Impl implements IBMWService { @Override public void createBMW() { System.out.println("制造BMW320"); } }
工厂类:
public interface Factory { IBMWService create(); } @Service public class BMW523FactoryImpl implements Factory{ @Override public IBMWService create() { return new BMW523Impl(); } } @Service public class BMW320FactoryImpl implements Factory{ @Override public IBMWService create() { return new BMW320Impl(); } }
启动类:
@SpringBootApplication public class StartClass { public static void main(String[] args) { Factory factory = new BMW320FactoryImpl(); IBMWService shapeService = factory.create(); shapeService.createBMW(); } }
工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
工厂方法模式组成:
1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活 起来——当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有 的代码。可以看出工厂角色的结构也是符合开闭原则的!
六、抽象工厂模式
产品抽象类:
public interface IAirconditionService { void create(); } @Service public class AirconditionA implements IAirconditionService{ @Override public void create() { System.out.println("创建-->AirconditionA"); } } @Service public class AirconditionB implements IAirconditionService{ @Override public void create() { System.out.println("创建-->AirconditionB"); } } public interface IEngineService { void create(); } @Service public class EngineA implements IEngineService{ @Override public void create() { System.out.println("创建-->EngineA"); } } @Service public class EngineB implements IEngineService { @Override public void create() { System.out.println("创建-->EngineB"); } }
工厂类:
public interface IAbstractFactoryService { IEngineService createEngine(); IAirconditionService createAircondition(); } @Service public class FactoryBMW320 implements IAbstractFactoryService { @Override public IEngineService createEngine() { return new EngineA(); } @Override public IAirconditionService createAircondition() { return new AirconditionB(); } } @Service public class FactoryBMW523 implements IAbstractFactoryService { @Override public IEngineService createEngine() { return new EngineA(); } @Override public IAirconditionService createAircondition() { return new AirconditionA(); } }
启动类:
@SpringBootApplication public class StartClass { public static void main(String[] args) { IAbstractFactoryService factory320 = new FactoryBMW320(); factory320.createAircondition().create(); factory320.createEngine().create(); IAbstractFactoryService factory523 = new FactoryBMW523(); factory523.createEngine().create(); factory523.createAircondition().create(); } }
优点:
抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
总结:
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。
所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。
更多设计模式请点击这里