1. 简单工厂模式

1.1 介绍

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,**我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。**常见的工厂模式有简单工厂模式、工厂方法模式、抽象工程模式等。

1.2 优缺点

优点

  • 调用者只需要使用工厂即可,而不用亲自书写创建对象的代码,减少了代码量
  • 扩展性高,如果想增加一个产品,只需要扩展一个工厂即可
  • 屏蔽产品的具体实现,实现了代码层面的解耦

缺点

  • 增加产品时需要扩展工厂,增加了系统的复杂度,也增加了系统具体类的依赖
  • 当需要添加新产品时,需要在工厂内部进行修改,不符合"开—闭原则"

1.3 使用场景

  • 经常创建某对象时,可以使用工厂模式,比如Spring的bean工厂

1.4 注意事项

  • 工厂模式适用于创建复杂对象,而简单对象无需工厂模式,如通过new就可以完成创建的对象

2. 案例实现

注意

在之后的24篇设计模式中,本文全部以造车作为出发点来进行案例代码设计。

我来到4S店买车,我说:“老板,给我来辆奥迪”。老板去汽车厂里调了一台奥迪来了,我又说,来一辆宝马,老板又去宝马车厂里调了一台宝马来了。我又准备说,老板说等等,我好累,让我歇会。

abstract class Car{
   
    abstract String getName();
}

class BMW extends Car{
   

    @Override
    String getName() {
   
        return "宝马";
    }
}

class Audi extends Car{
   

    @Override
    String getName() {
   
        return "奥迪";
    }
}

class CarSimpleFactory{
   
    public static Car getCar(String name){
   
        if(name == "宝马"){
   
            return new BMW();
        }else if(name == "奥迪"){
   
            return new Audi();
        }
        
        return new Audi();
    }
}

/** * 简单工厂模式 * 通过实现工厂类来创建产品 * * @Author cly * @Date 2021/08/30 23:37 * @Version 1.0 */
public class SimpleFactory {
   
    public static void main(String[] args) {
   
        Car car = CarSimpleFactory.getCar("宝马");
        System.out.println(car.getName());
    }
}

3. 源码实现

3.1 JDK中Calendar中

可以说简单工厂模式在 JDK 源码中无处不在,下面以 Calendar 类为例讲解简单工厂模式在 JDK 源码中的应用。Calendar 类的 getInstance() 方法源码如下。

public static Calendar getInstance()
{
   
	return createCalendar(TimeZone.getDefault(),
                       Locale.getDefault(Locale.Category.FORMAT));
}

// 简单工厂模式
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;
}