**
设计模式(二)之工厂模式
**
-
案例说明
本文以生产汽车和电池的例子来体现三种工厂模式(简单工厂模式、工厂方法模式和抽象工厂模式)的写法和优缺点,结合UML类图和代码一起说明。
-
不用工厂模式的传统写法
有三种车,电动车、汽油车和太阳能车,每次卖车店要预订车子都要直接跟具体的汽车类型有关联,每次要新增一种车型都要修改多处代码,扩展性低,耦合性高。
@Data
public abstract class Car {
/**
* 车的名称,ElectricCar为电动车,PetrolCar为汽油车,SolarCar为太阳能汽车
*/
private String name;
/**
* 各种类型的车采购原料流程不一样,定义为抽象方法
*/
abstract public void purchase();
public void assembly(){
System.out.println(name + " 配件组装");
}
public void shipment(){
System.out.println(name + " 出库运输");
}
}
public class ElectricCar extends Car {
@Override
public void purchase() {
System.out.println("ElectricCar的配件采购");
}
}
public class PetrolCar extends Car {
@Override
public void purchase() {
System.out.println("PetrolCar的配件采购");
}
}
public class SolarCar extends Car {
@Override
public void purchase() {
System.out.println("SolarCar的配件采购");
}
}
public class CarShop {
/**
* 模拟客户端调用controller层
*/
public static void main(String[] args) {
while (true){
Scanner scanner = new Scanner(System.in);
String input = scanner.next();
OrderCar orderCar = new OrderCar();
Car car = orderCar.getCar(input);
System.out.println(car);
}
}
}
public class OrderCar {
/**
* 模拟service层
*/
public Car getCar(String name) {
Car car = null;
switch (name){
case "ElectricCar":
car = new ElectricCar();
break;
case "PetrolCar":
car = new PetrolCar();
break;
case "SolarCar":
car = new SolarCar();
break;
default:
throw new RuntimeException("无此类型的汽车");
}
car.setName(name);
car.purchase();
car.assembly();
car.shipment();
System.out.println("您订购的" + car.getName() + "已送达您的车库~~");
return car;
}
}
此处要在控制台输入一个参数来模拟前端传入一个参数。
D:\jdk8\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=63140:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\ideaworkspace\design_pattern\design\target\classes;D:\dev_tools\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar com.wd.factory.none.CarShop
ElectricCar
ElectricCar的配件采购
ElectricCar 配件组装
ElectricCar 出库运输
您订购的ElectricCar已送达您的车库~~
Car(name=ElectricCar)
PetrolCar
PetrolCar的配件采购
PetrolCar 配件组装
PetrolCar 出库运输
您订购的PetrolCar已送达您的车库~~
Car(name=PetrolCar)
SolarCar
SolarCar的配件采购
SolarCar 配件组装
SolarCar 出库运输
您订购的SolarCar已送达您的车库~~
Car(name=SolarCar)
传统写法总是会把判断的代码写在逻辑处理层,代码看起来会非常臃肿。
- 简单工厂模式
定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)。简单工厂模式属于创建型模式,是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。先看一下类图:
在OrderCar和具体的车型实体类之间加了一层工厂的实体类,这样就能是业务代码只需要直接跟工厂类对话,新增车型只需在工厂类里面新增判断即可,可使得业务层代码更为简洁,下面是具体的代码实现:
public class CarShop {
/**
* 模拟客户端调用controller层
*/
public static void main(String[] args) {
while (true){
Scanner scanner = new Scanner(System.in);
String input = scanner.next();
OrderCar orderCar = new OrderCar();
Car car = orderCar.getCar(input);
System.out.println(car);
}
}
}
public class OrderCar {
/**
* 模拟service层
*/
public Car getCar(String name) {
Car car = SimpleFactory.createCar(name);
car.purchase();
car.assembly();
car.shipment();
System.out.println("您订购的" + car.getName() + "已送达您的车库~~");
return car;
}
}
public class SimpleFactory {
public static Car createCar(String name){
Car car = null;
switch (name){
case "ElectricCar":
car = new ElectricCar();
break;
case "PetrolCar":
car = new PetrolCar();
break;
case "SolarCar":
car = new SolarCar();
break;
default:
throw new RuntimeException("无此类型的汽车");
}
car.setName(name);
return car;
}
}
此处要在控制台输入一个参数来模拟前端传入一个参数。
D:\jdk8\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=63610:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\ideaworkspace\design_pattern\design\target\classes;D:\dev_tools\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar com.wd.factory.simplefactory.CarShop
ElectricCar
ElectricCar的配件采购
ElectricCar 配件组装
ElectricCar 出库运输
您订购的ElectricCar已送达您的车库~~
Car(name=ElectricCar)
PetrolCar
PetrolCar的配件采购
PetrolCar 配件组装
PetrolCar 出库运输
您订购的PetrolCar已送达您的车库~~
Car(name=PetrolCar)
SolarCar
SolarCar的配件采购
SolarCar 配件组装
SolarCar 出库运输
您订购的SolarCar已送达您的车库~~
Car(name=SolarCar)
- 工厂方法模式
假设我们多了一个需求,就是要生产不同地址厂家的汽车,此时使用简单工厂模式,创建不同的简单工厂类,比如BYDSimpleFactory、VolksWagenSimpleFactory 等等,从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好,那我们就可以考虑使用工厂方法模式。先看一下类图:
这里将简单工厂模式中的简单工厂类换成了工厂接口,具体的实现方法由其两个子类BYDFactory和VolkswagenFactory来实现,由子类决定要实例化的类,可见,工厂方法模式是将对象的实例化推迟到子类。下面是代码实现:
public class CarShop {
/**
* 模拟客户端调用controller层
*/
public static void main(String[] args) {
while (true){
Scanner scanner = new Scanner(System.in);
String address = scanner.next();
String name = scanner.next();
OrderCar orderCar = new OrderCar();
Car car = orderCar.getCar(address,name);
System.out.println(car);
}
}
}
在此处先判断由哪个厂家生产汽车:
public class OrderCar {
/**
* 模拟service层
*/
public Car getCar(String address,String name) {
IFactory iFactory = null;
Car car = null;
// 根据品牌不同选择不同的品牌的工厂进行生产
switch (address){
case "BYD":
iFactory = new BYDFactory();
break;
case "Volkswagen":
iFactory = new VolkswagenFactory();
break;
default:
throw new RuntimeException("无此类型的汽车");
}
car = iFactory.createCar(address, name);
car.purchase();
car.assembly();
car.shipment();
car.setName(name);
car.setAddress(address);
System.out.println("您订购的" + car.getName() + "已送达您的车库~~");
return car;
}
}
public interface IFactory {
Car createCar(String address, String name);
}
public class BYDFactory implements IFactory{
@Override
public Car createCar(String address, String name){
Car car = null;
switch (name){
case "ElectricCar":
car = new BYDElectricCar();
break;
case "PetrolCar":
car = new BYDPetrolCar();
break;
case "SolarCar":
car = new BYDSolarCar();
break;
default:
throw new RuntimeException("无此类型的汽车");
}
car.setName(name);
return car;
}
}
public class VolkswagenFactory implements IFactory{
@Override
public Car createCar(String address, String name){
Car car = null;
switch (name){
case "ElectricCar":
car = new VolkswagenElectricCar();
break;
case "PetrolCar":
car = new VolkswagenPetrolCar();
break;
case "SolarCar":
car = new VolkswagenSolarCar();
break;
default:
throw new RuntimeException("无此类型的汽车");
}
car.setName(name);
return car;
}
}
此处要在控制台输入两个参数来模拟前端传入两个参数。
D:\jdk8\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=64664:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\ideaworkspace\design_pattern\design\target\classes;D:\dev_tools\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar com.wd.factory.methodfactory.CarShop
BYD
ElectricCar
比亚迪ElectricCar的配件采购
ElectricCar 配件组装
ElectricCar 出库运输
您订购的ElectricCar已送达您的车库~~
Car(name=ElectricCar, address=BYD)
Volkswagen
SolarCar
上海大众SolarCar的配件采购
SolarCar 配件组装
SolarCar 出库运输
您订购的SolarCar已送达您的车库~~
Car(name=SolarCar, address=Volkswagen)
- 抽象工厂模式
假设我们在上面工厂方法的基础上,又多了一个需求,就是要在生产不同地址厂家汽车的基础上,还要能生产汽车的电池,这就用到了抽象工厂模式。抽象工厂模式其实是围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂,主要用于解决在一个产品族里面,定义多个产品,这此案例中就是在一个工厂里聚合多个同类产品。先看一下类图:
除了生产汽车之外还要生产汽车的电池,我们全部交给IFactory来生产,具体的实现还是由其两个子类工厂来做。下面是代码实现:
新增了电池类
@Data
public abstract class Battery {
/**
* 电池的名称,SunBattery为太阳能电池,NormalBattery为普通电池
*/
private String name;
/**
* 车的地址品牌,BYD为比亚迪,Volkswagen为上海大众
*/
private String address;
/**
* 各种类型的车采购原料流程不一样,定义为抽象方法
*/
abstract public void purchase();
public void assembly(){
System.out.println(name + " 配件组装");
}
public void shipment(){
System.out.println(name + " 出库运输");
}
}
public class BYDNormalBattery extends Battery{
@Override
public void purchase() {
System.out.println("比亚迪NormalBattery的配件采购");
}
}
public class BYDSunBattery extends Battery{
@Override
public void purchase() {
System.out.println("比亚迪SunBattery的配件采购");
}
}
public class VolkswagenNormalBattery extends Battery{
@Override
public void purchase() {
System.out.println("上海大众NormalBattery的配件采购");
}
}
public class VolkswagenSunBattery extends Battery{
@Override
public void purchase() {
System.out.println("上海大众SunBattery的配件采购");
}
}
public class CarShop {
/**
* 模拟客户端调用controller层
*/
public static void main(String[] args) {
while (true){
Scanner scanner = new Scanner(System.in);
String address = scanner.next();
String name = scanner.next();
String bName = scanner.next();
OrderCar orderCar = new OrderCar();
Car car = orderCar.getCar(address,name);
System.out.println(car);
Battery battery = orderCar.getBattery(address, bName);
System.out.println(battery);
}
}
}
在此处先判断由哪个厂家生产汽车:
public class OrderCar {
/**
* 模拟service层
*/
public Car getCar(String address, String name) {
IFactory iFactory = null;
Car car = null;
// 根据品牌不同选择不同的品牌的工厂进行生产
switch (address){
case "BYD":
iFactory = new BYDFactory();
break;
case "Volkswagen":
iFactory = new VolkswagenFactory();
break;
default:
throw new RuntimeException("无此类型的汽车");
}
car = iFactory.createCar(address, name);
car.purchase();
car.assembly();
car.shipment();
car.setName(name);
car.setAddress(address);
System.out.println("您订购的" + car.getName() + "已送达您的车库~~");
return car;
}
public Battery getBattery(String address, String bName) {
IFactory iFactory = null;
Battery battery = null;
// 根据品牌不同选择不同的品牌的工厂进行生产
switch (address){
case "BYD":
iFactory = new BYDFactory();
break;
case "Volkswagen":
iFactory = new VolkswagenFactory();
break;
default:
throw new RuntimeException("无此类型的电池");
}
battery = iFactory.createBattery(address, bName);
battery.purchase();
battery.assembly();
battery.shipment();
battery.setName(bName);
battery.setAddress(address);
System.out.println("您订购的" + battery.getName() + "已送达您的仓库~~");
return battery;
}
}
public interface IFactory {
Car createCar(String address, String name);
Battery createBattery(String address, String bName);
}
生产的商品由原来的只生产汽车变成生产汽车和电池:
public class BYDFactory implements IFactory {
@Override
public Car createCar(String address, String name){
Car car = null;
switch (name){
case "ElectricCar":
car = new BYDElectricCar();
break;
case "PetrolCar":
car = new BYDPetrolCar();
break;
case "SolarCar":
car = new BYDSolarCar();
break;
default:
throw new RuntimeException("无此类型的汽车");
}
car.setName(name);
return car;
}
@Override
public Battery createBattery(String address, String bName) {
Battery battery = null;
switch (bName){
case "NormalBattery":
battery = new BYDNormalBattery();
break;
case "SunBattery":
battery = new BYDSunBattery();
break;
default:
throw new RuntimeException("无此类型的电池");
}
battery.setName(bName);
return battery;
}
}
生产的商品由原来的只生产汽车变成生产汽车和电池:
public class VolkswagenFactory implements IFactory {
@Override
public Car createCar(String address, String name){
Car car = null;
switch (name){
case "ElectricCar":
car = new VolkswagenElectricCar();
break;
case "PetrolCar":
car = new VolkswagenPetrolCar();
break;
case "SolarCar":
car = new VolkswagenSolarCar();
break;
default:
throw new RuntimeException("无此类型的汽车");
}
car.setName(name);
return car;
}
@Override
public Battery createBattery(String address, String bName) {
Battery battery = null;
switch (bName){
case "NormalBattery":
battery = new VolkswagenNormalBattery();
break;
case "SunBattery":
battery = new VolkswagenSunBattery();
break;
default:
throw new RuntimeException("无此类型的电池");
}
battery.setName(bName);
return battery;
}
}
此处要在控制台输入三个参数来模拟前端传入三个参数。
D:\jdk8\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=64761:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\ideaworkspace\design_pattern\design\target\classes;D:\dev_tools\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar com.wd.factory.abstractfactory.CarShop
BYD
ElectricCar
NormalBattery
比亚迪ElectricCar的配件采购
ElectricCar 配件组装
ElectricCar 出库运输
您订购的ElectricCar已送达您的车库~~
Car(name=ElectricCar, address=BYD)
比亚迪NormalBattery的配件采购
NormalBattery 配件组装
NormalBattery 出库运输
您订购的NormalBattery已送达您的仓库~~
Battery(name=NormalBattery, address=BYD)
Volkswagen
SolarCar
SunBattery
上海大众SolarCar的配件采购
SolarCar 配件组装
SolarCar 出库运输
您订购的SolarCar已送达您的车库~~
Car(name=SolarCar, address=Volkswagen)
上海大众SunBattery的配件采购
SunBattery 配件组装
SunBattery 出库运输
您订购的SunBattery已送达您的仓库~~
Battery(name=SunBattery, address=Volkswagen)
- 总结
工厂模式的意义就是将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦,从而提高项目的扩展和维护性。如果是业务较为简单时可以用简单工厂模式,如果业务稍复杂并且只涉及到单层条件的产品生产时可以用工厂方法模式;如果业务很复杂,涉及到多种产品的生产时可以使用抽象工厂模式。