本文節選自《設計模式就該這樣學》
1 使用簡單工廠模式封裝產品創建細節
接下來看代碼,還是以創建一門網絡課程為例。假設有Java架構、大數據、人工智能等課程,已經形成了一個生態。我們可以定義一個課程標准ICourse接口。
public interface ICourse {
/** 錄制視頻 */
public void record();
}
創建一個Java課程的實現類JavaCourse。
public class JavaCourse implements ICourse {
public void record() {
System.out.println("錄制Java課程");
}
}
客戶端調用代碼如下。
public static void main(String[] args) {
ICourse course = new JavaCourse();
course.record();
}
由上面代碼可知,父類ICourse指向子類JavaCourse的引用,應用層代碼需要依賴JavaCourse。如果業務擴展,則繼續增加PythonCourse,甚至更多,那么客戶端的依賴會變得越來越臃腫。因此,我們要想辦法把這種依賴減弱,把創建細節隱藏。雖然在目前的代碼中,創建對象的過程並不復雜,但從代碼設計角度來講不易於擴展。因此,用簡單工廠模式對代碼進行優化。首先增加課程PythonCourse類。
public class PythonCourse implements ICourse {
public void record() {
System.out.println("錄制Python課程");
}
}
然后創建CourseFactory工廠類。
public class CourseFactory {
public ICourse create(String name){
if("java".equals(name)){
return new JavaCourse();
}else if("python".equals(name)){
return new PythonCourse();
}else {
return null;
}
}
}
最后修改客戶端調用代碼。
public class SimpleFactoryTest {
public static void main(String[] args) {
CourseFactory factory = new CourseFactory();
factory.create("java");
}
}
當然,為了調用方便,可將CourseFactory的create()方法改為靜態方法,其類圖如下圖所示。

客戶端調用雖然簡單了,但如果業務繼續擴展,要增加前端課程,則工廠中的create()方法就要隨着產品鏈的豐富每次都要修改代碼邏輯,這不符合開閉原則。因此,我們可以采用反射技術繼續對簡單工廠模式進行優化,代碼如下。
public class CourseFactory {
public ICourse create(String className){
try {
if (!(null == className || "".equals(className))) {
return (ICourse) Class.forName(className).newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
客戶端調用代碼修改如下。
public static void main(String[] args) {
CourseFactory factory = new CourseFactory();
ICourse course = factory.create("com.gupaoedu.vip.pattern.factory.simplefactory.JavaCourse");
course.record();
}
優化之后,產品不斷豐富,不需要修改CourseFactory中的代碼。但問題是,方法參數是字符串,可控性有待提升,而且還需要強制轉型。繼續修改代碼。
public ICourse create(Class<? extends ICourse> clazz){
try {
if (null != clazz) {
return clazz.newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
優化客戶端測試代碼。
public static void main(String[] args) {
CourseFactory factory = new CourseFactory();
ICourse course = factory.create(JavaCourse.class);
course.record();
}
最后來看如下圖所示的類圖。

2 簡單工廠模式在JDK源碼中的應用
簡單工廠模式在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) {
}
}
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;
}
3 簡單工廠模式在Logback源碼中的應用
在大家經常使用的Logback中,可以看到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());
}
關注微信公眾號『 Tom彈架構 』回復“設計模式”可獲取完整源碼。
本文為“Tom彈架構”原創,轉載請注明出處。技術在於分享,我分享我快樂!
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力。關注微信公眾號『 Tom彈架構 』可獲取更多技術干貨!
