1、介绍
工厂模式又叫虚拟构造函数模式或者多态性工厂模式。可分为简单工厂、工厂方法和抽象工厂模式。
- 简单工厂: 一个工厂类处于对产品类实例化的中心位置上,它知道每一个产品,决定哪一个产品应当实例化。优点是允许客户端相对产品创建的过程,并在系统引入新产品时无需修改客户端,但需要修改工厂类,在某种程度上支持开闭原则,但支持力度不够。
- 工厂方法:是简单工厂模式的进一步抽象和推广,在该模式中,核心工厂类不在负责所有产品的创建,而是将具体的创建工作交给子类去做。这个核心类则成为一个抽象工厂角色,只需给出具体工厂子类必须实现的接口,无需关注哪个产品类实例化的细节。这种可以允许系统在不修改具体工厂角色的情况下引进新产品。
- 抽象工厂:是所有形态的工厂模式中最为抽象和最具有一般性的形态。抽象工厂模式与工厂模式的最大区别在与,工厂方法模式针对的是一个产品等级结构;而抽象工厂则需要处理多个产品等级结构。
2、工厂模式
Define an interface for creating an object,but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
意思是: 定义一个用于创建对象的接口,在子类决定实例化那个类。工厂方法使一个类的实例化延迟到其子类。
使用场景:
- 工厂模式是new一个对象的替代品,因此在所有需要生产对象处都可以使用,但需慎重考虑是否增加一个工厂类进行管理,增加代码的复杂度。
- 需要灵活的,可扩展的框架
3、UML类图
角色介绍:
- Factory : 是工厂模式的核心,与应用系统无关,具有的工厂类必须实现这个接口。
- ConcreteFactory:实现接口,与应用层密切交互,收到应用程序的调用以及创建产品对象。
- Product:负责定义产品的共性,实现对产品最抽象的定义。
- ConcreteProduct:实现抽象产品角色所声明的接口,工厂模式所创建的每一个对象都是某个具有产品的实例。
抽象产品类
public abstract class Product {
/**
* 产品类的抽象方法,由具体的产品类去实现
*/
public abstract void method();
}
具体产品类A
public class ConcreteProductA extends Product{
@Override
public void method() {
System.out.println("我是具体产品A");
}
}
具体产品类B
public class ConcreteProductB extends Product{
@Override
public void method() {
System.out.println("我是具体产品B");
}
}
抽象工厂类
public abstract class Factory {
/**
* @return 具体的产品对象
*/
public abstract Product createProduct();
}
具体工厂类
public class ConcreteFactory extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
客户类
public class Client {
public static void main(String[] args) {
Factory factory = new ConcreteFactory();
Product product = factory.createProduct();
product.method();
}
}
上述的代码运行结果得到的产品对象时ConcreteProductA的实例,若是想得到ConcreteProductB的实例,需更改ConcreteFactory中的代码:
public class ConcreteFactory extends Factory {
@Override
public Product createProduct() {
// return new ConcreteProductA();
return new ConcreteProductB();
}
}
若是还有ConcreteProductC产品,创建该产品的实例,则又要修改代码,显然方式有缺陷。这里可以利用反射的方式来生产具体的产品对象。在抽象工厂类中传入一个Class类来决定是哪一个产品类:
public abstract class Factory {
/**
* @param clz 产品对象类类型
* @return 具体的产品对象
*/
public abstract <T extends Product> T createProduct(Class<T> clz);
}
对于具体的工厂类,则可以通过反射创建类实例。
public class ConcreteFactory extends Factory {
@Override
public <T extends Product> T createProduct(Class<T> clz) {
Product product = null;
try {
product = (Product) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) product;
}
}
在Client中需要创建哪个产品实例,就传入哪个类的类型即可,这个方法简洁,动态。
public class Client {
public static void main(String[] args) {
Factory factory = new ConcreteFactory();
Product pA = factory.createProduct(ConcreteProductA.class);
pA.method();
Product pB= factory.createProduct(ConcreteProductB.class);
pB.method();
}
}
4、示例
以车厂生产汽车为例,比如:需要生产模块汽车,车型有Q1, Q2。首先构造抽象工厂类
public abstract class CarFactory {
/**
* 某车型的工厂方法
* @param clz 具体的车型号
* @return 具体的车型号的车对象
*/
public abstract <T extends Car> T createCar(Class<T> clz);
}
所生产的两种车型在主结构上并没有什么差异,所以只需要一种生产线即可:
public class ConcreteCarFactory extends CarFactory {
@Override
public <T extends Car> T createCar(Class<T> clz) {
Car car = null;
try {
car = (Car) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) car;
}
}
对于两种车型,都有行驶和自动驾驶的功能,抽象出汽车的接口如下:
public abstract class Car {
/**
* 定义车可以有启动行驶功能
*/
public abstract void drive();
/**
* 定义车可以有自动导航功能
*/
public abstract void selfNavigation();
}
接下来就是生产Q1和Q2两种类型的车。
public class CarQ1 extends Car {
@Override
public void drive() {
System.out.println("Q1启动了!");
}
@Override
public void selfNavigation() {
System.out.println("Q1开始自动导航了");
}
}
public class CarQ2 extends Car {
@Override
public void drive() {
System.out.println("Q2启动了!");
}
@Override
public void selfNavigation() {
System.out.println("Q2开始自动导航了");
}
}
最后我们将各个类组装起来形成一条完整的流水线:
public class Client {
public static void main(String[] args) {
CarFactory factory = new ConcreteCarFactory();
CarQ1 carQ1 = factory.createCar(CarQ1.class);
carQ1.drive();
carQ1.selfNavigation();
CarQ2 carQ2 = factory.createCar(CarQ2.class);
carQ2.drive();
carQ2.selfNavigation();
}
}
输出结果如下:
Q1启动了!
Q1开始自动导航了
Q2启动了!
Q2开始自动导航了
5、总结
优点:
- 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,若一个调用者需要一个具体的产品对象,只要知道这个产品的类名或约束字符串即可,无需关注创建对象的过程,降低了模块之间的耦合度。
- 优秀的可扩展性,在增加产品类时,只要在调用是输入产品的类名或扩展一个具体的工厂类。
- 解耦框架,高层模块只需要知道产品的抽象类,其他的实现类都不用关心。工厂模式符合迪米特法则 和依赖倒置原则,只依赖产品类的抽象;另外还符合里氏替换原则,可以使用产品子类替换产品父类。
缺点:为工厂模式添加新的产品就得编写一个新的产品类。