设计模式之工厂方法
工厂方法
定义 :定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行
创建型 :创建型
适用场景:
- 创建对象需要大量重复的代码
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 一个类通过其子类来指定创建哪个对象
优点:
- 用户只需要关心所需产品对应的工厂,无需关心创建的细节
- 加入新产品符合开闭原则,提高可扩展性
缺点:
-
类的个数容易过多,增加复杂度
-
增加了系统的抽象性和理解难度
UML类图:
Product:抽象产品类,也可以是接口,具体看实际情况而定
ConcreteProduct:具体产品实现类
Creator:抽象工厂类,也可以是接口,定义了供子类实现的生产产品的抽象方法,也即是将对象的实例化推迟到子类去做
ConcreteFactory:具体工厂,真正生产产品的类
示例
在上篇简单工厂模式的示例中,不同品牌的打印机对象创建是委托给一个打印机工厂,创建逻辑都堆砌在一个工厂方法中通过if分支判断进行,一旦要引入新品牌的打印机驱动,就需要改造工厂类的方法,违反了开闭原则。在工厂方法中,每一个种产品都有一个对应的工厂类进行产品对象创建,引入一个新的产品,只需要添加新的工厂即可。下面改造下简单工厂模式中的示例
- 定义一个抽象产品接口(也可以是抽象类),定义了所有打印机产品都具有的功能
/**
* 各个打印机需要实现的打印机接口(产品接口)
*/
public interface IPrinter {
void print();
}
- 具体产品实现类
/**
* 惠普打印机(具体产品)
*/
public class HpPrinter implements IPrinter {
@Override
public void print(){
System.out.println("惠普打印机开始打印...");
}
}
/**
* 佳能打印机(具体产品)
*/
public class CanonPrinter implements IPrinter {
@Override
public void print(){
System.out.println("佳能打印机开始打印");
}
}
- 抽象工厂方法,这里就是统一了打印机工厂创建产品的行为
/**
* 抽象工厂
*/
public abstract class PrinterFactory {
abstract IPrinter createPrinter();
}
- 具体工厂类,实现具体产品的创建
public class CannoPrinterFactory extends PrinterFactory {
@Override
IPrinter createPrinter() {
return new CanonPrinter();
}
}
public class HpPrinterFactory extends PrinterFactory {
@Override
IPrinter createPrinter() {
return new HpPrinter();
}
}
- 客户端调用
public class Client {
public static void main(String[] args){
//新增不同的产品,只需要提供不同的创建工厂实例
PrinterFactory cannoPrinterFactory = new CannoPrinterFactory();
IPrinter cannoPrinter = cannoPrinterFactory.createPrinter();
cannoPrinter.print();
PrinterFactory hpPrintPrinterFactory = new HpPrinterFactory();
IPrinter hpPrinter = hpPrintPrinterFactory.createPrinter();
hpPrinter.print();
}
}
上面的示例中,我们可以发现,虽然我们增加新的产品时候不需要像简单工厂那样去修改工厂类,但是却增加了相对应的产品工厂实现类和产品实现类(产品的实现类也可以放在工厂类中当做一个内部类去维护),造成了类的膨胀。在后面的抽象工厂模式中,引入了产品族的概念,可以比较下二者的适用范围
使用典范
- java.util.Collection
//相当于是抽象工厂类
public interface Collection<E> extends Iterable<E> {
//...省略
/**
* 这个就是其中一个工厂方法,需要由子类去实现
* Returns an iterator over the elements in this collection. There are no
* guarantees concerning the order in which the elements are returned
* (unless this collection is an instance of some class that provides a
* guarantee).
*
* @return an <tt>Iterator</tt> over the elements in this collection
*/
Iterator<E> iterator();
//...省略
}
其中的ArrayList就是一个抽象工厂类的实现类,里面实现了 iterator()工厂方法,其中返回的Itr() 返回的就是一个具体的产品,Iterator接口就是抽象产品接口
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//...省略
/**
* 对Collection中定义的工厂方法实现,返回一个Iterator产品接口的实现类对象
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* Iterator 就是一个抽象产品接口,Itr 就是具体的产品类
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
// ...省略
}
// ...省略
}
public static void main(String[] args) {
Collection fatory = new ArrayList();//具体工厂实例
Iterator iterator = fatory.iterator();//返回具体产品实例
// ...接下来就可以是用iterator这个产品的功能,如循环遍历
}
- logback日志框架中的LoggerFactory
一般的日志框架不会直接使用,而是使用门面模式的slf4j,它提供了一个抽象工厂接口org.slf4j.ILoggerFactory和一个抽象产品接口org.slf4j.Logger,而具体的日志框架随用户自行引入
package org.slf4j;
//抽象产品工厂
public interface ILoggerFactory {
//抽象工厂方法
public Logger getLogger(String name);
}
//抽象产品接口
public interface Logger {
//省略 ...
}
我们一般在项目中,都会以如下的方式去获得日志对象
final static Logger logger = LoggerFactory.getLogger(Wombat.class);
查看LoggerFactory类,可以追溯到这样一个获得日志产品对象的方法
/**
* Return a logger named according to the name parameter using the
* statically bound {@link ILoggerFactory} instance.
*
* @param name
* The name of the logger.
* @return logger
*/
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();//这里是获得了具体工厂
return iLoggerFactory.getLogger(name);//调用工厂获得具体日志产品对象
}
我们在代码中都是调用门面模式slf4j的api,隔离了具体的日志框架,这给我们在替换日志组件时候达到了无缝切换的效果,但有一个大大的前提是这些日志组件都是实现了slf4j定义的抽象产品接口和抽象工厂接口规范
重点关注getILoggerFactory
,下面的英文注释也说得很明白,在编译时就会绑定日志组件的工厂实例并返回
/**
* Return the {@link ILoggerFactory} instance in use.
* <p/>
* <p/>
* ILoggerFactory instance is bound with this class at compile time.
*
* @return the ILoggerFactory instance in use
*/
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
查看ILoggerFactory接口发现,有三个ILoggerFactory实现类
其中Logback日志框架的工厂如下:
//具体工厂实现类
public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
...
//具体工厂方法
@Override
public final Logger getLogger(final String name) {
...
}
...
}
因此当引入了logback日志组件并配置日志参数后,调用getILoggerFactory
方法返回的就是logback的工厂实例