简介
定义:定义一个工厂类,可以根据传入的参数不同创建不同类实例,被创建的实例通常都有相同的父类。
简单工厂模式在java中得到了大量的使用,它属于创建型的设计模式,但是它不属于GOF23设计模式中的一种。工厂模式提供公共的接口,客户端直接使用公共接口来创建对象,客户端这边不关心对象是怎么创建的,其中包含3个角色:工厂角色,抽象产品角色,具体产品角色,工厂角色是简单工厂模式的核心,负责产品实例的内部逻辑;抽象产品角色是所有具体产品角色的父类,封装了公共的方法;具体产品角色是工厂角色创建的目标对象。因为简单工厂模式将对象的创建和使用分离,使得系统更加符合单一职责原则。
适用场景
1、工厂类创建的对象比较少
2、客户端只需要传入某个参数,对如何创建对象不关心
优点
1、只需要传入参数就可以获取到需要的对象,客户端使用简单。
2、通过反射或者配置文件,可以在不修改任何代码的情况下更换或者新增产品类,提供系统的灵活性。
3、让创建和使用进行分离。
缺点
1、工厂类的职责比较重,如果新增一些类,需要修改工厂类判断逻辑,违背了开闭原则。
2、增加类的个数,增加系统的复杂性和理解难度。
代码示例
如图,可以看到简单工厂的UML类图:
富士康可以生产华为、苹果等手机,那么以这个为例子来看看具体的类:
public interface Mobile {
void produce();
}
public class IphoneMobile implements Mobile{
public void produce() {
System.out.println("生产苹果手机");
}
}
public class HuaweiMobile implements Mobile{
public void produce() {
System.out.println("生产华为手机");
}
}
public class FoxconnFactory {
public Mobile getMobile(String mobileType){
if("iphone".equals(mobileType)){
return new IphoneMobile();
}else if("huawei".equals(mobileType)){
return new HuaweiMobile();
}
return null;
}
}
public class SimpleFactoryTest {
public static void main(String[] args) {
FoxconnFactory foxconnFactory = new FoxconnFactory();
Mobile mobile = foxconnFactory.getMobile("iphone");
mobile.produce();
Mobile huawei = foxconnFactory.getMobile("huawei");
huawei.produce();
}
}
通过传入的mobileType类型来判断创建什么手机,如果需要创建小米,那么需要修改FoxconnFactory里的getMobile方法,这样就会对工厂类进行修改,可以通过反射来修改上面的方法。如:
public Mobile getMobile(Class c){
try {
return (Mobile)Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
FoxconnFactory foxconnFactory = new FoxconnFactory();
Mobile ipohoneMobile = foxconnFactory.getMobile(IphoneMobile.class);
ipohoneMobile.produce();
}
改成反射,如果新增一个手机,那么不需要修改工厂类了,符合开闭原则。
上面示例源码
源码分析
日期中使用
Calendar中也有使用简单工厂模式,如:
public static Calendar getInstance(TimeZone zone)
{
return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
//省略.....
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 (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;
}
createCalendar中根据Locale来判断使用哪个Calendar,来看下它的UML图:
DriverManager中使用
DriverManager中的registerDriver方法,也使用了简单工厂,DriverManager.registerDriver(new Driver());
通过传入java.sql.Driver
来注册具体哪个数据库驱动。
日志中使用
protected Logger logger = LoggerFactory.getLogger(getClass());
通过该代码来获取具体使用哪个日志框架,部分源码:
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 " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public class Log4jLoggerFactory implements ILoggerFactory {
//省略代码。。。。
public Logger getLogger(String name) {
Logger slf4jLogger = loggerMap.get(name);
if (slf4jLogger != null) {
return slf4jLogger;
} else {
org.apache.log4j.Logger log4jLogger;
if (name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
log4jLogger = LogManager.getRootLogger();
else
log4jLogger = LogManager.getLogger(name);
Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
return oldInstance == null ? newInstance : oldInstance;
}
}
}
可以看出通过名称来获取日志框架,UML类图如下:
注意:本文归作者所有,未经作者允许,不得转载