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


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

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彈架構:30個設計模式真實案例(附源碼),挑戰年薪60W不是夢

本文為“Tom彈架構”原創,轉載請注明出處。技術在於分享,我分享我快樂!
如果本文對您有幫助,歡迎關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力。關注微信公眾號『 Tom彈架構 』可獲取更多技術干貨!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM