一、简介
工厂模式是工作中比较常用的一种模式,分为简单工厂模式、工厂方法模式,抽象工厂模式,接下来我们将首先介绍简单工厂模式的思想以及通过一个简答的示例说明其好处。
- 概念:
简单工厂模式属于创建型模式,是工厂模式的一种,也叫静态工厂模式(Static Factory Method)。简单工厂模式是由一个工厂对象决定创建出哪一种类型的产品,定义一个统一的创建对象的类,由这个类负责实例化对象的行为。
- 说明:
实例化对象的时候不再使用 new Object()形式,可以根据用户的选择条件来实例化相关的类。对于客户端来说,去除了具体的类的依赖。只需要给出具体实例的描述给工厂,工厂就会自动返回具体的实例对象。
二、简单工厂模式
以生产各种品牌的汽车为例,说明简单工厂的实现方法,下图是相关类图:
具体代码实现:
【a】ICar汽车接口
/**
* @Description: ICar汽车接口
* @author: weixiaohuai
* @Date: 2019/10/19 14:49
*/
public interface ICar {
/**
* 生产汽车
*/
void productCar();
}
【b】BenzCar奔驰汽车类
/**
* @Description: BenzCar奔驰汽车类
* @author: weixiaohuai
* @Date: 2019/10/19 14:49
*/
public class BenzCar implements ICar {
@Override
public void productCar() {
System.out.println("生产奔驰汽车...");
}
}
【c】BmwCar宝马汽车类
/**
* @Description: BmwCar宝马汽车类
* @author: weixiaohuai
* @Date: 2019/10/19 14:49
*/
public class BmwCar implements ICar {
@Override
public void productCar() {
System.out.println("生产宝马汽车...");
}
}
【d】AudiCar奥迪汽车类
/**
* @Description: AudiCar奥迪汽车类
* @author: weixiaohuai
* @Date: 2019/10/19 14:49
*/
public class AudiCar implements ICar {
@Override
public void productCar() {
System.out.println("生产奥迪汽车...");
}
}
【e】简单工厂类
/**
* @Description: 简单工厂类,根据汽车类型生产不同品牌的汽车供客户端使用
* @author: weixiaohuai
* @Date: 2019/10/19 14:49
* <p>
* 说明:客户端不需要知道具体生产汽车的逻辑,只管使用汽车就行,简单工厂模式将创建对象的操作统一封装在工厂类中,
* 减少了客户端对汽车的具体依赖,从某种程度上有利于松耦合。
*/
public class ICarSimpleFactory {
/**
* 根据汽车类型返回具体的汽车
*
* @param carTypeEnum 汽车类型
* @return 汽车
*/
public static ICar productCarByCarType(CarTypeEnum carTypeEnum) {
ICar iCar = null;
switch (carTypeEnum) {
case BENZ:
iCar = new BenzCar();
break;
case BMW:
iCar = new BmwCar();
break;
case AUDI:
iCar = new AudiCar();
break;
default:
break;
}
return iCar;
}
}
/**
* 汽车类型枚举类
*/
enum CarTypeEnum {
BMW, BENZ, AUDI
}
简单工厂类对外提供了一个根据汽车品牌生产具体汽车类对象的静态方法,客户端使用的时候直接传入各种品牌对应的名字即可,不必关心具体怎么生产汽车的,工厂类会直接返回具体的汽车对象。
【f】客户端代码:客户端直接调用静态工厂方法实例化具体的产品对象。
public class Client {
public static void main(String[] args) {
ICar benzCar = ICarSimpleFactory.productCarByCarType(CarTypeEnum.BENZ);
ICar audiCar = ICarSimpleFactory.productCarByCarType(CarTypeEnum.AUDI);
benzCar.productCar();
audiCar.productCar();
}
}
【g】运行结果
可见,客户端只需传入具体品牌对应的类型即可返回汽车对象,不需再使用 new Object()形式创建,后面如果想扩展其他品牌的时候,只需要在工厂类中增加相应的分支判断即可。
三、总结
优点:
- 客户端使用的时候,不必直接依赖具体的产品对象,只负责消费产品即可,明确了各自的职责和权利。
缺点:
- 工厂类负责所有实例的创建行为,这样违反了高内聚原则,将全部创建逻辑集中到了一个工厂类中,会显得工厂类很臃肿,而且工厂类能创建的产品类型一般都不能过多,如果需要添加新的类,则需要改变工厂类的代码。如果系统中的产品种类不断增加,会导致工厂类太多判断逻辑,对系统的维护和扩展都不太好,同时也违反了开闭原则(对扩展开放,对修改关闭)。
适用场景:
- 工厂类中创建的对象种类相对比较稳当,也比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。;
- 客户端只知道传入工厂类的参数,对于如何创建对象(逻辑)并不关心:只需要知道产品类型所对应的参数;
JDK中使用到简单工厂模式的类:
- 如java.text.DateFormat、java.util.Calendar等。下面简单分析一下Calendar类中怎么使用简单工厂的,下面是关键代码,通过静态工厂方法实例化Calendar对象。
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;
}