在前面我们学习了简单工厂模式,简单工厂模式适用于产品对象比较固定的使用场景。简单工厂模式工厂模式存在两个比较大的问题,一个是新产品的加入必须修改工厂类,违反了开闭原则;另一个是所有产品对象都与工厂类耦合,无法对工厂类进行抽象,不利于系统扩展。
工厂方法模式就可以很好的解决这两个问题,不再使用单一的工厂类创建所有产品,而是针对不同产品使用不同工厂创建,提供一个与产品等级结构相对应的工厂等级结构。
1. 定义
工厂方法模式定义一个用于创建对象的工厂接口,让子类决定创建哪种类型的产品对象,使得具体产品对象的创建延迟到抽象工厂的子类中进行。工厂方法模式又称工厂模式,是一种创建型模式。
在工厂方法模式中,存在4个角色:
- 抽象产品角色:它定义了产品接口,是工厂生产具体产品的父类,可以是接口或抽象类。
- 具体产品角色:它实现了抽象产品接口,是工厂类生产的具体产品对象。
- 抽象工厂角色:抽象工厂接口声明了工厂方法,用于返回产品,是工厂方法模式的核心,所有的具体工厂都要实现抽象工厂接口。
- 具体工厂角色:它是抽象工厂类的子类,实现了抽象工厂生命的工厂方法,返回一个具体的产品实例对象,和具体产品角色一一对应。
可以看出来,工厂方法模式和简单工厂模式唯一的区别就是多了抽象工厂角色,它的出现就很好的解决了简单工厂模式的两个缺点。
2. 代码实现
现在通过这样一个需求,有一个电子工厂,可以生产不同的电子产品,分别是电脑和手机。
- 抽象产品角色
// 1.抽象的产品角色
public abstract class Product {
// 业务方法 - 开机
protected abstract void powerOn();
}
- 具体产品角色
// 2.具体的产品角色 - 电脑
class Computer extends Product {
@Override
protected void powerOn() {
System.out.println("电脑开机!");
}
}
// 2.具体的产品角色 - 手机
class Phone extends Product {
@Override
protected void powerOn() {
System.out.println("手机开机!");
}
}
- 抽象工厂角色
// 3.抽象的工厂角色
public interface Factory {
Product createProduct();
}
- 具体工厂角色
// 4.具体的工厂角色 - 电脑工厂
class ComputerFactory implements Factory {
@Override
public Product createProduct() {
return new Computer();
}
}
// 4.具体的工厂角色 - 电脑工厂
class PhoneFactory implements Factory {
@Override
public Product createProduct() {
return new Phone();
}
}
- 客户端
public class Client {
public static void main(String[] args) {
// 电脑产品
Factory computerFactory = new ComputerFactory();
Product computer = computerFactory.createProduct();
computer.powerOn();
// 手机产品
PhoneFactory phoneFactory = new PhoneFactory();
Product phone = phoneFactory.createProduct();
phone.powerOn();
}
}
3. UML类图
这里忽略客户端client:
4. 总结
4.1 优点
- 和其他工厂模式一样,将对象的创建和使用过程分离,做进一步的解耦,用户只需关注所使用的工厂即可;
- 工厂和产品都有同层级抽象设计,这也是工厂方法模式被称为多态工厂的原因,所以扩展性更强;
- 在新加入具体产品时,只需增加对应的具体工厂和具体产品角色即可,无需修改源代码,符合开闭原则;
4.2 缺点
- 具体产品角色都要有与之对应的具体工厂,增加了类的个数,增加了系统复杂性,造成系统额外的运行开销;
- 工厂和产品均使用抽象层开发,无形中增加了代码理解的难度。
4.3 适用场景
- 客户端不知道要具体要创建的产品对象,而只关注产品的工厂;
- 对一些无法获取到源代码的场景下,工厂或者产品的接口只有字节码;
- 对象的创建过程比较复杂,可能涉及到一些io操作或者数据库连接等;
5. 问题
- 工厂方法模式中的工厂方法可否是静态方法?
不可以,因为静态方法是属于类的,只能通过类名.方法名来创建对象,这样抽象工厂角色就没有存在的意义,无法在工厂的维度上做抽象的扩展。
- 抽象工厂和具体工厂没有存在的意义,开发的时候直接面向抽象产品角色编程也可以
其实工厂存在的意义是使得工厂创建和使用过程分离,做进一步的解耦,这是创建型设计模式的核心所在,工厂用来做生产,客户端去调用做业务逻辑;
另外还可以考虑一种场景,如果产品对象的创建过程比较复杂,可能涉及到一些jdbc连接、io操作等,举个例子,有个开发场景要记录业务日志,有产品角色需要写入到数据库,有产品角色需要记录到本地磁盘文件中。对于这样的场景,我们可以把比较复杂的jdbc连接,io操作的初始化写入到工厂类中执行,客户端就不用关心这些复杂的操作,只需要使用创建好的对象就可以。