上文我们简单介绍了简单工厂模式,本文我们将会聊一聊工厂方法模式(Factory Method Pattern)。
在介绍工厂方法模式之前,我们先来看一下简单工厂模式的样例代码。
//具体类的抽象接口
public interface Shape {
/**
* 绘制图形
*/
void draw();
/**
* 擦除图形
*/
void erase();
}
//具体类Circle
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("绘制圆形");
}
@Override
public void erase() {
System.out.println("擦除圆形");
}
}
//具体类Triangle
public class Triangle implements Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("绘制三角形");
}
@Override
public void erase() {
// TODO Auto-generated method stub
System.out.println("擦除三角形");
}
}
//工厂类ShapeFactory
public class ShapeFactory {
public static Shape getShape(String type) {
if(type.equals("triangle")) {
return new Triangle();
}else if(type.equals("circle")) {
return new Circle(10);
} else {
System.out.println("不支持的类型");
return null;
}
}
}
简单工厂模式虽然简单,但是它存在一个非常严重的问题,即当系统引入新的具体实现类时,由于静态工厂方法通过所传入参数的不同来创建不同的实例,那么我们就不得不去修改工厂类的源代码,这违背了“开闭原则”,那么如何实现在增加具体类的同时不影响原来的代码呢?工厂方法模式应运而生。
简单工厂模式只提供了一个工厂类,该工厂类处于对具体类进行实例化的核心位置,他需要知道所有具体类的实例化细节,并决定何时实例化哪一个具体类。新的具体类的添加,务必需要修改工厂类,增加新的具体类的创建逻辑,违背“开闭原则”;简单工厂模式只有一个工厂类,工厂类的职责过重,同时所有具体类的实例化都在一个工厂类中实现,工厂类与具体类的耦合严重,严重影响了系统的灵活性与拓展性,而工厂方法模式则可以很好的解决这一问题。
在我们详述工厂方法模式的概念之前,我们先对以上的简单工厂模式的例子进行一个改造。
1、对于具体类及具体类的接口,我们不做修改,保持原来的代码不动
//具体类的抽象接口
public interface Shape {
/**
* 绘制图形
*/
void draw();
/**
* 擦除图形
*/
void erase();
}
//具体类Circle
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("绘制圆形");
}
@Override
public void erase() {
System.out.println("擦除圆形");
}
}
//具体类Triangle
public class Triangle implements Shape {
@Override
public void draw() {
System.out.println("绘制三角形");
}
@Override
public void erase() {
System.out.println("擦除三角形");
}
}
2、增加一个工厂接口ShapeFactory,接口中声明一个getShape的方法
public interface ShapeFactory {
Shape getShape();
}
3、为每个具体的类(Circle、Triangle)创建一个对应的工厂类并实现工厂接口
//CircleFactory
public class CircleFactory implements ShapeFactory {
@Override
public Shape getShape() {
return new Circle();
}
}
//TriangleFactory
public class TriangleFactory implements ShapeFactory {
@Override
public Shape getShape() {
return new Triangle();
}
}
4、客户使用时只需要知道工厂接口以及具体类的工厂类即可创建对应的具体实例
public class TestDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new CircleFactory();
Shape shape = shapeFactory.getShape();
shape.draw();
shapeFactory = new TriangleFactory();
Shape shape2 = shapeFactory.getShape();
shape2.draw();
}
}
5、当我们需要增加一个新的类型,如Rectange时,那么我们只需要增加具体类以及具体类对应的工厂类即可,无需修改原有的其他任何一行代码。
//Rectange 类
public class Rectange implements Shape {
@Override
public void draw() {
System.out.println("绘制长方形");
}
@Override
public void erase() {
System.out.println("擦除长方形");
}
}
//RectangleFactory工厂
public class RectangleFactory implements ShapeFactory {
@Override
public Shape getShape() {
return new Rectange();
}
}
客户端如果需要使用新的类型时,只需要通过新增的工厂获得对应实例即可。
public class TestDemo {
public static void main(String[] args) {
//... ...
ShapeFactory shapeFactory = new RectangleFactory();
Shape shape = shapeFactory.getShape();
shape.draw();
//... ...
}
}
我们上面示例的实现就是一个工厂方法模式的一个简单实现,通过对简单工厂模式的改造,我们可以发现在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有具体类的实例对象,而是针对不同的具体类实现了不同的工厂,系统提供了一个与具体类相同等级的工厂等级结构。
工厂方法模式(Factory Method Pattern)
定义一个用于创建对象的接口(ShapeFactory),让其子类(CircleFactory、RectangleFactory、TriangleFactory)去决定创建哪一个具体类的实例,工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern), 又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。 工厂方法模式是一种类创建型模式。
工厂方法模式的构成
具体类型的公共接口(Shape)或父类:他是工厂所创建的所有对象的父类,封装了所有被创建对象的公共方法,该角色的引入大大提高了系统的灵活性,使得工厂类中只需要定义一个通用的工厂方法,因为所有创建的具体对象都是该角色的子类对象,为客户端和具体实现类之间的解耦提供了很大的好处。
具体实现类(如Circle、Triangle ):它是工厂类创建的目标,所有被创建的对象都充当这个角色的某个具体实现类。具体实现类都实现了一个公共的接口或者都继承了一个公共的父类,它们需要实现接口或父类中声明的抽象方法。
工厂接口(ShapeFactory):在工厂接口中,声明了工厂方法,用于返回一个具体类。工厂接口是工厂方法模式的核心,所有创建对象的工厂都必须实现这个接口。
具体工厂(CircleFactory、RectangleFactory、TriangleFactory):他是工厂接口(ShapeFactory)的具体实现,实现了工厂接口中的工厂方法,并可由客户端调用,返回一个具体实现类的实例。
在实际使用时, 具体工厂类在实现工厂方法时除了创建具体产品对象之外, 还可以负责产品对象的初始化工作以及一些资源和环境配置工作, 例如连接数据库、 创建文件等。
在客户端代码中, 只需关心工厂类即可, 不同的具体工厂可以创建不同的产品。
小结
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,弥补了简单工厂模式的缺点。工厂方法模式是使用频率最高的设计模式之一, 是很多开源框架和API类库的核心模式。
优点
在工厂方法模式中,工厂方法用来创建具体类的实例,同时向客户隐藏了具体类实例化的细节,用户只需要关心具体类对应的工厂,无需关心实例创建的细节以及甚至不需要关心具体类的类名,实现具体类与用户之间的解耦;
基于工厂接口和具体类型的公共接口的多态性设计是工厂方法模式的关键。它能够让工厂能够自主确定创建何种具体实现类的对象,但是具体对象的创建细节又封装到了具体工厂的内部。工厂方法模式被称为多态工厂模式,就正是因为所有的具体工厂类都有一个公共的接口或者父类。
使用工厂方法模式的另外一个优点就是在系统中加入新的具体类时,无需修改原有的代码,只需要增加新类的实现以及新类对应的工厂即可,完全符合“开闭原则”;
缺点
添加新的具体实现类时,需要添加对应的工厂类,系统中的类的个数会成对增加,在一定程度上增加了系统的复杂度;类的增多就有更多的类需要编译,增加系统的开销;
由于考虑到系统的可扩展性, 需要引入抽象层, 在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度, 且在实现时可能需要用到DOM、 反射等技术, 增加了系统的实现难度;
适用场景
客户端不需要知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体类的类名,只需要知道它的工厂即可,具体类的实例由工厂创建;
抽象工厂类通过其子类来指定创建哪个对象。 在工厂方法模式中, 对于抽象工厂类只需要提供一个创建产品的接口, 而由其子类来确定具体要创建的对象, 利用面向对象的多态性和里氏代换原则, 在程序运行时, 子类对象将覆盖父类对象, 从而使得系统更容易扩展。