问题背景
面向接口编程:
Api api=new Imp();
上面代码中,客户端直接用new Imp()来创建实现类,存在以下问题:
- 面向接口编程一个重要原因是隐藏实现。new Imp()的方式将内部实现暴露给了客户端。
- 面向接口编程另一个重要原因解耦合。如果有新的实现类,需要改代码,没有把接口和实现完全分离。
如何只给客户端Api接口,而不让他们知道内部实现类呢?
简单工厂模式
简单工厂模式(Simple Factory Pattern):定义一个工厂类,根据参数的不同返回不同类的实例,这些实例通常都具有共同的父类或接口。
简单工厂模式是一种创建型模式,创建实例的方法一般是静态方法,因此又称为静态工厂方法模式(Static Factory Method)。
简单工厂模式的大概结构如下图:
Factory是最核心的部分,createApi方法用于创建Api实现类,根据不同的条件,选择不同的实现。
简单工厂模式的核心思想是让用户“选择实现”,可以通过以下方式:
- 方法参数,如根据不同的condition值创建不同的实现类。
- 配置文件,根据配置文件利用反射等技术动态创建实现类。
适用场景
简单工厂模式的优点:
- 客户端避免直接创建产品对象,实现了对象创建和使用的分离。
- 通过配置文件,可以在不修改客户端代码的情况下更换或增加新的具体产品类,在一定程度上提高了系统的灵活性。
简单工厂模式的缺点:
- 工厂类集中了所有产品的创建逻辑,职责过重,容易违背单一职责原则。
- 添加新产品必须修改工厂逻辑,不利于系统的扩展和维护,违背开闭原则。
- 简单工厂模式一般使用静态工厂方法,导致工厂类无法使用继承。
简单工厂模式适用场景:
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
- 工厂类负责创建的对象比较少,工厂方法中的业务逻辑不至于太过复杂。
典型实例
Calendar
Calendar类的静态方法getInstance(TimeZone zone, Locale aLocale),通过客户端传入的Locale选择不同的实现类。
这里,Calendar同时扮演了抽象产品和工厂的角色,具体的产品是Calendar的子类。
public static Calendar getInstance(TimeZone zone, Locale aLocale)
{
return createCalendar(zone, aLocale);
}
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 interface ILoggerFactory {
Logger getLogger(String var1);
}
LoggerFactory中的getLogger方法:
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static Logger getLogger(Class clazz) {
return getLogger(clazz.getName());
}