本文从是什么、为什么、怎么做的三个步骤,分析简单工厂模式、工厂方法模式和抽象工厂模式,通过框架源码学习如何优雅地使用工厂模式。
一、什么是工厂模式?
关于什么是工厂模式这个问题呢?其实完全可以见名知意,工厂是做什么的?工厂是生产产品的地方啊,那么映射到编程领域,工厂模式不就是生产对象的一种模式嘛?借用百度百科的一句话来说:工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
工厂模式,按照业务实际场景可以划分为3中不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象方法模式。
简单工厂模式又叫静态工厂方法模式,简单来说就是,该模式有一个具体的工厂类,可以生产许多不同的产品。有一件有意思的事情,简单工厂模式其实不是一种设计模式(不属于GoF23种设计模式),更像是一种编程等习惯,但是由于该习惯经常被使用,许多开发人员都把它称为”工厂模式“,那么就”将错就错“被大家所接受咯。
在简单工厂模式中,生产产品是由一个单一的、具体的工厂类来实现的;在工厂方法模式中,不再采用这种方式,而是由工厂类的子类实现具体产品的生产工作。当增加一类产品的时候,只需要增加一个相应的工厂类的子类。这样做的好处是可以解决简单工厂模式由于生产产品种类过多而引发的代码臃肿的问题。举个例子,需要创建3种类型的产品,简单工厂模式需要3个分支(不管是if-else也好,switch-case也罢),工厂方法模式需要3个子类;那么产品类型有10种呢?当生产的产品种类较多的时候,使用工厂方法模式可以解决简单工厂方法模式大量代码重复的问题,这也就是工厂方法模式存在的意义。
抽象工厂模式是这么定义的:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确执行具体类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么。
二、为什么使用工厂模式?
”为什么要使用工厂模式“,关于这个问题我是这么理解的:
第一,为什么要使用设计模式?逼格,对,设计模式就是逼格的体现。除了逼格,说的实际点,就是可以使代码变得清晰和优雅,让新人接手代码的时候在心里少骂我们几句。
第二,主要的目的还是解耦。如果没有简单工厂模式,我们需要关心生产逻辑(创建过程)和调用,可以使用一坨代码去实现,此时这二者是没有分开的;当使用了简单工厂模式,具体的生产逻辑放在了简单工厂里面,只需要调用即可;当品类增多,简单工厂的工厂类的逻辑变得复杂,耦合变得严重,出现工厂方法模式,将不同品类的生产逻辑剥离到子类中进行,让外部只是知道如何调用即可,实现解耦....其实从不使用设计模式--> 简单工厂模式--> 工厂方法模式-->抽象工厂模式,这就是层次递进的解耦关系。那为什么需要使用工厂模式呢,我想本质也就在这里吧。
三、如何优雅地使用工厂模式?
关于如何使用工厂模式?不写之前的demo了,来看下优秀的框架中是如何使用工厂模式的。
3.1 简单工厂模式
简单工厂模式java.util.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;
}
3.2 工厂方法模式
Logback源码中的工厂方法模式的使用:
public static Logger getLogger(String name){
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static Logger getLogger(Class clazz){
return getLogger(clazz.getName());
}
3.3 抽象方法模式
在Spring源码中,所有工厂都是BeanFactory的子类。通过对BeanFactory的实现,我们可以从Spring的容器访问Bean。根据不同的策略调用getBean()方法,从而获得具体对象。
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> var1);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
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;
@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}