设计模式 - 抽象工厂模式(设计模式介绍系列)(三)

174 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情

上一篇文章我们介绍了工厂模式的基本概念,同时详细地描述了“工厂方法模式”的使用。这篇文章我们将介绍“抽象工厂模式”。

抽象工厂模式

抽象工厂模式的理解和使用其实比较复杂,在我们日常工作过程中其实几乎不太会应用得到。

但是作为一个经典的设计模式,我们还是有必要在这里体会一下这个经典的设计模式的思想。

类图

抽象工厂模式存在的意义是:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类。

对比工厂方法模式,它们的区别就是:多了一层抽象,减少工厂的数量。

首先我们给出抽象工厂模式的类图,根据类图和后续的代码,我们可以更直观地理解抽象工厂模式:

Screen Shot 2022-04-25 at 2.25.13 PM.png

式例代码

然后给出实例代码,代码的应用场景依旧延续上一篇文章的“车辆工厂”。

首先描述我们的需求:

现在汽车按照动力模式主要分为两大类:“新能源”汽车和传统燃油动力汽车。

有的消费者喜欢购买新能源汽车,认为其环保节能;有的消费者由于气候原因更喜欢购买燃油汽车。

针对汽车动力模式来说,不是新能源,就是燃油汽车。

上一篇文章中,在工厂方法模式的处理下,每种类型的汽车都会创建一个工厂(如小汽车工厂、卡车工厂),如果汽车类型过多的话,那么就有很多工厂存在。

针对现在的使用场景,我们可以将概念抽象出来:按照动力类型区分,汽车不是新能源就是燃油型。

所以我们只需要两个工厂(新能源汽车工厂、燃油汽车工厂)。

代码实现

针对最上层的工厂抽象,最大的工厂还是定义创建什么车:

public interface VehicleFactory {
  Vehicle createCar();
  Vehicle createTruck();
}

创建新能源汽车的工厂:

public class NewEnergyVehicleFactory implements VehicleFactory{
  @Override
  public Vehicle createCar() {
    return new NewEnergyCar();
  }

  @Override
  public Vehicle createTruck() {
    return new NewEnergyTruck();
  }
}

创建燃油汽车的工厂:

public class FuelVehicleFactory implements VehicleFactory{
  @Override
  public Vehicle createCar() {
    return new FuelCar();
  }

  @Override
  public Vehicle createTruck() {
    return new FuelTruck();
  }
}

根据所以汽车都有的特性,抽象出汽车抽象类:

public abstract class Vehicle {

  public abstract void seats();

  public abstract void powerSystem();
}

按照工厂的产品等级,创建汽车抽象类,下一步是创建小汽车和卡车:

// 所有小汽车都有的特性
public abstract class Car extends Vehicle{
  @Override
  public void seats() {
    System.out.println("五座小汽车");
  }
}
// 所有卡车都有的特性
public abstract class Truck extends Vehicle{
  @Override
  public void seats() {
    System.out.println("两座卡车");
  }
}

小汽车分为新能源小汽车、燃油小汽车;卡车分为新能源卡车,燃油卡车:

public class NewEnergyCar extends Car{
  @Override
  public void powerSystem() {
    System.out.println("新能源小汽车");
  }
}
public class FuelCar extends Car{
  @Override
  public void powerSystem() {
    System.out.println("燃油小汽车");
  }
}
public class NewEnergyTruck extends Truck{
  @Override
  public void powerSystem() {
    System.out.println("新能源卡车");
  }
}
public class FuelTruck extends Truck{
  @Override
  public void powerSystem() {
    System.out.println("燃油卡车");
  }
}

使用

public static void main(String[] args) {
  // 需要新能源汽车就创建新能源汽车厂
  VehicleFactory newEnergyVehicleFactory = new NewEnergyVehicleFactory();

  Vehicle newEnergyCar = newEnergyVehicleFactory.createCar();
  newEnergyCar.seats();
  newEnergyCar.powerSystem();

  // 需要燃油汽车就创建燃油汽车工厂
  VehicleFactory fuelVehicleFactory = new FuelVehicleFactory();

  Vehicle fuelTruck = fuelVehicleFactory.createTruck();
  fuelTruck.seats();
  fuelTruck.powerSystem();

}

输出:

五座小汽车
新能源小汽车
两座卡车
燃油卡车

总结

工厂方法模式的工厂是创建出某一种产品;而抽象工厂模式的工厂创建出的是“一类产品”。

我们把这一类产品称为“产品族”。


根据上面的实际应用场景,小汽车(car)是一类,卡车(truck)也是一类。所以在我们的最上层汽车工厂VehicleFactory 中定义了两类产品Vehicle createCar();Vehicle createTruck();

针对我们汽车产品的继承结构我们称之为“产品等级”。

我们的汽车的产品等级从上到下可以划分为:Vechicle -> Car/Truck -> 通用属性seat -> 动力系统powerSystem。

因为所有汽车都是有座位的,同时汽车都需要有动力。所以Vehicle 接口定义了两个方法:void seat();void powerSystem();

  • 所有的小汽车都是有座位的,所以Car 实现了seat() 方法.

    • 小汽车又分为新能源汽车和燃油汽车,所以定义了两个类NewEnergyCarFuelCar 继承了Car,实现了powerSystem()方法
  • 同理,所有的卡车也都是有座位的,所以Truck 实现了seat() 方法.

    • 卡车也分为新能源汽车和燃油汽车,所以定义了两个类NewEnergyTruckFuelTruck 继承了Truck,实现了powerSystem()方法

针对上述实现,我们可以看出,其中具体的工厂是:面向多个产品等级结构去生产。

所以NewEnergyVehicleFactory 继承了VehicleFactory,实现了createCar()createCar()来生产新能源小汽车和新能源卡车。

FuelVehicleFactory 也继承了VehicleFactory,实现了createCar()createCar()来生产燃油小汽车和燃油卡车。

也就是说,找到新能源汽车厂来创建新能源汽车;找到燃油汽车厂来创建燃油车。


至此,我们已经讲解了工厂模式的三种类型,结合在一起来说就是:

  • 简单工厂模式就是以某些具体的产品为中心来写代码,有几个具体的产品,就在工厂中生产出几个产品。整个过程只有一个工厂。

  • 工厂方法模式中的产品设计是所有产品都继承一个产品类型,如小汽车Car只有一层继承关系Vehicle。工厂的职责也很明确,就是制造Vehicle 的。所以如果想生产Car,那么就要自己实现一个CarFactory,想生产一个Truck,就要自己实现一个TruckFactory。所以可以认为,工厂方法模式是有一个总工厂,具体的产品放到具体的工厂(继承总工厂)中去实现。

  • 抽象工厂模式的特点就是“总工厂”中已经规定好产品族。如“小汽车”,“卡车”。针对每个产品族有不同的产品层级(或者说依据产品特性进行分层)。依据不同的产品登等级来定义不同的工厂。比如例子中的“新能源汽车工厂”和“燃油汽车工厂”。

经过这个总结可以发现,三种工厂模式的区别就比较明显了。实际开发中,我们一般使用简单工厂模式和工厂方法模式比较多。抽象工厂模式有比较明显的缺点,即:无法扩展产品族。比如要添加一类产品“摩托车”,那么我们就需要同时修改VehicleFactory/NewEnergyVehicleFactory/FuelVehicleFactory

还是,我们要根据具体需求具体分析,没有最好的设计模式,只有最适合的设计模式。