设计模式之工厂方法

工厂方法

定义 :定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行

创建型 :创建型

适用场景

  • 创建对象需要大量重复的代码
  • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
  • 一个类通过其子类来指定创建哪个对象

优点

  • 用户只需要关心所需产品对应的工厂,无需关心创建的细节
  • 加入新产品符合开闭原则,提高可扩展性

缺点

  • 类的个数容易过多,增加复杂度

  • 增加了系统的抽象性和理解难度

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的工厂实例

posted @ 2020-04-19 19:31  didi516  阅读(201)  评论(0编辑  收藏  举报