工厂模式 Factory Pattern、抽象工厂模式 Abstract Factory Pattern
建议使用JDK版本:JDK 1.9+
创建型模式:关注对象的创建过程,它描述如何将对象的创建、使用分离,让用户无需关心对象的创建细节,从而降低系统的耦合度,让设计方案易于修改、扩展。
一、简单工厂模式
简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
这种模式出现在很多框架中,比如 MyBatis 的 SqlSessionFactory 类,此外Spring 中也大量应用工厂模式。
1、结构
- Factory(工厂角色):
工厂类,负责实现创建所有产品实例的内部逻辑,可以被外界直接调用以创建所需对象产品。 - Product(抽象产品角色):
工厂类创建对象的父类,封装了各种产品对象的公用方法,使得创建所有的具体产品都是其子类对象,只需定义一个工厂。 - ConcreteProduct(具体产品角色):
简单工厂模式的创建目标,继承自抽象产品角色,需声明抽象产品中的抽象方法。
2、举例
样品代码:
Car抽象类(抽象产品类,定义产品)
public abstract class Car {
//为更好的演示,我们不定义属性
//定义方法run()
public abstract void run();
}
SUV类(具体产品类,继承自Car类)
public class SUV extends Car{
//重写父类方法
@Override
public void run() {
System.out.println("SUV跑起来");
}
}
Hatchback类(具体产品类,继承自Car类)
public class Hatchback extends Car{
//重写父类方法
@Override
public void run() {
System.out.println("小轿车跑起来");
}
}
CarFactory类(工厂类):
public class CarFactory {
public static Car createCar(String carType) {
//定义引用
Car car = null;
try {
//反射获取Class对象(包名 + 类名)
Class clazz = Class.forName("com.simple." + carType);
//获取实例(JDK 1.9推荐使用,可以根据参数不同,调用不同的构造方法)
car = (Car) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
//不存在这个类
System.out.println("没有这款车!");
} catch (Exception e) {
e.printStackTrace();
}
//返回引用指向的实例
return car;
}
}
测试代码:
//用户无需关心内部实现,只需知道类型名称即可
//SUV
Car car1 = CarFactory.createCar("SUV");
car1.run();
//Hatchback
Car car2 = CarFactory.createCar("Hatchback");
car2.run();
运行结果:
3、优点
- 客户无需决定何时创建对象,仅仅使用产品,实现了创建、使用的分离。
- 客户无需知道所创建的具体对象类名,只需要知道具体产品类所对应的参数即可。
- 通过引入配置文件,可一定程度上提高系统的灵活性。
4、缺点
- 工厂类集中了所有创建逻辑,职责过重。
- 系统扩展困难,一旦添加新的产品,则必须修改工厂逻辑,不易于维护。
- 简单工厂模式使用了静态工厂方法,无法形成基于继承的等级结构。
二、工厂方法模式
工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
1、结构
- Product(抽象产品):
定义产品的接口,工厂方法模式创建对象的超类型,也就是产品对象的公共父类。 - ConcreteProduct(具体产品):
实现了接口产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。 - Factory(抽象工厂):
在抽象工厂类中声明了工厂方法(Factory Method),用于返回一个产品。所有创建对象的工厂类必须实现此接口。 - ConcreteFactory(具体工厂):
抽象工厂类和子类,实现了在抽象工厂中声明的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
2、举例
样品代码:
Car接口
public interface Car {
//Car接口,定义方法
void run();
}
SUV类
public class SUV implements Car{
//重写父类方法
@Override
public void run() {
System.out.println("SUV跑起来");
}
}
Hatchback类
public class Hatchback implements Car {
//重写父类方法
@Override
public void run() {
System.out.println("小轿车跑起来");
}
}
CarFactory接口
public interface CarFactory {
//定义创造汽车方法
Car createCar();
}
SUVFactory类
public class SUVFactory implements CarFactory {
//方法的具体实现
@Override
public Car createCar() {
//定义引用
SUV suv = null;
try {
//反射获取Class对象(包名 + 类名)
Class clazz = Class.forName("com.simple.SUV");
//获取实例(JDK 1.9推荐使用,可以根据参数不同,调用不同的构造方法)
suv = (SUV) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
//不存在这个类
System.out.println("没有这款SUV!");
} catch (Exception e) {
e.printStackTrace();
}
//返回引用指向的实例
return suv;
}
}
HatchbackFactory类
public class HatchbackFactory implements CarFactory{
//方法的具体实现
@Override
public Car createCar() {
//定义引用
Hatchback hatchback = null;
try {
//反射获取Class对象(包名 + 类名)
Class clazz = Class.forName("com.simple.Hatchback");
//获取实例(JDK 1.9推荐使用,可以根据参数不同,调用不同的构造方法)
hatchback = (Hatchback) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
//不存在这个类
System.out.println("没有这款小轿车!");
} catch (Exception e) {
e.printStackTrace();
}
//返回引用指向的实例
return hatchback;
}
}
测试代码:
//定义工厂引用
CarFactory factory = null;
//指向SUV工厂
factory = new SUVFactory();
factory.createCar().run();
//指向Hatchback工厂
factory = new HatchbackFactory();
factory.createCar().run();
运行结果
3、优点
- 工厂方法模式中,工厂方法用以创建客户所需要的产品,同时还向客户隐藏了具体产品实例化这一细节,用户只需知道对应工厂。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键,它能够让工厂自主确定创建对象类型,其完全被封闭在工厂内部,故该模式又被称为 多态工厂模式。
- 引入新产品无需修改抽象工厂,无需修改其他继承产品,只需添加一个具体工厂和具体产品即可,扩展性非常好,符合开闭原则。
4、缺点
- 添加新产品具体类,必须提供对应的工厂,添加了系统的复杂度。
三、抽象工厂模式(工具模式)
抽象工厂模式(Abstract Factory Pattern) / 工具模式(Kit Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
1、结构
- AbstractFactory(抽象工厂):
声明了一组用于创建一族产品的方法,每一个方法对应一种产品。 - ConcreteFactory(具体工厂):
实现抽象工厂的方法,可生成具体产品,这些产品故成了一个产品族。 - AbstractProduct(具体产品):
为每种产品声明接口,在抽象产品中声明了产品所有的业务方法。 - ConcreteProduct(具体产品):
定义具体工厂生产的具体产品对象,实现抽象产品中定义的业务方法。
2、举例
样品代码:
Car接口:
public interface Car {
//定义同一方法
void run();
}
SUV接口:
public interface SUV extends Car {
//定义 SUV 特殊方法
void load();
}
Hatchback接口:
//定义 Hatchback 特殊方法
void hatch();
BrandASUV类:
public class BrandASUV implements SUV {
@Override
public void run() {
System.out.println("A品牌SUV跑起来!");
}
@Override
public void load() {
System.out.println("A品牌SUV容载货物!");
}
}
BrandAHatchback类:
public class BrandAHatchback implements Hatchback {
@Override
public void run() {
System.out.println("A品牌小轿车跑起来!");
}
@Override
public void hatch() {
System.out.println("A品牌小轿车打开后备箱!");
}
}
BrandBSUV类:
public class BrandBSUV implements SUV{
@Override
public void run() {
System.out.println("B品牌SUV跑起来!");
}
@Override
public void load() {
System.out.println("B品牌SUV容载货物!");
}
}
BrandBHatchback类:
public class BrandBHatchback implements Hatchback{
@Override
public void run() {
System.out.println("B品牌小轿车跑起来!");
}
@Override
public void hatch() {
System.out.println("B品牌小轿车打开后备箱!");
}
}
CarFactory接口:
public interface CarFactory {
SUV createSUV();
Hatchback createHatchback();
//定义同一造车模板(减少代码冗余),如子类有需要,可随时重写该方法
default Car createCar(String carType) {
//方法的具体实现
//定义引用
Car car = null;
try {
//反射获取Class对象(包名 + 类名)
Class clazz = Class.forName("com.simple." + carType);
//获取实例(JDK 1.9推荐使用,可以根据参数不同,调用不同的构造方法)
car = (Car) clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
//不存在这个类
System.out.println("没有这款车型!");
} catch (Exception e) {
e.printStackTrace();
}
//返回引用指向的实例
return car;
}
}
BrandAFactory类:
public class BrandAFactory implements CarFactory{
@Override
public SUV createSUV() {
return (BrandASUV) createCar("BrandASUV");
}
@Override
public Hatchback createHatchback() {
return (BrandAHatchback) createCar("BrandAHatchback");
}
}
BrandBFactory类:
public class BrandBFactory implements CarFactory {
@Override
public SUV createSUV() {
return (BrandBSUV) createCar("BrandBSUV");
}
@Override
public Hatchback createHatchback() {
return (BrandBHatchback) createCar("BrandBHatchback");
}
}
测试代码:
CarFactory factory = null;
factory = new BrandAFactory();
Hatchback hatchbackA = factory.createHatchback();
hatchbackA.run();
hatchbackA.hatch();
SUV suvA = factory.createSUV();
suvA.run();
suvA.load();
System.out.println("-------------------");
factory = new BrandBFactory();
Hatchback hatchbackB = factory.createHatchback();
hatchbackB.run();
hatchbackB.hatch();
SUV suvB = factory.createSUV();
suvB.run();
suvB.load();
测试结果:
3、优点
- 隔离了具体类的生成,使得客户并不需要知道什么被创建。这种隔离使得更换一个工厂相对容易,所有具体工厂都实现了抽象工厂的接口。
- 当一个产品族的多个对象一起工作时,能够保证客户端始终只使用同一个产品族中的产品。
4、缺点 — 开闭原则倾斜性
若在第2小节举例中,添加一种新的车型,首先要修改抽象工厂接口CarFactory,其次要逐个修改具体工厂类,实现相应方法创建新的车型。
抽象工厂无法解决此问题,这是它最大的缺点。在抽象工厂中,添加产品族(例中表现为品牌)很简单,但是增加 新的产品等级结构(例中表现为新的车型)很麻烦,这种性质称为: 开闭原则倾斜性 :
- 增加产品族 :抽象工厂很好的支持了开闭原则,只需增加具体产品并对应增加一个新的具体工厂,对已有代码无需做任何修改。
- 增加新的产品等级结构 : 增加新的产品等级结构,需要修改所有工厂类(抽象工厂类 + 具体工厂类)增加生产新产品的方法,违背了开闭原则。