工厂方法模式是在简单工厂模式上再加了一层,添加了一个抽象工厂和具体工厂。即不再使用一个工厂类来统一负责所有产品的创建,而是把创建具体产品的任务交给了专门的工厂子类去完成。比如说,我要生产矩形、圆形、菱形这三种形状,就不再使用一个工厂了,而是让矩形工厂、圆形工厂、菱形工厂去完成,而这三个工厂又是形状工厂(抽象工厂)的子工厂。如果我们想要新生产一个椭圆形,只需要新增一个椭圆形工厂就行,让椭圆形工厂去完成生产。这更符合开闭原则。
定义
定义一个用于创建对象的接口,但是让子类决定哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
结构
Product(抽象产品)
定义产品的接口,所有具体产品的公共父类。
ConcreteProduct(具体产品)
实现了抽象产品接口,由专门的具体工厂创建,具体工厂与具体产品之间一一对应。
Factory(抽象工厂)
声明了工厂方法,用于返回同一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
ConcreteFactory(具体工厂)
抽象工厂的子类,实现了抽象工厂中声明的工厂方法,可以由客户端调用,返回一个具体产品的实例。
Spring中也用到了工厂方法模式,一般情况下,应用程序有自己的工厂对象来创建bean。如果将应用程序自己的工厂对象交给Spring管理,那么Spring管理的就不是普通的bean,而是工厂Bean。
应用实例 - 日志记录器
/**
*
* @ClassName Log
* @Description 日志记录器接口 - 抽象产品
* @Author 柳成荫
* @Date 2019年9月21日
* @Version V1.0.0
*/
public interface Log {
void writeLog();
}
/**
*
* @ClassName FileLog
* @Description 文件记录器 - 具体产品
* @Author 柳成荫
* @Date 2019年9月21日
* @Version V1.0.0
*/
public class FileLog implements Log {
@Override
public void writeLog() {
System.out.println("文件日志记录");
}
}
/**
*
* @ClassName DatabaseLog
* @Description 数据库日志记录 - 具体产品
* @Author 柳成荫
* @Date 2019年9月21日
* @Version V1.0.0
*/
public class DatabaseLog implements Log {
@Override
public void writeLog() {
System.out.println("数据库日志记录");
}
}
/**
*
* @ClassName LogFactory
* @Description 日志记录工厂接口 - 抽象工厂
* @Author 柳成荫
* @Date 2019年9月21日
* @Version V1.0.0
*/
public interface LogFactory {
Log createLog();
}
/**
*
* @ClassName FileLogFactory
* @Description 文件日志记录器工厂类 - 具体工厂
* @Author 柳成荫
* @Date 2019年9月21日
* @Version V1.0.0
*/
public class FileLogFactory implements LogFactory {
@Override
public Log createLog() {
return new FileLog();
}
}
/**
*
* @ClassName DatabaseLogFactory
* @Description 数据库日志记录工厂类 - 具体工厂
* @Author 柳成荫
* @Date 2019年9月21日
* @Version V1.0.0
*/
public class DatabaseLogFactory implements LogFactory {
@Override
public Log createLog() {
return new DatabaseLog();
}
}
/**
*
* @ClassName Client
* @Description 客户端测试类
* @Author 柳成荫
* @Date 2019年9月21日
* @Version V1.0.0
*/
public class Client {
public static void main(String[] args) {
LogFactory logFactory;
Log log;
logFactory = new FileLogFactory();
log = logFactory.createLog();
log.writeLog();
}
}
优点
1.用户只需关心产品对应的工厂,无需关心创建细节,甚至无需知道具体产品类的类名。
2.让工厂自主确定创建何种产品对象,而如何创建这个对象的细节完全封装在具体工厂内部。
3.系统中加入新产品时无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,只需要添加一个具体的工厂和具体的产品即可。系统的可扩展性非常好,完全符合开闭原则。
缺点
1.添加新产品时,需要编写新的具体产品类和具体的工厂,导致系统中类的个数成对增加。在一定程度上增加了系统的复杂性,有更多的类需要编译和运行,给系统增加了一些额外的开销。
2.需要使用抽象层进行定义,增加了系统的抽象性和理解难度。