什么是简单工厂模式
工厂模式是最常用的一类创建型设计模式。它不属于GoF 23种设计模式。它的设计思想很简单,基本流程:首先将需要创建的不同对象封装到不同的具体产品类中,将公共部分进行抽取后封装到一个抽象产品类中,每一个具体产品类都是抽象产品类的子类。然后提供一个工厂类用于创建各种产品,在工厂类中提供一个创建产品类的方法,该方法可以根据所传入的参数不同创建不同的具体产品对象,客户端只需要调用工厂类的工厂方法。
简单工厂模式是说定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态方法,因此简单工厂模式又称为静态工厂方法模式。
简单工厂模式包含以下几个角色:
Factory(工厂角色):即工厂类,是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑。工厂类可以被客户单直接调用创建所需要的产品对象,在工厂类中提供了静态的工厂方法返回抽象的产品类型。
Product(抽象产品角色):工厂类所创建的的所有对象的父类,封装了各种产品对象的公共方法。
ConcreteProduct(具体产品角色):是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,并实现了抽象产品中声明的抽象方法。
简单工厂模式的优缺点
优点
- 工厂类包含必要的判断逻辑,可以决定在声明时候创建哪一个产品实例,客户端可以免除直接创建产品对象的职责,简单工厂模式实现了对象的创建和使用的分离。
- 客户端无需知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可。
缺点
- 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式必然会增加系统中类的个数,增加了系统的复杂性。
- 系统扩展难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成无法形成基于继承的等级结构。
简单工厂模式的应用场景
- 工厂类负责创建的对象比较少,由于创建的对象比较少,不会造成工厂方法中的业务逻辑太复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心。
简单工厂模式案例
/**
* 抽象产品
*/
public interface SweepingRobot {
void orderRobot();
}
/**
* 具体产品
*/
public class XiaoMiRobot implements SweepingRobot {
@Override
public void orderRobot() {
System.out.println("购买小米扫地机器人。");
}
}
/**
* 具体产品
*/
public class EcovacsRobot implements SweepingRobot {
@Override
public void orderRobot() {
System.out.println("购买科沃斯扫地机器人.");
}
}
/**
* 工厂类
*/
public class RobotSimpleFactory {
public static SweepingRobot orderRobot(Integer brand) {
switch (brand) {
case 1:
return new XiaoMiRobot();
case 2:
return new EcovacsRobot();
default:
return null;
}
}
}
public class Client {
public static void main(String[] args) {
SweepingRobot sweepingRobot = RobotSimpleFactory.orderRobot(1);
sweepingRobot.orderRobot();
}
}
简单工厂模式在源码中的应用
Calendar
/**
* Calendar 是抽象产品,也充当了工厂类
**/
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
/**
* Converts the current calendar field values in {@link #fields fields[]}
* to the millisecond time value
* {@link #time}.
*
* @see #complete()
* @see #computeFields()
*/
protected abstract void computeTime();
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;
}
......
}
/**
* 具体产品
**/
class JapaneseImperialCalendar extends Calendar {
protected void computeTime() {
// In non-lenient mode, perform brief checking of calendar
// fields which have been set externally. Through this
// checking, the field values are stored in originalFields[]
// to see if any of them are normalized later.
if (!isLenient()) {
if (originalFields == null) {
originalFields = new int[FIELD_COUNT];
}
for (int field = 0; field < FIELD_COUNT; field++) {
int value = internalGet(field);
if (isExternallySet(field)) {
// Quick validation for any out of range values
if (value < getMinimum(field) || value > getMaximum(field)) {
throw new IllegalArgumentException(getFieldName(field));
}
}
originalFields[field] = value;
}
}
......
}
}
/**
* 具体产品
**/
public class GregorianCalendar extends Calendar {
protected void computeTime() {
// In non-lenient mode, perform brief checking of calendar
// fields which have been set externally. Through this
// checking, the field values are stored in originalFields[]
// to see if any of them are normalized later.
if (!isLenient()) {
if (originalFields == null) {
originalFields = new int[FIELD_COUNT];
}
for (int field = 0; field < FIELD_COUNT; field++) {
int value = internalGet(field);
if (isExternallySet(field)) {
// Quick validation for any out of range values
if (value < getMinimum(field) || value > getMaximum(field)) {
throw new IllegalArgumentException(getFieldName(field));
}
}
originalFields[field] = value;
}
}
}
......
}