为了防止被“杀”了祭天,学点设计模式,并总结下还是有必要的。
一:模式理解
- 工厂模式的作用是新建对象。
- 工厂模式的目的是让新建对象的过程更加优雅,减少对业务代码的混淆。
- 包含简单工厂,工厂方法,抽象工厂。
- 以下几点可以在例子之后再看。
单独建立用于生成对象的工程类,即为简单工厂,类似于工具类。在原有代码上,抽象出一个方法,用于生成对象,即为工厂方法。新建一个完全抽象的工厂接口/类,具体生成的对象由该工厂的实现类/子类决定,即为抽象工厂。
二: 例子
你是一个富二代,拥有有一家汽车公司,公司的主要工作是把汽车卖给客户,即sellCar。
主要流程包括原料采购,组装汽车,汽车展示。
为方便模拟,三个步骤分别为:
- 原料采购直接sout原料采购。
- 组装汽车(assembleCar)生成对应的汽车对象。
- 汽车展示调用生成汽车对象的display方法,确保生产正确。
有一个汽车类,包含属性为牌子,颜色,还有一个display方法。
// 汽车类
@Data
public class Car {
private String brand;
private String color;
public void display() {
System.out.println("This is a car");
}
}
目前你司主营奔驰和宝马车,均继承自父类Car,对应的类分别为:
// 宝马车实体类
public class BMWCar extends Car{
@Override
public void display(){
System.out.println("This is a BMW car");
}
}
// 奔驰车实体类
public class BenzCar extends Car {
@Override
public void display(){
System.out.println("This is a Benz car");
}
}
两个类没什么区别,只是分别重写了Car类的display方法。
你司的日常就是卖汽车(sellCar),根据不同的订单,输入不同的carType,新建不同的汽车对象。
public class CarCompany {
public void sellCar(String carType) {
System.out.println("原料采购");
Car car = null;
if (StringUtils.equals(carType, "BMW")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "Benz")) {
car = new BenzCar();
} else {
car = new Car();
}
car.display();
}
public static void main(String[] args) {
CarCompany carCompany = new CarCompany();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String carType = scanner.next();
carCompany.sellCar(carType);
}
}
}
输入/输出:
BMW
原料采购
This is a BMW car
Benz
原料采购
This is a Benz car
car
原料采购
This is a car
可以看出,你司的业务还是挺简单粗暴,只需判断输入生产不同的车即可。
作为富二代的你并不打算对其做出什么改变。
然而,有一天,你发现奥迪车卖的也不错,准备开始在自己公司卖奥迪车。
此时在业务代码sellCar中增加一个else判断条件。
你的公司越做越大,每天都会需要决定加入或者删除某些车是否生产。
某一次,一个程序员在修改if else的过程中,不小心搞出个bug。由于习惯性复制粘贴,在应该生产QQ车的时候,生产了一辆宾利车。
为此,你“杀”了这位程序员祭天。
有没有办法可以在不修改业务代码的情况下,搞定第二步骤,即组装汽车逻辑的修改呢?
1. 简单工厂
将新建对象的逻辑从业务代码中提取出来。
新建一个简单汽车工厂类来承载新建汽车对象的功能。
public class SimpleCarFactory {
public static Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "BMW")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "Benz")) {
car = new BenzCar();
} else {
car = new Car();
}
return car;
}
}
修改之后,汽车工厂的代码变为:
public class CarCompany {
public void sellCar(String carType) {
// 原料采购
System.out.println("原料采购");
// 组装汽车
Car car = SimpleCarFactory.assembleCar(carType);
// 展示汽车
car.display();
}
public static void main(String[] args) {
CarCompany carCompany = new CarCompany();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String carType = scanner.next();
carCompany.sellCar(carType);
}
}
}
输入和输出都与之前一样。
- 严格意义上,简单工厂并不是一种设计模式,更像是一种重构。
- 重构之后,CarCompany的sellCar代码变得非常清晰,特别是引入方法assembleCar后,一看就可以知道该步骤是在组装汽车。
- 对于刚看代码的新手而言,就算不理解assembleCar具体操作,也可以根据该方法名知道该方法的作用。
- 经过程序员的一顿改,你特别高兴,自己家又多了一个工厂,又多了个厂长的身份,还高兴地freestyle了一番。
2. 方法工厂
工厂的目的是生成一个对象,简单工厂为此新建了一个类。
此外,也可以在CarCompany类下增加一个新建汽车对象的私有方法。
该方法的作用和简单工厂一致,不同的是以方法的形式出现,将其称为方法工厂。
public class CarCompany {
public void sellCar(String carType) {
System.out.println("原料采购");
Car car = assembleCar(carType);
car.display();
}
private Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "BMW")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "Benz")) {
car = new BenzCar();
} else {
car = new Car();
}
return car;
}
public static void main(String[] args) {
CarCompany carCompany = new CarCompany();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String carType = scanner.next();
carCompany.sellCar(carType);
}
}
}
当然,如果只是将方法的实现移动一个位置,那也算不上是设计模式。
好不容易多了个厂长的身份,程序员竟然这么改。你顿时不高兴了,回头就准备掏出自己40米的大刀。
此时,程序员急忙开始解释。
如果将CarCompany中的assembleCar方法申明为抽象方法,具体实现由子类来决定。
public abstract class CarCompany {
public void sellCar(String carType) {
System.out.println("原料采购");
Car car = assembleCar(carType);
car.display();
}
protected abstract Car assembleCar(String carType);
}
此时,你完全可以开多家公司用于生产不同的车,它们都将继承自CarCompany。
CarCompany类规定了sellCar方法的步骤,留下组装汽车的方法让不同的公司自己去实现。
新建两个公司,一个卖宝马,一个卖奔驰,分开之后,业务得到了扩展,两个工厂都可以生产轿车和SUV。
public class BMVCarCompany extends CarCompany {
@Override
protected Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
return car;
}
}
public class BenzCarCompany extends CarCompany {
@Override
protected Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BenzCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
return car;
}
}
public class Client {
public static void main(String[] args) {
CarCompany bmwCarCompany = new BMVCarCompany();
CarCompany benzCarCompany = new BenzCarCompany();
bmwCarCompany.sellCar("car");
benzCarCompany.sellCar("car");
bmwCarCompany.sellCar("suv");
benzCarCompany.sellCar("suv");
}
}
输入/输出:
原料采购
This is a BMW car
原料采购
This is a Benz car
原料采购
This is a BMW SUV car
原料采购
This is a BMW SUV car
两个公司可以完全独立运行,在需要新建一个公司的时候,只需要增加新的公司类即可,如AudiCarCompany。
听了程序员这番解释,你虽然没听懂,但觉得可以一下子多搞几个分公司,内心还是有点小激动的,随手收回了自己那把40米的大刀。
3. 抽象工厂
由于成本变动,宝马和奔驰子公司都需要更换组装汽车的过程,第一时间想到是直接修改两个公司的assembleCar的代码。
然而现在宝马公司又需要生产纪念版的宝马车,和普通宝马不同的是,特别版的宝马使用了特殊的红色油漆。
面对这样的需求,虽贵为富二代的你,还是准备讨好下程序员,以一起去彻夜鼓掌作为诱惑,希望程序员尽快完成。
在之前的步骤中,已经将组装汽车的过程抽象成工厂,那么更改/修改汽车组装的过程,可以抽象成换了一家代工厂。
但无论怎么换代工厂,都要求这些工厂有assembleCar的功能,所以需要一个抽象类或者接口来约束。其中的组装方法是抽象的,具体实现在子类中定义。
这就是抽象工厂这个名称的含义。
public abstract class AbstractCarFactory {
public abstract Car assembleCar(String carType);
}
分别新建两家代工厂。
// 宝马车代工厂
public class BMWCarFactory extends AbstractCarFactory {
@Override
public Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
return car;
}
}
// 奔驰车代工厂
public class BenzCarFactory extends AbstractCarFactory {
@Override
public Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BenzCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
return car;
}
}
两家公司分别持有AbstractCarFactory对象,在assembleCar方法中,只需直接返回对应工厂组装的汽车即可,不用关注具体的工艺。
public class BMVCarCompany extends CarCompany {
private AbstractCarFactory carFactory;
public BMVCarCompany(AbstractCarFactory carFactory) {
this.carFactory = carFactory;
}
@Override
protected Car assembleCar(String carType) {
return carFactory.assembleCar(carType);
}
}
public class BenzCarCompany extends CarCompany {
private AbstractCarFactory carFactory;
public BenzCarCompany(AbstractCarFactory carFactory) {
this.carFactory = carFactory;
}
@Override
protected Car assembleCar(String carType) {
return carFactory.assembleCar(carType);
}
}
public class Client {
public static void main(String[] args) {
AbstractCarFactory bmwCarFactory = new BMWCarFactory();
CarCompany bmwCarCompany = new BMVCarCompany(bmwCarFactory);
AbstractCarFactory benzCarFactory = new BenzCarFactory();
CarCompany benzCarCompany = new BenzCarCompany(benzCarFactory);
bmwCarCompany.sellCar("car");
benzCarCompany.sellCar("car");
bmwCarCompany.sellCar("suv");
benzCarCompany.sellCar("suv");
}
}
当某个工厂需要更换代工厂时候,只需新建一个继承自AbstractCarFactory抽象工厂的具体工厂即可,如AudiCarFactory。
并将此工厂作为carCompany的属性置入,如果只希望增加代码而不是修改代码,可以新建AudiCarCompany即可。
以下例子为宝马公司更换特别版生产工厂之后,生产宝马车的过程,在display中同时展示宝马车的颜色。
可以看到,在更换特别版工厂之后,生产出来的宝马车加上了特别的红色。
而作为富二代的你,开上这辆特别版的宝马,突然发现一下子多了好多一夜成长的机会。
此时,你已经忘了答应给程序员的鼓掌奖励。/(ㄒoㄒ)/~~
public class BMWCarFactoryForSpecialEdition extends AbstractCarFactory {
@Override
public Car assembleCar(String carType) {
Car car = null;
if (StringUtils.equals(carType, "car")) {
car = new BMWCar();
} else if (StringUtils.equals(carType, "suv")) {
car = new BMWSUVCar();
} else {
car = new Car();
}
car.setColor("Red for special edition");
return car;
}
}
public class BMWCar extends Car {
@Override
public void display() {
System.out.println("This is a BMW car with " + getColor());
}
}
输入/输出:
原料采购
This is a BMW car with Red for special edition
三: 再理解
- 简单工厂只是一个代码重构的过程,为组装汽车的过程新建了一个类,一个静态方法。
- 方法工厂可以分为简单方法工厂和抽象方法工厂。简单方法工厂和简单方法差不多,只是少新建了一个工厂类。抽象方法工厂,将生成对象的方法申明为抽象,在子类中实现,方便建立新的公司,业务拆分。
- 抽象工厂,定义一个什么都不做,只约定做什么的工厂,即将工厂的作用抽象出来。抽象工厂和策略模式很类似,只是抽象工厂的目的是生成对象,而策略模式的目的主要在于为不同类别的对象执行不同的步骤。