1. 概述
工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
上面那段话其实就是工厂模式的目的,但说的比较抽象,这里来通俗地解释下。首先工厂模式是一种创建型模式,那很明显这个模式就是和对象的创建有关,所以对象的new
操作就会比较特殊。在工厂模式中,对象不是直接在客户端中new
出来,而是调用工厂类的工厂方法新建对象(也就是在这个工厂类中new,而不是在外面new)。
不直接new
而是在工厂中new
有什么好处呢?我们来模拟两种情况,一点一点慢慢推导,现在有一个汽车类car
:
-
假如你要拥有一辆汽车(获得一个
car
实例),你如果采用new
的话就相当于是造一辆车出来,虽然你是个汽车工程师(知道car
类如何构造),但是一辆车有很多个部件(car
的属性很复杂),直接造一辆车会很麻烦(代码很复杂)。如果你要造一辆车那也还可以勉强接受,那如果你要造多辆车呢?想象一下,假如
car
有几十个属性需要初始化,每次构建一个可以用的car
类需要几十行代码,这个car
类又很常用,那每次都要写几十行重复的代码谁顶得住,所以一个比较容易想到的做法就是把构建实例化对象的过程抽离出去,变成一个方法。这个其实就相当于工厂模式中的工厂方法,把建车这个过程从我(客户端)转移到汽车工厂(工厂类)中。 -
看过上面那种情况,此时你又来杠,既然创建一个实例化对象比较复杂,那就把构建方法抽离出去咯,工厂模式就这?确实,工厂模式就这,很简单对吧,它的核心就是把实例化对象的过程抽离出去。但是也没有这么简单,下面来看另一种情况。
我们都知道Java是面向对象的语言,所以有多态和继承,一个类能有多个子类或者实现类,那现在
car
类也有多个子类,比如有奔驰benz
和宝马BMW
两个子类,他们的构建过程也很复杂,car
变成了抽象类,我们每次要构建的是car
的子类,这个时候采用工厂模式又该怎么办呢?工厂方法又该怎么写?那再如果我们要考虑代码的可扩展性,假如以后又会加入别的种类的车,工厂类代码要怎么设计才能有更好的可扩展性呢?针对这种情况的代码有两种,这些不同的种类也就是工厂模式中的静态工厂和动态工厂。
这两种模式各有特点,不能说谁好谁差,而是根据实际需求使用。
所以总结一下:
- 实例化对象不用new,采用工厂方法替代
- 将选择实现类、创建对象统一管理和控制,从而将调用者跟我们的类解耦。
- 工厂模式针对可扩展性的设计,有两种代码,一种叫做静态工厂,一种叫做动态工厂。
2. 特点
在说具体实现之前,再来看一下一些比较抽象比较概念性的东西,其实工厂模式比较简单,这些还是比较容易理解的。
-
优点
- 可以避免创建者和具体产品之间的紧密耦合。
- 单一职责原则。 可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
- 开闭原则。 无需更改现有客户端代码, 就可以在程序中引入新的产品类型。
-
缺点
- 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂
-
适用情况
-
当写代码过程中, 如果无法预知对象确切类别及其依赖关系时(类是否会增加子类), 可使用工厂方法
工厂方法将创建产品的代码与实际使用产品的代码分离, 从而能在不影响其他代码的情况下扩展产品创建部分代码。
例如, 如果需要向应用中添加一种新产品, 你只需要开发新的创建者子类, 然后重写其工厂方法即可。
-
如果希望别人能扩展我们写的框架, 框架里可使用工厂方法
继承可能是扩展软件库或框架默认行为的最简单方法。 但是当你使用子类替代标准组件时, 框架如何辨识出该子类?解决方案是将各框架中构造组件的代码集中到单个工厂方法中, 并在继承该组件之外允许任何人对该方法进行重写。
举个例子:假设你使用开源 UI 框架编写自己的应用。 你希望在应用中使用圆形按钮, 但是原框架仅支持矩形按钮。 你可以使用圆形按钮
RoundButton
子类来继承标准的按钮Button
类。 但是, 你需要告诉 UI框架UIFramework
类使用新的子类按钮代替默认按钮。为了实现这个功能, 你可以根据基础框架类开发子类 圆形按钮
UIUIWithRoundButtons
, 并且重写其createButton
创建按钮方法。 基类中的该方法返回 按钮对象, 而你开发的子类返回 圆形按钮对象。 现在, 你就可以使用 圆形按钮 UI类代替 UI框架类。 就是这么简单! -
如果希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。
-
3. 实现
下面将会讲解两种工厂模式的两种实现,首先我们需要举一个实际例子,来模拟工厂模式,还是举上面那个造车的过程,现在有一个接口car
,它抽象表示车,它有一个方法driver()
表示车的驾驶,所有车都要实现这个方法,现在有两种车,奔驰Benz
和宝马BMW
,下面来看下在工厂模式怎么实现:
3.1 静态工厂
- UML类图
- JAVA代码
/** * @Author: chy * @Description: 车的接口 * @Date: Create in 16:21 2021/2/23 */ public interface Car { /** * 车的驾驶方法 */ void driver(); } /** * @Author: chy * @Description: 奔驰 * @Date: Create in 16:23 2021/2/23 */ public class Benz implements Car{ @Override public void driver() { System.out.println("开奔驰"); } } /** * @Author: chy * @Description: 宝马 * @Date: Create in 16:22 2021/2/23 */ public class BMW implements Car{ @Override public void driver() { System.out.println("开宝马"); } } /** * @Author: chy * @Description: 汽车工厂 * @Date: Create in 16:44 2021/2/23 */ public class CarFactory { /** * 第一种方式 */ public static Car getCar(String carName){ if (carName.equals("Benz")) { return new Benz(); } else { return new BMW(); } } /** * 第二种方式 */ public static Car getBenz(){ return new Benz(); } public static Car getBMW(){ return new BMW(); } } /** * @Author: chy * @Description: 客户端 * @Date: Create in 16:47 2021/2/23 */ public class Client { public static void main(String[] args) { // 演示第一种方法 Car benz = CarFactory.getCar("Benz"); benz.driver(); Car bmw = CarFactory.getCar("BMW"); bmw.driver(); // 演示第二种方法 Car benz2 = CarFactory.getBenz(); benz2.driver(); Car bmw2 = CarFactory.getBMW(); bmw2.driver(); } }
这就是静态工厂,是不是挺简单的,静态工厂也有两种实现方式,第一种是根据参数返回具体对象,第二种是为每个对象单独保存一种方法。这两种实现,各有优劣:
- 第一种:代码比较少,比较简单
- 第二种:逻辑清晰,无需判断参数;有更好的扩展性,假如现在假如一种新的种类的车,使用第二种方法是需要添加一个方法,使用第一种则是需要修改代码,我们都知道,增加优于修改。
所以对比下来,第二种方法其实是比较优秀的一种写法。
3.2 动态工厂
动态工厂在我看来不是一种好的设计,它的缺点十分明显:类爆炸(有超级多的类),但他也有优点:逻辑清晰,可扩展性强,比起静态工厂,动态工厂还是缺点大于优点。
动态工厂其实就是为每一个对象创建一个工厂,这样遵循了一个原则:增加代码优于修改代码。因为假如加入新的子类,在动态工厂中是新建一个子类的工厂,这是增加代码;在静态工厂中则是修改工厂类,这是修改代码。但我们也不一定要时时刻刻遵守规则,因为它带来的负面影响更大,所以推荐使用静态工厂。
-
UML类图
-
Java代码
/** * @Author: chy * @Description: 车的接口 * @Date: Create in 16:21 2021/2/23 */ public interface Car { /** * 车的驾驶方法 */ void driver(); } /** * @Author: chy * @Description: 奔驰 * @Date: Create in 16:23 2021/2/23 */ public class Benz implements Car{ @Override public void driver() { System.out.println("开奔驰"); } } /** * @Author: chy * @Description: 宝马 * @Date: Create in 16:22 2021/2/23 */ public class BMW implements Car{ @Override public void driver() { System.out.println("开宝马"); } } /** * @Author: chy * @Description: 汽车工厂 * @Date: Create in 16:44 2021/2/23 */ public class CarFactory { /** * 造车方法 * @return 汽车实例化对象 */ Car getCar(); } /** * @Author: chy * @Description: 奔驰汽车工厂 * @Date: Create in 17:18 2021/2/23 */ public class BenzFactory implements CarFactory{ @Override public Car getCar() { return new Benz(); } } /** * @Author: chy * @Description:宝马汽车工厂 * @Date: Create in 17:18 2021/2/23 */ public class BMWFactory implements CarFactory{ @Override public Car getCar() { return new BMW(); } } /** * @Author: chy * @Description: 客户端 * @Date: Create in 16:47 2021/2/23 */ public class Client { public static void main(String[] args) { // 获取奔驰 BenzFactory benzFactory = new BenzFactory(); Car benz = benzFactory.getCar(); benz.driver(); // 获取宝马 BMWFactory bmwFactory = new BMWFactory(); Car bmw = bmwFactory.getCar(); bmw.driver(); } }