设计模式-工厂模式

概念

工厂模式是一种创建性模式,在接口中定义创建对象的方法,将具体的创建对象的过程在子类中实现,用户只需要通过接口创建需要的对象即可,不用关注对象的具体创建过程。

通俗的讲 就是用工厂方法代替new操作创建一个实例化对象的方法,以提供一种方便的创建有同种类型接口的产品的复杂对象。

比如说一个类在创建时候,需要其他类的的信息,直接通过new关键字实例化对象会增加耦合度,不利于维护,直接通过new关键字会增加代码的耦合度,不利于维护,因此需要通过工厂模式将创建实例和使用实例分开。将创建实例的方法封装到工厂方法中,使用时直接调用工厂来进行获取。

按照实际业务场景划分,工厂模式有三种不同的实现方式,分别是:简单工厂模式、工厂方法模式和抽象工厂模式

要分清楚这三者的关系,首先我们先了解两个概念,什么是产品等级?什么是产品族?

产品等级:即有继承结构的同种类的产品,我们称之为产品等级。比如:苹果手机、小米手机。

产品族:一个具体工厂生产的不同等级的一组产品称为一个产品族。比如:小米手机、小米扫地机等。

image-20220814235546606

image-20220814235600348

如果分别用一句话来概括他们,那就是:

1)简单工厂:生产同一产品等级的任意指定的产品。(不支持增加新产品,增加新产品需要修改代码)

2)工厂方法:生产同一产品等级的固定工厂产品。(支持增加新产品)

3)抽象工厂:生产不同产品族的全部产品。(支持增加产品族,但不支持增加新产品)

简单工厂模式

概念:

简单工厂模式也叫静态工厂模式,就是工厂类(一般使用静态方法)通过接收的参数来区分并返回不同的对象实例。

感觉像是一种编程习惯。

结构:

简单工厂包含如下角色:

  • 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。

  • 具体产品 :实现或者继承抽象产品的子类

  • 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。

    实现

比亚迪和特斯拉都是能在路上跑的车。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Car{
void run();
}
class Byd implements Car{
@Override
public void run() {
System.out.println("比亚迪在路上跑");
}
}

class Tsl implements Car{
@Override
public void run() {
System.out.println("特斯拉在路上跑");
}
}

image-20220815001030919

在没有工厂的情况下,我们要让比亚迪或者特斯拉出来跑的话。我们会这样写:

1
2
3
4
5
6
7
8
9
public class CarUse {

public static void main(String[] args) {
Car c1 = new Byd();
Car c2 = new Tsl();
c1.run();
c2.run();
}
}

image-20220815001111887

此时的调用者(CarUse类)和创建者Byd和Tsl都是有关联的,调用者什么都是亲力亲为。

我们知道,工厂模式就是要分离调用者和创建者。现在我们创建一个简单工厂,由简单工厂类去统一管理创建者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//写法一
public class CarFactory {
static final int CAR_BYD = 0;
static final int CAR_TSL = 1;
public static Car createCar(int type){
switch (type) {
case 0:
return new Byd();
case 1:
return new Tsl();
default:
System.out.println("无法识别的品牌");
return null;
}
}
}
//写法二
public class CarFactory {
private static final Map<String,Car> createCar = new HashMap<String,Car>();
static{
createCar.put("byd", new Byd());
createCar.put("tsl", new Tsl());
}
public static Car createCar(String type){
if(type==null){
return null;
}
return createCar.get(type);
}

}

image-20220815001205978

虽然我们多写了一个CarFactory工厂类,整体上的代码变多了一点点。但是我们的调用者不必依赖我们的创建者,仅仅需要告诉工厂你要什么,无需知道工厂去哪里帮你实现你的需求,这样调用者就省去了很多的事情。

后来生意做红火了,来了个有钱的客户,这次要买玛莎拉蒂,也就是新产品了。我们的工厂就不适用了,我们的工厂需要找到玛莎拉蒂的实体,然后在我们的工厂上加入新的品牌,不修改代码是无法扩展了。

优缺点

优点:

封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。

缺点:

增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。

工厂方法模式:

虽然简单工厂模式解决了调用者和创建者之间的耦合,但是工厂和创建者之间依然存在着耦合。这样的设计违背了我们的“开闭原则”,未来新产品的扩展并不灵活。而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码(比如工厂类)的情况下引进新的产品,即满足开闭原则。

概念

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//声明一个抽象工厂
interface CarFactory {
Car createCar();
}
//比亚迪工厂
public class BydFactory implements CarFactory{
@Override
public Car createCar() {
return new Byd();
}
}
//特斯拉工厂
public class TslFactory implements CarFactory{
@Override
public Car createCar() {
return new Tsl();
}
}
//调用者
public class CarUse {
public static void main(String[] args) {
Car c1 = new BydFactory().createCar();
Car c2 = new TslFactory().createCar();
c1.run();
c2.run();
}
}

实现

工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式(对于一个项目或者一个独立的模块而言)只有一个工厂类,而工厂方法模式有一组实现了相同接口的工厂类。调用者不是什么都找一个工厂,而是需要什么就找什么工厂提供,这样可选择的范围更大。

image-20220815001551762

现在,我们的新客户要玛莎拉蒂,就可以直接找我们的玛莎拉蒂工厂。我们只需要有新建的玛莎拉蒂工厂即可满足客户的要求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//新建玛莎拉蒂工厂
public class MsldFactory implements CarFactory{
@Override
public Car createCar() {
return new Msld();
}

}

class Msld implements Car{
@Override
public void run(){
System.out.println("玛莎拉蒂在路上跑");
}
}

我们的客户,只需要找到这家玛莎拉蒂工厂即可。

1
2
3
4
5
6
7
8
9
10
11
public class CarUse {

public static void main(String[] args) {
Car c1 = new BydFactory().createCar();
Car c2 = new TslFactory().createCar();
c1.run();
c2.run();
Car c3 = new MsldFactory().createCar();
c3.run();
}
}

优缺点:

  • 优点

1)用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;

2)对于新产品的创建,只需多写一个相应的工厂类,不会影响已有的代码,后期维护容易,增强系统的扩展性。

  • 缺点

类的个数容易过多,增加系统的复杂度和理解难度。

注:从结构的复杂度、代码的复杂度以及客户端调用者的编程

简单工厂模式和工厂方法模式的比较:

比较维度 简单工厂模式 工厂方法模式 说明
结构、代码复杂度 简单工厂模式占优,只需要一个工厂类,而工厂方法模式会随着工厂的数量增加而增加
调用者使用难度 简单工厂模式的工厂类是静态类,调用者无需实例化
扩展性 工厂方法模式满足OCP原则,具有非常良好的扩展性。简单工厂的扩展性稍弱,但是只要改动不大产品不是特别复杂,简单工厂模式无疑是更优的选择。

注:事实上我们有时并不需要像一些框架一样做到那么灵活,反而一般都用简单工厂模式,这样代码简洁逻辑也清晰,可以使用

应用场景:

1)对于产品种类相对较少的情况,考虑使用简单工厂模式;

2)当您想为您的库或框架的用户提供一种扩展其内部组件的方法时,请使用工厂方法模式。

DJK中工厂模式的使用:

1)java.util.Calendar中的getInstance()方法使用了工厂模式。

抽象工厂: