架構師內功心法,經典框架都在用的工廠模式詳解

1、經典框架都在用設計模式解決問題

Spring就是一個把設計模式用的淋漓盡致的經典框架,其實從類的名稱就可以看出來,咱們來一一列舉一下:設計模式

特別須要說明的是,設計模式歷來都不是單個設計模式獨立使用的。 在一般狀況下,常常是多個設計模式混合使用,你中有我,我中有你。全部的設計模式講解都會圍繞Spring的IOC、AOP、JDBC、MVC來進行展開。設計模式根據設計類型進行分類以下:框架

2、工廠模式詳解

2.1 工廠模式的由來

在咱們的現實生活當中,原始社會自給自足(沒有工廠)、農耕社會的小做坊(簡單工廠,民間酒坊)、工業革命流水線(工廠方法,自產自銷)、現代產業鏈工廠(抽象工廠,富士康)ide

從現實生活聯想到咱們項目中的代碼一樣也是由簡而繁一步一步迭代而來的,可是對於調用者確是愈來愈簡單化。學習

2.2 簡單工廠模式(Simple Factory Pattern)

簡單工廠模式是指由一個工廠對象決定建立出哪種產品的實例,但它不屬於GOF,23設計模式。優化

參考資料維基百科地址:https://en.wikipedia.org/wiki/Design_Patterns#Patterns_by_Type設計

簡單工廠模式適用於工廠類負責建立的對象較少的場景,且客戶端只須要傳入工廠類的參數,對於如何建立對象的邏輯不須要關係。3d

接下來咱們來舉例,以高中學校課程爲例。語文、數學、英語等多門學科。咱們能夠定義一個課程標準ICourse接口:code

public interface ICourse {

    /**
     * 學習課程
     */
    public void study();
}

建立一個語文課的實現ChineseCourse類:orm

public class ChineseCourse implements ICourse {
    @Override
    public void study() {
        System.out.println("學習語文課");
    }

    public static void main(String[] args) {
        ICourse course = new ChineseCourse();
        course.study();
    }
}

看上面的main方法中,應用層的代碼須要依賴ChineseCourse,若是業務擴展,會繼續增長MathCourse甚至更多,這樣的話客戶端的依賴會愈來愈臃腫的。因此咱們須要對建立代碼的細節進行隱藏,咱們使用簡單工廠模式對代碼進行優化。先添加MathCourse類:視頻

public class MathCourse implements ICourse {
    @Override
    public void study() {
        System.out.println("學習數學課");
    }
}

建立CourseFactory工廠類:

public class CourseFactory {
    public ICourse create(String name) {
        if("chinese".equals(name)) {
            return new ChineseCourse();
        }else if("math".equals(name)) {
            return new MathCourse();
        }else {
            return null;
        }
    }
}

mian方法調用:

public static void main(String[] args) {
        CourseFactory courseFactory = new CourseFactory();
        courseFactory.create("chinese");
}

客戶端調用是簡單了,可是咱們的業務繼續擴展,須要增長英文課,那麼工廠中的create()方法要根據增長的業務每次都修改代碼邏輯,不符合開閉原則。所以,咱們還須要對簡單工廠進行優化,利用反射技術:

public class CourseFactory {

    public ICourse create(String className) {
            try {
                if(!(null == className || "".equals(className))) {
                    return (ICourse) Class.forName(className).newInstance();
                }
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
    }

    public static void main(String[] args) {
        CourseFactory courseFactory = new CourseFactory();
        ICourse course = courseFactory.create("com.sfp.ChineseCourse");
        course.study();
    }
}

優化以後,課程不斷增長不須要修改CourseFactory中的代碼了。可是,方法參數是字符串,可控性有待提升,並且還需進行強制轉換。再次修改代碼:

public class CourseFactory {

    public ICourse create(Class<? extends ICourse> clazz) {
        try {
            if(null != clazz) {
                return clazz.newInstance();
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        CourseFactory courseFactory = new CourseFactory();
        ICourse course = courseFactory.create(ChineseCourse.class);
        course.study();
    }
}

簡單工廠模式的例子無處不在,如今咱們來看JDK當中的類使用簡單工廠模式的例子,例如Calendar類,其中Calendar.getInstance()方法,咱們查看源碼具體的實現步驟:

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) {
    return getLogger(clazz.getName());
}

簡單工廠的缺點:工廠的職責相對太重,不易於擴展過於複雜的代碼結構。

2.3 工廠方法模式(Factory Method Pattern)

工廠方法模式是指定義一個建立對象的接口,但讓實現這個接口的類來決定實例化哪一個類,工廠方法讓類的實例化推遲到子類中進行。 在工廠方法模式中只關心所需產品對應的工廠,無意關注建立細節,而加入新的產品符合開閉原則。

工廠方式模式主要解決產品擴展的問題,根據單一職責原則將職能進行拆分,專人幹專事。語文課由語文工廠建立,數據課由數學工廠建立,對工廠自己作一個抽象。示例代碼以下:

建立ICourseFactory接口:

public interface ICourseFacotry {

