前言
对比现实生活中,工厂的作用是为了产生各种各样的产品,而在面向对象编程中,工厂产生的自然就是程序中的“产品”,一般情况下就是程序中经常出现的各种对象了。你会在各种框架、组件中看到xxxFactory,yyyFactory这些都是工厂设计模式。为什么要有工厂设计模式?答案就是解耦,工厂的作用就是帮我们返回我们需要的对象,当我们需要对象的时候通过工厂来获取就可以了,至于其他有关这个对象的细节,他是怎么创建的,何时创建的,怎么初始化的,调用者不需要关心,也不需要参与其中,从而达到了解耦的目的
常用的工厂模式大概有三种:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
以上三种工厂模式相对都比较简单,这里就借鉴常用的例子,以汽车这个产品为代表引入三种模式的实现,并简单概括之。
1.简单工厂模式
简单工厂顾名思义就是简单的工厂,也叫静态工厂模式,在我看来它更像一个杂货工厂,该模式只是简单的实现了一个解耦的效果,但是通过它引入工厂模式却很合适
UML图:
以汽车为例,首先描述汽车接口以及实现类
public interface Car {
void run();
}
public class Audi implements Car{
@Override
public void run() {
System.out.println("audi run...");
}
}
public class Byd implements Car{
@Override
public void run() {
System.out.println("byd run...");
}
}
描述汽车工厂
public class SimpleCarFactory {
public static Car produce_car(String brandName){
if (Objects.equals(brandName,"audi")){
return new Audi();
}else if (Objects.equals(brandName,"byd")){
return new Byd();
}else {
//...
}
return null;
}
}
测试
public class Test {
public static void main(String[] args) {
Car audi = SimpleCarFactory.produce_car("audi");
audi.run(); //audi run...
}
}
以上代码来看,相对简单,根据传入的参数不同工厂返回不同的对象,对象创建的过程由工厂来完成,达到了解耦的目的,但是问题也很明显,该模式下并没有实现高内聚的原则,所有对象创建的逻辑全部都在工厂中,当有新的产品需要生产时,就不得不去修改工厂类,而且随着产品数量增多,工厂类也会冗长不堪
使用场景:
工厂类负责创建的对象比较少,客户只知道传入了工厂类的参数,对于始何创建对象(逻辑)不关心
2.工厂方法模式
工厂方法模式是在简单工厂模式的基础上,更加细化了工厂的职责,将具体的产品交由具体的工厂负责,反之获取特定的产品需要通过特定的工厂。换句话说就是进一步抽象了工厂,使得工厂变得更容易扩展。
还是以上边的汽车为例,汽车还是那个汽车,但是这个时候工厂需要发生一些变化,我们针对不同的汽车创建不同的工厂
UML:
抽象一个汽车工厂接口出来
public interface CarFactory {
Car produce_car();
}
根据不同的汽车实现各自的工厂
public class AudiCarFactory implements CarFactory{
@Override
public Car produce_car() {
return new Audi();
}
}
public class BydCarFactory implements CarFactory{
@Override
public Car produce_car() {
return new Byd();
}
}
测试
public class Test {
public static void main(String[] args) {
CarFactory audiFactory = new AudiCarFactory();
Car audi = audiFactory.produce_car();
audi.run(); //audi run...
CarFactory bydFactory = new BydCarFactory();
Car byd = bydFactory.produce_car();
byd.run(); //byd run ...
}
}
根据以上可以看出,工厂的职责更加明确了,并且也对工厂进行了向上的抽象,这使得增加了一定的扩展性,遵循了开闭原则,进行扩展的时候,只需要修改客户端代码就可以了。
但是如果产品变得非常多,亦或者是产品的种类划分的更细节,这会导致工厂类也会随着膨胀,而且产品本身发生了变化,将不得不去修改对应的工厂类,这也会使得工作变得繁重起来。
适用场景:
- 客户只知道创建产品的工厂名,而不知道具体的产品名
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口
- 客户不关心创建产品的细节,只关心产品的品牌
3.抽象工厂
抽象工厂相对于前两种稍微要复杂一些,从表现上看相对于工厂方法进行了更多的抽象,将产品进行了更加细致的分组,除了汽车品牌之外,再引入汽车的驱动方式,电机驱动和内燃机驱动,分别对byd和audi进行描述,如下
UML:
首选根据驱动方式,定义两个基类
/**
* @description: 电车
* @version: 1.0
*/
public abstract class ElectricCar {
abstract void run();
}
/**
* @description: 燃油车
* @version: 1.0
*/
public abstract class FuelCar {
abstract void run();
}
结合汽车品牌,可以衍生出四种汽车类型
- 奥迪燃油车
- 奥迪电动车
- 比亚迪燃油车
- 比亚迪电动车
分别描述它们,如下
/**
* @description: audi燃油车
* @version: 1.0
*/
public class FuelAudiCar extends FuelCar{
@Override
void run() {
System.out.println("Fuel audi run...");
}
}
/**
* @description: audi电车
* @version: 1.0
*/
public class ElectricAudiCar extends ElectricCar{
@Override
void run() {
System.out.println("Electric audi run...");
}
}
/**
* @description: byd燃油车
* @version: 1.0
*/
public class FuelBydCar extends FuelCar{
@Override
void run() {
System.out.println("Fuel byd run...");
}
}
/**
* @description: byd电车
* @version: 1.0
*/
public class ElectricBydCar extends ElectricCar{
@Override
void run() {
System.out.println("Electric byd run...");
}
}
接下来就是汽车工厂,分别创建byd和audi的汽车工厂
/**
* @description: audi工厂
* @version: 1.0
*/
public class AudiFactory implements CarFactory{
@Override
public ElectricCar produce_electric_car() {
return new ElectricAudiCar();
}
@Override
public FuelCar produce_fuel_car() {
return new FuelAudiCar();
}
}
/**
* @description: byd工厂
* @version: 1.0
*/
public class BydCarFactory implements CarFactory{
@Override
public ElectricCar produce_electric_car() {
return new ElectricBydCar();
}
@Override
public FuelCar produce_fuel_car() {
return new FuelBydCar();
}
}
测试
/**
* @description: TODO
* @create by twotiger2tigersofast
* @version: 1.0
*/
public class Test {
public static void main(String[] args) {
CarFactory audiFactory = new AudiFactory();
CarFactory bydFactory = new BydCarFactory();
//audi电车
ElectricCar electricAudiCar = audiFactory.produce_electric_car();
electricAudiCar.run();
//audi燃油车
FuelCar fuelAudiCar = audiFactory.produce_fuel_car();
fuelAudiCar.run();
//byd电车
ElectricCar electricBydCar = bydFactory.produce_electric_car();
electricBydCar.run();
//byd油车
FuelCar fuelBydCar = bydFactory.produce_fuel_car();
fuelBydCar.run();
}
}
Electric audi run...
Fuel audi run...
Electric byd run...
Fuel byd run...
与工厂方法不同的是,工厂方法一个具体的工厂负责一个具体的产品的生产,而抽象工厂一个工厂则负责了多个同族产品的生产,同一个工厂创建同族中的一系列产品,这一系列产品都归属于这个工厂,例如只要是byd工厂生成的电车,一定是byd电车。
这里有两个概念:
- 产品等级结构:产品等级结构指的是产品的继承结构,示例中的电车、燃油车就是产品等级结构
- 产品族:一个工厂可以生产的所有产品共同组成一个产品族,如byd电车和byd燃油车。
通过划分产品等级和产品族,减少了工厂类的数量,扩展性方面既然遵循开闭原则
当然,以上仅仅是示例,反过来也可以,以品牌作为产品族,同一个品牌下的燃油车和电动车作为产品的等级结构,具体需要根据实际情况去选择。
4.总结
总之,工厂模式就是为了方便创建同一接口定义的具有复杂参数和初始化步骤的不同对象。工厂模式一般用来创建复杂对象。通过工厂对客户端屏蔽了对象创建过程的复杂性,客户端只需要获取使用就可以了,其他的不需要客户端参与。