工厂方法模式
工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。
首先,我们先介绍简单工厂模式。
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
下面,我将由具体的实例一步步的引出为什么要用工厂方法模式,以及工厂方法模式解决了哪些问题。
想象一下,如果让我们自己实现一个简单的像 log4j 那样的日志框架该怎么设计呢?
我们可能这样做,但这样做的坏处是什么?
public class Log1 {
public static void debug(String message){
System.out.println(message);
}
}
public class Client2 {
Logger logger= LoggerFactory.getLogger(Config.LOG_TYPE);
public void begin(){
logger.debug("log");
}
}
这样做的坏处就是
有一天,我们想要把我们的应用改为一个更好日志框架,我们蒙了,为什么呢?
因为像
Log1 log1=new Log1();
这样的代码在每个类里面都有一个,我们要每一个都要更改,麻烦不说,还易出错。
于是我们做了如下改进。简单工厂模式登场了。
public class LoggerFactory {
public static Logger getLogger(String logType){
if("Log1".equals(logType)){
return new Log1();
}
if("Log2".equals(logType)){
return new Log2();
}
return null;
}
}
public class Client2 {
Logger logger= LoggerFactory.getLogger(Config.LOG_TYPE);
public void begin(){
logger.debug("log");
}
}
这样,我们只需改下配置中的日志类型就可以实现两种日志间的无缝切换,看起来一切完美。
简单工厂模式将日志对象的实例化推迟到工厂中,实现了应用和日志对象的实例化的解耦。
但是,简单工厂模式有什么不足呢?
工厂方法模式又改进了简单工厂模式的哪些缺点呢?
现在,我们又觉得原先的日志框架功能不太好用,我们又想开发一个新的日志框架了。
于是我们又加了一个if else 。如果以后我们又想再加呢?我们还得再写一个if else 。
像下面这样
public class LoggerFactory {
public static Logger getLogger(String logType){
if("Log1".equals(logType)){
return new Log1();
}
if("Log2".equals(logType)){
return new Log2();
}
if("Log3".equals(logType)){
return new Log3();
}
return null;
}
}
我们已经发现问题了,这样做违反了开闭原则。
于是。。。。。。工厂方法模式登场了
于是我们进一步改进我们的框架,采用工厂方法模式。
public class LoggerFactory {
private static IFactory iFactory;
public static Logger getLogger(){
return iFactory.getLogger();
}
}
public interface IFactory {
Logger getLogger();
}
public class Log1Factory implements IFactory {
@Override
public Logger getLogger() {
return new Log1();
}
}
这样,当以后我们再有新的日志框架时
只要这个新的日志框架符合我们的接口定义,实现了Ifactory 接口。我们就只需要为LoggerFactroy 的ifactory 注入这个新的工厂就行了。
注入方式可以是构造方法注入,setter 注入或者让 Spring 帮我们注入。
不错,self4j 就是这么一个日志接口的定义。Log4j2 和Logback 都实现了self4j 的接口,所以我们就可以开开心心的在Log4j2 和Logback 以及以后可能出的任何实现self4j 接口的日志框架之间自由切换了。
当然,他们的实现比我们这个简单的样例要复杂的多。
而且他们注入工厂是自动扫描工程中实现 self4j接口的实现类来实现,所以只要我们引入相关jar 包就行啦。