工厂方法模式
工厂方法模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
设计原则:1.依赖抽象,不要依赖具体类。(依赖倒置,减少具体类的依赖)
工厂方法模式-定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
设计背景
汽车工厂进行汽车生产时,汽车的“制造步骤”都是一致的。但唯独具体的车型是不同的。
制造步骤:
- 根据类型生产汽车
- 获得汽车对象
- 组装发动机
进行汽车内部检查
进行汽车外部检查
汽车出厂,联系客户提车
使用工厂方法模式,先定义好方法模版orderCar方法,将创建汽车的方法createCar进行抽象。
在汽车工厂在进行汽车制造时只需要传入对应的type,就可以获得具体的汽车对象。根据type创建汽车实例延迟到子类。
模式结构和定义
抽象工厂(AbstractCreator)角色:担任这个角色的是工厂方法模式的核心,它是与应用程序无关的。任何在模式中创建对象的工厂类必须继承或者实现这个接口,在实际的系统中,这个角色常常有Java抽象类来实现。对应抽象汽车工厂
具体工厂(ConcreteCreator)角色:担任这个角色的是实现了抽象工厂接口的具体Java类。具体工厂角色含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。对应丰田、本田汽车工厂
抽象产品(AbstractProduct)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在实际应用中这个角色常常由Java的抽象类来实现。对应抽象汽车类
具体产品(ConcreteProduct)角色:这个角色实现了抽象产品角色所声明的接口,工厂方法所创建的每一个对象都是某个具体产品角色的实例。对应丰田卡罗拉汽车类等等
应用实例
/**
* 抽象汽车工厂(生产汽车)
*
* @author shengyong.huang
* @date 2020-06-17
*/
public abstract class AbstractCarFactory {
/**
* 汽车生产订单
*/
/**
* 根据指定类型生成订单,生产汽车
*
* @param type
*/
public void orderCar(String type) {
AbstractCar car = createCar(type);
car.assemblyEngine();
car.externalInspection();
car.internalInspection();
System.out.printf("%s 汽车生产完成,请联系客户提车 \n", type);
}
/**
* 根据类型创建车子
*
* @param type 类型
* @return 车对象
*/
protected abstract AbstractCar createCar(String type);
}
/**
* 根据类型创建车子
*
* @param type 类型
* @return 车对象
*/
protected abstract AbstractCar createCar(String type);
}
/**
* 抽象汽车类
*
* @author shengyong.huang
* @date 2020-06-17
*/
public abstract class AbstractCar {
public void internalInspection() {
System.out.println("汽车内部检查");
}
public void externalInspection() {
System.out.println("汽车外部检查");
}
public void assemblyEngine() {
System.out.println("组装汽车发动机");
}
}
/**
* 本田雅阁
*
* @author shengyong.huang
* @date 2020-06-17
*/
public class HondaAccordCar extends AbstractCar {
}
/**
* 本田思域
*
* @author shengyong.huang
* @date 2020-06-17
*/
public class HondaCivicCar extends AbstractCar {
}
/**
* 本田飞度
*
* @author shengyong.huang
* @date 2020-06-17
*/
public class HondaFitCar extends AbstractCar {
}
/**
* 丰田卡罗拉
*
* @author shengyong.huang
* @date 2020-06-17
*/
public class ToyotaCorollaCar extends AbstractCar {
}
/**
* 丰田普拉多
*
* @author shengyong.huang
* @date 2020-06-17
*/
public class ToyotaPradoCar extends AbstractCar {
}
/**
* 广州本田汽车工厂
*
* @author shengyong.huang
* @date 2020-06-17
*/
public class GuangzhouHondaCarFactory extends AbstractCarFactory {
@Override
protected AbstractCar createCar(String type) {
if ("CIVIC".equals(type)) {
return new HondaCivicCar();
} else if ("FIT".equals(type)) {
return new HondaFitCar();
} else if ("ACCORD".equals(type)) {
return new HondaAccordCar();
} else {
throw new RuntimeException("未找到对应类型的车");
}
}
}
/**
* 一汽丰田汽车工厂
*
* @author shengyong.huang
* @date 2020-06-17
*/
public class FawToyotaCarFactory extends AbstractCarFactory {
@Override
protected AbstractCar createCar(String type) {
if ("PRADO".equals(type)){
return new ToyotaPradoCar();
} else if ("COROLLA".equals(type)){
return new ToyotaCorollaCar();
} else {
throw new RuntimeException("未找到对应类型的车");
}
}
}
/**
* 测试类
*
* @author shengyong.huang
* @date 2020-06-17
*/
public class TestMain {
public static void main(String[] args) {
AbstractCarFactory toyotaFactory = new FawToyotaCarFactory();
// 生产丰田普拉多
toyotaFactory.orderCar("PRADO");
AbstractCarFactory hondaFactory = new GuangzhouHondaCarFactory();
// 生产本田雅阁
hondaFactory.orderCar("ACCORD");
// 生产本田飞度
hondaFactory.orderCar("FIT");
}
}
优点和不足
优点
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
缺点
在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
使用场景
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。