徹底說透簡單工廠那些你沒有關注過的細節

Tom彈架構發表於2021-11-10

本文節選自《設計模式就該這樣學》

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()方法改為靜態方法,其類圖如下圖所示。

file

客戶端呼叫雖然簡單了,但如果業務繼續擴充套件,要增加前端課程,則工廠中的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();
}

最後來看如下圖所示的類圖。

file

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彈架構 』可獲取更多技術乾貨!

相關文章