一、工厂模式简介
在工厂模式中,创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式分为“三种”(准确点说是两种,因为简单工厂模式是一种编程习惯)
1、简单工厂模式(Simple Factory)
描述:又叫静态工厂,是将对象的创建包装进一个类
优点:实现简单,使用广泛,修改时只需要修改这个类
缺点:不能继承和改变这个方法的创建行为
2、工厂方法模式(Method Factory)
描述:定义了一个创建对象的接口,但是由子类决定要实例化的是哪一个,将类的实例化推迟到子类
优点:1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
3、抽象工厂模式(Abstract Factory)
描述:提供一个接口,用于创建相关或依赖对象家族,而不需要明确指定具体类
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体实现的里面加代码。
二、工厂模式中包含的设计原则
1、依赖倒置原则(Dependency Inversion Principle)
- 高层模块不依赖低层模块,高层和低层模块应该依赖于抽象(“高层”组件是由“低层“组件定义其行为)
- 接口或抽象类不依赖于实现类(如:实现类变量)
- 实现类依赖于抽象类或接口
总的来说是:面向接口编程,不针对实现编程,要依赖抽象,不依赖具体类。
2、开闭原则(Open-Closed Principle)
- 对扩展开放,对修改关闭
三、工厂模式实现
以下以汽车工厂为例,通过代码来诠释工厂模式的神奇。
1、简单工厂/静态工厂(GitHub源码)
假如现在有一个汽车工厂(CarFactory),需要生产两种不同品牌的车型,BenzCar和BMWCar,该怎样实现?
思路:
- 创建汽车对象,将汽车有相似的属性封装进Car,BenzCar和BMWCar继承Car。
- 创建生产方法,根据参数来判断生产哪一种车型。
创建汽车类,代码如下:
public interface Car {
/**
* 汽车品牌
*
* @return
*/
String getBrand();
}
public class BenzCar implements Car {
@Override
public String getBrand() {
return "I'm Benz.";
}
}
public class BMWCar implements Car {
@Override
public String getBrand() {
return "I'm BMW.";
}
}
接下来是CarFactory代码:
public class CarFactory {
public enum Kinds {
Benz, BMW
}
public static Car getCar(Kinds kinds) {
switch (kinds) {
case BMW:
return new BMWCar();
case Benz:
return new BenzCar();
}
return null;
}
}
CarFactory中的方法理论上可以在调用处直接根据需求,直接生成,这里需要多创建一个CarFactory类将生成代码移到这个类中,这样的好处是当以后需要修改时,只修改这一个类,但这样的方式不能通过继承的方式来改变创建方法的行为。
2、工厂方法(Github源码)
上一节讲了生成BenzCar和BMWCar两种车型,现在新的需求来了,该怎么修改呢?新需求如下:
新增两种车型生产,SUV(越野车)和 Sedan(轿车),且增加生产流程,那么现在BMW和Benz品牌个需要生产两种车型,分别为BMWSUVCar,BMWSedan和BenzSUVCar,BenzSedanCar
在没有学习设计模式之前,我们也能实现,代码如下:
public static Car getCar(Kinds kinds, Category category) {
Car car = null;
if (Kinds.Benz.equals(kinds)) {
if (Category.Sedan.equals(category)) {
car = new BenzSedan();
} else if (Category.SUV.equals(category)) {
car = new BenzSUV();
}
} else if (Kinds.BMW.equals(kinds)) {
if (Category.Sedan.equals(category)) {
car = new BMWSedan();
} else if (Category.SUV.equals(category)) {
car = new BMWSUV();
}
}
return car;
}
//调用
Car benzSedanCar = getCar(Kinds.Benz,Category.Sedan);
benzSedanCar.run();
Car benzSUVCar = getCar(Kinds.Benz,Category.SUV);
benzSUVCar.run();
Car BMWSedanCar = getCar(Kinds.BMW,Category.Sedan);
BMWSedanCar.run();
Car BMWSUVCar = getCar(Kinds.BMW,Category.SUV);
BMWSUVCar.run();
这样的实现方式从逻辑上来说也没毛病,功能也完全可以实现,但如果新的需求又来了呢,比如说添加新的车型,添加新的品牌,该怎样扩展? 继续if else 的无止境下去? 就算能这样加下去,如果新增了另外一些属性呢? 是不是这个类就更复杂了,结构也会越来越混乱,这不是我们想要的。 先不说扩展性,就光依赖,这个类就够庞大的了。下面来从设计模式中心思想之一”只依赖接口,不依赖具体实现“重新理一下思路:
- 抽象化,针对接口编程
- 针对实现编程,个性化生产实现
抽象化,针对接口编程
抽象化的目的,提供接口依赖。抽象的原则是寻找相似的操作和流程,提取到父类接口(抽象类和接口)。这里可以把Benz和BMW的生产流程作为一个抽象(CarFactory),汽车本身也是一个抽象(Car),父类代码如下:
public abstract class Car {
protected String brand;
void prepare() {
System.out.println("=== Start create a " + brand + " Car ===");
}
void chassis() {
System.out.println("Creating chassis.");
}
void body() {
System.out.println("Creating body.");
}
void engine() {
System.out.println("Creating engine.");
}
void electrical() {
System.out.println("Creating electrical.");
}
void finish() {
System.out.println("=== The birth of a new " + brand + "Car ===");
}
public void run() {
System.out.println("Hello, I'm a new " + brand + " Car.");
System.out.println();
}
}
public abstract class CarFactory {
/**
* 创建汽车
*
* @param item 汽车类型
* @return
*/
protected abstract Car createCar(String item);
public Car newCar(String item) {
Car car = createCar(item);
car.prepare();
car.chassis();
car.body();
car.engine();
car.electrical();
car.finish();
return car;
}
}
其中prepare(),chassis(),body(),engine(),electrical()和finish()这些是汽车的生产流程,唯一不同的是由createCar()提供的汽车类型。
针对实现编程
由于BMW和Benz的生产流程可能存在一些差异,为了避免这些差异,我们将两个工厂分开分别为BenzFactory和BMWFactory且都需要继承标准流程CarFactory。对于汽车也是如此,这里为了方便就没做区分,仅仅是从名称上来区分(在实际开发中可以根据需求再做一次抽象,分别生成BMWCar和BenzCar)。
以下的Benz和BMW的两种车型的代码,并根据Brand型号来区分:
public class BenzSedan extends Car {
public BenzSedan() {
brand = "Benz sedan";
}
}
public class BenzSUV extends Car {
public BenzSUV() {
brand = "Benz SUV";
}
}
public class BMWSedan extends Car {
public BMWSedan() {
brand = "BMW sedan";
}
}
public class BMWSUV extends Car {
public BMWSUV() {
brand = "BMW SUV";
}
}
接下来该是创建工厂的时候了,BMWFactory和Benz工厂:
public class BenzCarFactory extends CarFactory {
public static final String SEDAN = "sedan";
public static final String SUV = "suv";
@Override
protected Car createCar(String item) {
Car car = null;
if (SEDAN.equals(item)) {
car = new BenzSedan();
} else if (SUV.equals(item)) {
car = new BenzSUV();
}
return car;
}
}
public class BMWCarFactory extends CarFactory {
public static final String SEDAN = "sedan";
public static final String SUV = "suv";
@Override
protected Car createCar(String item) {
Car car = null;
if (SEDAN.equals(item)) {
car = new BMWSedan();
} else if (SUV.equals(item)) {
car = new BMWSUV();
}
return car;
}
}
是不是大功告成了? ,别急。接下来我们来看看怎么调用:
public static void main(String[] args) {
//奔驰工厂
CarFactory carFactory = new BenzCarFactory();
//创建奔驰轿车
Car car = carFactory.newCar(BenzCarFactory.SEDAN);
car.run();
//创建奔驰SUV
car = carFactory.newCar(BenzCarFactory.SUV);
car.run();
//宝马工厂
carFactory = new BMWCarFactory();
//创建奔驰轿车
car = carFactory.newCar(BMWCarFactory.SEDAN);
car.run();
//创建奔驰SUV
car = carFactory.newCar(BMWCarFactory.SUV);
car.run();
}
虽然和之前调用生成汽车的代码看起来,半斤八两,但这不是重点,重点是现在的代码结构可扩展性是之前if else方式无法比拟的。
接下来我们尝试之前说的扩展:
1、增加一种Benz的车型SportsCar,我们只需要创建SportsCar和修改BenzFactory即可,不影响BMWFactory的任何东西,调用方式不变。
2、增加一个汽车品牌,Lincoln,只需要创建LincolnFactory和Lincoln相关的Car类即可。
可能有些人会觉得这种方式和if else那种,除了代码结构清晰一点外也看不出什么优势,那是因为没有更深入的去研究,这里只能告诉你,慢慢深入去研究,然后再项目中用起来,你就懂了。
3、抽象工厂模式(GitHub源码)
接下来就继续之前的需求,更深入的去了解工厂模式。新需求:
我们需要为不同的品牌不同型号的汽车自定义不同的配件,比如说:Body(车身),Chassis(底盘),Electrical(电力系统),Engine(引擎)
还想用if else的自己去厕所哭吧,分析思路:汽车品牌和车型我们之前已经处理过了可以继续沿用,现在的问题是新增的配件Parts的处理。同样首先还是抽象化,将配件抽象出来Body,Chassis,Electrical,Engine。然后根据品牌和车型生成不同的配件,这里唯一不同的地方是,配件可能是配套使用的,我们可以再增加一个配件的工厂 PartsFactory,保证一辆车上使用的是匹配的配件。接下来就看到底这个PartsFactory是怎样给汽车提供配件的了。代码如下:
public abstract class Car {
protected String brand;
protected Chassis chassis;
protected Body body;
protected Engine engine;
protected Electrical electrical;
public void prepare() {
System.out.println("=== Start create a " + brand + " Car ===");
}
public void chassis() {
System.out.println("Creating " + chassis.description());
}
public void body() {
System.out.println("Creating " + body.description());
}
public void engine() {
System.out.println("Creating " + engine.description());
}
public void electrical() {
System.out.println("Creating " + electrical.description());
}
public void finish() {
System.out.println("=== The birth of a new " + brand + "Car ===");
}
public void run() {
System.out.println("Hello, I'm a new " + brand + " Car.");
System.out.println();
}
}
public interface PartsFactory {
/**
* 车身
*
* @return
*/
Body body();
/**
* 电子系统
*
* @return
*/
Electrical electrical();
/**
* 底盘
*
* @return
*/
Chassis chassis();
/**
* 引擎
*
* @return
*/
Engine engine();
}
public class BenzPartsFactory implements PartsFactory {
@Override
public Body body() {
return new BenzBody();
}
@Override
public Electrical electrical() {
return new BenzElectrical();
}
@Override
public Chassis chassis() {
return new BenzChassis();
}
@Override
public Engine engine() {
return new BenzEngine();
}
}
public class BMWPartsFactory implements PartsFactory {
@Override
public Body body() {
return new BMWBody();
}
@Override
public Electrical electrical() {
return new BMWElectrical();
}
@Override
public Chassis chassis() {
return new BMWChassis();
}
@Override
public Engine engine() {
return new BMWEngine();
}
}
以上代码中BenzBody等为继承抽象配件的实现类。BenzPartsFactory和BMWPartsFactory保证同一次使用的是一个系列的配件, 接下来我们需要修改CarFactory:
public class BMWCarFactory extends CarFactory {
public static final String SEDAN = "sedan";
public static final String SUV = "suv";
@Override
protected Car createCar(String item) {
PartsFactory partsFactory;
Car car = null;
if (SUV.equals(item)) {
partsFactory = new BMWPartsFactory();
car = new BMWSUV(partsFactory);
} else if (SEDAN.equals(item)) {
//如果需要扩展,可继续创建相关的PartsFactory和Parts
}
return car;
}
}
public class BMWCarFactory extends CarFactory {
public static final String SEDAN = "sedan";
public static final String SUV = "suv";
@Override
protected Car createCar(String item) {
PartsFactory partsFactory;
Car car = null;
if (SUV.equals(item)) {
partsFactory = new BMWPartsFactory();
car = new BMWSUV(partsFactory);
} else if (SEDAN.equals(item)) {
//如果需要扩展,可继续创建相关的PartsFactory和Parts
}
return car;
}
}
这就是抽象工厂,对于调用者来说,调用方式和之前工厂方法调用方法一样,但是内部实现方式却改变了,调用者根本不需要关心接口提供者是怎么实现的。
扩展:结构越复杂,扩展的难度就越大,如果我们需要添加新的汽车型号,需要添加对应的PartsFactory,Car和修改CarFactory
此处源码为部分源码,点击查看完整源码