    ICourse create();
}

再分別建立子工廠,ChineseCourseFactory類:

public class ChineseCourseFactory implements ICourseFacotry {
    @Override
    public ICourse create() {
        return new ChineseCourse();
    }
}

MathCourseFactory類:

public class MathCourseFactory implements ICourseFacotry {
    @Override
    public ICourse create() {
        return new MathCourse();
    }
}

執行main方法:

public static void main(String[] args) {
        ICourseFacotry chineseCourseFactory = new ChineseCourseFactory();
        ICourse chineseCourse = chineseCourseFactory.create();
        chineseCourse.study();
        
        ICourseFacotry mathCourseFactory = new MathCourseFactory();
        ICourse mathCourse = mathCourseFactory.create();
        mathCourse.study();
    }

2.4 抽象工廠模式(Abstract Factory Pattern)

抽象工廠模式是指提供一個建立一系列相關或相互依賴對象的接口,無需指定它們具體的類。 客戶端(應用層)不依賴於產品實例如何被建立、實現等細節,強調的是一系列相關的產品對象(屬於同一產品族)一塊兒使用建立對象須要大量重複的代碼。須要提供一個產品庫的類,全部的產品以一樣的接口出現,從而使客戶端不依賴於具體實現。

咱們仍是以課程的例子爲例,如今是新冠狀病毒疫情的時期,高中學生只能在家利用互聯網進行在線直播上課,每一個課程不只要提供課程的錄播視頻,並且還要提供老師的課堂筆記。至關於如今的業務變動爲同一個課程不單純是課程信息,同時要包括錄播視頻、課堂筆記等纔是一個完整的課程。在產品等級中增長兩個產品接口Ivideo錄播視頻和INote課堂筆記。

Ivideo接口:

public interface IVideo {

    void record();
}

INote接口:

public interface INote {

    void edit();
}

建立抽象工廠類CourseFactory類:

public interface CourseFactory {

    IVideo createVideo();

    INote createNote();
}

建立語文課產品族,語文課視頻的ChineseVideo類:

public class ChineseVideo implements IVideo {
    @Override
    public void record() {
        System.out.println("錄製語文課視頻!");
    }
}

建立語文課課堂筆記的ChineseNote類:

public class ChineseNote implements INote {
    @Override
    public void edit() {
        System.out.println("編寫語文課筆記!");
    }
}

建立語文課產品族的具體工廠類ChineseCourseFactory:

public class ChineseCourseFactory implements CourseFactory {
    @Override
    public IVideo createVideo() {
        return new ChineseVideo();
    }

    @Override
    public INote createNote() {
        return new ChineseNote();
    }
}

而後再建立數學產品,Math視頻MathVideo類:

public class MathVideo implements IVideo {
    @Override
    public void record() {
        System.out.println("錄製數學課視頻!");
    }
}

建立數學課堂筆記的MathNote類:

public class MathNote implements INote {
    @Override
    public void edit() {
        System.out.println("編寫數學課筆記!");
    }
}

建立數學課產品族的具體工廠類MathCourseFactory:

public class MathCourseFactory implements CourseFactory {
    @Override
    public IVideo createVideo() {
        return new MathVideo();
    }

    @Override
    public INote createNote() {
        return new MathNote();
    }

    public static void main(String[] args) {
        MathCourseFactory mathCourseFactory = new MathCourseFactory();
        mathCourseFactory.createNote().edit();
        mathCourseFactory.createVideo().record();
    }
}

上面的案例代碼完整的描述了兩個產品族語文課程和數學課程,也描述了兩個產品等級的視頻和課堂筆記。抽象工廠很是完美清晰地描述了這一層複雜的關係。若是咱們再升級擴展產品等級,將課堂做業也加入到課程中,咱們的代碼須要從抽象工廠,到具體的工廠都要進行調整,很顯然不符合開閉原則。因此抽象工廠也是有缺點的:

一、規定了全部可能被建立的產品集合,產品族中擴展新的產品困難,須要修改抽象工廠的接口。

二、增長了系統的抽象性和理解難度。

3、工廠模式總結

相關文章
相關標籤/搜索