简单工厂模式详解
简单工厂模式
定义
简单工厂模式是指由一个工厂对象决定创建出哪一种产品类的实例,但它不属于GOF23中设计模式。
适用场景
适用于工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关系。
缺点
工厂类的职责相对过重,不易于扩展过于复杂的产品结构
实践案例
以生产手机为例
public interface IPhone {
//生产
void produce();
}
public class ApplePhone implements IPhone {
@Override
public void produce() {
System.out.println("开始生产Apple手机");
}
}
public class HuaWeiPhone implements IPhone {
@Override
public void produce() {
System.out.println("开始生产华为手机");
}
}
public class XiaoMiPhone implements IPhone {
@Override
public void produce() {
System.out.println("开始生产小米手机");
}
}
public class PhoneFactory {
public IPhone create(String brandName){
IPhone phone = null;
if (brandName.toLowerCase().equals("apple")) {
phone = new ApplePhone();
} else if (brandName.toLowerCase().equals("huawei")) {
phone = new HuaWeiPhone();
} else if (brandName.toLowerCase().equals("xiaomi")) {
phone = new XiaoMiPhone();
}
return phone;
}
}
public class PhoneFactoryTest {
public static void main(String[] args) {
PhoneFactory phoneFactory = new PhoneFactory();
IPhone phone = phoneFactory.create("xiaomi");
phone.produce();
}
}
客户端调用简单,但手机业务扩展时,需要对简单工厂中create()方法修改逻辑,不符合开闭原则
因此,我们对简单工厂还可以继续优化,可加入反射技术:
public class PhoneFactory {
public IPhone create(String className){
IPhone phone = null;
try {
if (className != null && !className.trim().equals("")) {
phone = (IPhone) Class.forName(className).newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return phone;
}
}
public class PhoneFactoryTest {
public static void main(String[] args) {
PhoneFactory phoneFactory = new PhoneFactory();
IPhone phone = phoneFactory.create("com.design.pattern.simple.factory.phone.XiaoMiPhone");
phone.produce();
}
}
优化之后,手机业务扩展后,不需要修改简单工厂类中的方法。但是有个问题,方法参数的字符串,可控性有待提高,而且还需要强制转型,所以再次优化:
public class PhoneFactory {
public IPhone create(Class <? extends IPhone> clazz){
IPhone phone = null;
try {
if (clazz != null) {
phone = clazz.newInstance();
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return phone;
}
}
public class PhoneFactoryTest {
public static void main(String[] args) {
PhoneFactory phoneFactory = new PhoneFactory();
IPhone phone = phoneFactory.create(XiaoMiPhone.class);
phone.produce();
}
}
简单工厂在JDK源码也是无处不在,下面我们来举个例子,例如 Calendar类,看Calendar.getInstance方法,下面打开的是 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;
}
还有一个大家经常使用的 logback,我们可以看到 LoggerFactory 中有多个重载的方法 getLogger():
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
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 http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation");
}
}
return logger;
}