工厂模式详解

1.1工厂模式的由来

现实生活中,原始社会(没有工厂)--> 农耕小作坊(简单工厂)--> 工业革命(工厂方法)--> 代工厂(抽象工厂),我们的项目代码同样也是由简至繁一步一步迭代而来的,但对于调用者,却越来越简单。在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。其中复杂对象指的是类的构造函数参数过多等对类的构造有影响的情况下,我们将创建对象的过程抽离出来,减少代码中的耦合以及后期我们的维护成本。

1.2工厂模式的分类

工厂模式按照实际业务场景进行划分,有3种不同的实现方式分别是简单工厂模式、工厂方法模式和抽象工厂模式。

2.1简单工厂模式

2.1.1 简单工厂模式的定义

简单工厂模式(Simple Factory Pattern)又叫做静态工厂方法(Static Factory Method Pattern)模式,简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。

2.1.2 简单工厂模式的应用场景

对于一个产品种类相对较少,考虑使用简单工厂模式可以很方便的创建所需要的产品。使用简单工厂模式的客户端是需要传入工厂类的参数,由工厂完成创建对象的逻辑。

2.1.3 简单工厂模式的通用写法

类图

public interface IProduct {
    void doSomething();
}
public class ProductA implements IProduct{
    @Override
    public void doSomething() {
        System.out.println("Product A");
    }
}
public class ProductB implements IProduct{
    @Override
    public void doSomething() {
        System.out.println("Product B");
    }
}
public class ProductC implements IProduct{
    @Override
    public void doSomething() {
        System.out.println("Product C");
    }
}
public class SimpleFactory {
    public SimpleFactory() {
    }

    public static IProduct makeProduct(String type){
        switch (type){
            case "A":
                return new ProductA();
            case "B":
                return new ProductB();
            case "C":
                return new ProductC();
            default:
                System.out.println("不支持当前类型");
                break;
        }
        return null;
    }
}

2.1.4 简单工厂模式封装产品的创建细节

类图

public interface Animal {
    void eatFood();
}
public class Cat implements Animal {
    @Override
    public void eatFood() {
        System.out.println("猫吃猫粮");
    }
}
public class Dog implements Animal {
    @Override
    public void eatFood() {
        System.out.println("狗吃狗粮");
    }
}
public class AnimalFactory {
    public static Animal create(String name){
        if ("cat".equals(name)){
            return new Cat();
        }else if ("dog".equals(name)){
            return new Dog();
        }else {
            return null;
        }
    }
}

思考分析:这样虽然简化了调用者的调用方式,但如果增加更多的动物种类,每次都需要取维护工厂类的create方法,这不符合开闭原则,因此我们采用反射继续对代码优化。

public static Animal create(String classname) {
     if (!(null == classname || "".equals(classname))) {
         try {
             return (Animal) Class.forName(classname).newInstance();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
     return null;
 }
public class SimpleFactoryTest {
    public static void main(String[] args) {
        AnimalFactory.create("org.example.simplefactory2.cat");
    }
}

这样的话,即使动物种类增加,工厂类代码也不需要变化,只是代码中的入参为类名路径,可控性差,继续优化。

  public static Animal create(Class<? extends Animal> clazz){
        try{
            if (null != clazz){
             return clazz.newInstance();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
public class SimpleFactoryTest {
    public static void main(String[] args) {
        AnimalFactory.create(Cat.class);
    }
}

2.1.5 简单工厂模式在框架源码中的应用

  • 简单工厂在JDK源码中举例(Calendar)
 private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }
  • 简单工厂在Logback源码中的应用
    Logback中的LoggerFactory,根据入参不同存在多个重载的getLogger方法
public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }

    public static Logger getLogger(Class<?> clazz) {
        Logger logger = getLogger(clazz.getName());
        if (DETECT_LOGGER_NAME_MISMATCH) {
            Class<?> autoComputedCallingClass = Util.getCallingClass();
            if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
                Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
                Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
            }
        }

        return logger;
    }

2.2 工厂方法模式

2.2.1 工厂方法模式的定义

工厂方法模式(Factory Method Pattern)又叫作多态性工厂模式,指定义一个创建对象的接口,但由实现这个接口的类来决定实例化哪个类,工厂方法把类的实例化推迟到子类中进行。
也就是说,工厂方法模式中,不在由工厂类生产对应的产品,而是由工厂类的子类实现具体产品的生产逻辑,解决简单工厂中switch case的臃肿,也符合开闭原则。

2.2.2 工厂方法模式的应用场景

  • 创建对象需要大量重复代码
  • 应用层不依赖产品实例的创建细节
  • 一个类通过起子类指定创建哪个对象实例

2.2.3 工厂模式的通用写法

public interface IFactory {
    IProduct makeProduct();
}
public class FactoryB implements IFactory{
    @Override
    public IProduct makeProduct() {
        return new ProductB();
    }
}
public class FactoryA implements IFactory{

    @Override
    public IProduct makeProduct() {
        return new ProductA();
    }
}

工厂方法模式主要是解决产品的扩展问题,当需要增加新产品时,我们只需要新增类去实现工厂接口,做到工厂职责的单一化,符合单一职责原则。

2.2.4 工厂方法模式在框架中的应用

  • 工厂方法模式在Logback中的应用
public interface ILoggerFactory {
    Logger getLogger(String var1);
}
public class NOPLoggerFactory implements ILoggerFactory {
    public NOPLoggerFactory() {
    }

    public Logger getLogger(String name) {
        return NOPLogger.NOP_LOGGER;
    }
}
.....

2.3 抽象工厂模式

2.3.1 抽象工厂模式概述

抽象工厂模式(Abstract Factory Pattern)指提供一个创建一系列相关或相互依赖对象的接口,无需指定他们具体的类。客户端不需要指定产品的具体类型,创建多个产品族中的产品对象。

2.3.2 抽象工厂的应用场景

抽象工厂模式适用于需要生成产品族的情景。抽象产品类内部提供了多个其他抽象产品,抽象工厂提供了多个具体的工厂子类,可以生产相应的产品族对象。

2.3.3 抽象工厂的通用写法

类图

public interface IAbstractFactory {
    IProductA makeProductA();

    IProductB makeProductB();
}
public interface IProductA {
    void doA();
}
public interface IProductB {
    void doB();
}
public class ProductA1 implements IProductA {
    @Override
    public void doA() {
        System.out.println("A1 属于 A");
    }
}
public class ProductA2 implements IProductA{
    @Override
    public void doA() {
        System.out.println("A2 属于 A");
    }
}
public class ProductB1 implements IProductB{
    @Override
    public void doB() {
        System.out.println("B1 属于 B");
    }
}
public class ProductB2 implements IProductB{
    @Override
    public void doB() {
        System.out.println("B2 属于 B");
    }
}
public class Factory1 implements IAbstractFactory{
    @Override
    public IProductA makeProductA() {
        return new ProductA1();
    }

    @Override
    public IProductB makeProductB() {
        return new ProductB1();
    }
}
public class Factory2 implements IAbstractFactory{
    @Override
    public IProductA makeProductA() {
        return new ProductA2();
    }

    @Override
    public IProductB makeProductB() {
        return new ProductB2();
    }
}

2.3.4 抽象工厂在框架中的使用

  • 抽象工厂在Spring中的使用(BeanFactory)
public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

在Spring中,所有工厂都是BeanFactory的子类,BeanFactory根据不同的策略调用getBean,获取不同的对象。

2.3.5 优缺点分析

优点:

  • 当需要扩展产品族时,只需要增加新的具体工厂的实现类,不需要对已有代码进行修改,符合开闭原则。
  • 对于客户端,只需要调用同一个产品的产品族。
    缺点:
  • 产品族中扩展新产品困难,需修改抽象工厂接口。
posted @ 2022-03-02 11:37  amazing_yml  阅读(4750)  评论(0编辑  收藏  举报