2、設計模式總覽及工廠模式詳解

2、架構師內功心法之設計模式

2.架構師內功心法之設計模式

2.1.課程目標

一、經過對本章內容的學習,瞭解設計模式的由來。java

二、介紹設計模式能幫咱們解決哪些問題。git

三、剖析工廠模式的歷史由來及應用場景。設計模式

2.2.內容定位

不用設計模式並不是不能夠,可是用好設計模式能幫助咱們更好地解決實際問題,設計模式最重要的
是解耦。設計模式每天都在用,但本身卻無感知。咱們把設計模式做爲一個專題,主要是學習設計模式
是如何總結經驗的,把經驗爲本身所用。學設計模式也是鍛鍊將業務需求轉換技術實現的一種很是有效
的方式。微信

2.3.回顧軟件設計原則

設計原則 解釋
開閉原則 對擴展開放,對修改關閉
依賴倒置原則 經過抽象使各個類或者模塊不相互影響,實現鬆耦合。
單一職責原則 一個類、接口、方法只作一件事。
接口隔離原則 儘可能保證接口的純潔性,客戶端不該該依賴不須要的接口。
迪米特法則 又叫最少知道原則,一個類對其所依賴的類知道得越少越好。
里氏替換原則 子類能夠擴展父類的功能但不能改變父類原有的功能。
合成複用原則 儘可能使用對象組合、聚合,而不使用繼承關係達到代碼複用的目的。

2.4.設計模式總覽

寫出優雅的代碼架構

更好地重構項目app

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

Spring就是一個把設計模式用得淋漓盡致的經典框架,其實從類的命名就能看出來,我來一一列舉:ide

設計模式名稱 舉例
工廠模式 BeanFactory
裝飾器模式 BeanWrapper
代理模式 AopProxy
委派模式 DispatcherServlet
策略模式 HandlerMapping
適配器模式 HandlerAdapter
模板模式 JdbcTemplate
觀察者模式 ContextLoaderListener

咱們的課程中,會圍繞 Spring 的 IOC、AOP、MVC、JDBC
這樣的思路展開,根據其設計類型來設計講解順序:學習

類型 名稱 英文
建立型模式 工廠模式 Factory Pattern
單例模式 Singleton Pattern
原型模式 Prototype Pattern
結構型模式 適配器模式 Adapter Pattern
裝飾器模式 Decorator Patter
代理模式 Proxy Pattern
行爲性模式 策略模式 Strategy Pattern
模板模式 Template Pattern
委派模式 Delegate Pattern
觀察者模式 Observer Pattern

3.工廠模式詳解

3.1.工廠模式的歷史由來

原始社會自給自足(沒有工廠)、農耕社會小做坊(簡單工廠,民間酒
坊)、工業革命流水線(工廠方法,自產自銷)、現代產業鏈代工廠(抽象工廠,富士康)測試

3.2.簡單工廠模式

3.2.1.定義

簡單工廠模式(Simple Factory Pattern)是指由一個工廠對象決定建立出哪種產品類的實例,
但它不屬於GOF 23種設計模式。簡單工廠適用於工廠類負責建立的對象較少的場景,且客戶端只須要
傳入工廠類的參數,對於如何建立對象的邏輯不須要關心。

3.2.2.demo

public class SimpleFactoryTest {
    public static void main(String[] args) {
        CourseFactory factory = new CourseFactory();
        ICourse course = factory.create(JavaCourse.class);
        course.record();
    }
}

public class JavaCourse implements ICourse {
    public void record() {
        System.out.println("錄製Java課程");
    }
}

public class CourseFactory {
    public ICourse create(Class<? extends ICourse> clazz){
        // 反射
        try {
            if (null != clazz) {
                return clazz.newInstance();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

<img src="http://woshiamiaojiang.gitee.io/image-hosting/SimpleFactory.png" style="zoom: 50%;" />

3.2.3.源碼

  • Calendar.getInstance()
  • LoggerFactory.getLogger()

簡單工廠模式在 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) {
                // 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());
    }

3.2.4.優缺點

  • 優勢

    • 簡單
  • 缺點

    • 工廠類的職責相對太重,不易於擴展過於複雜的產品結構。

3.3.工廠方法模式

3.3.1.定義

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

3.3.2.demo

public class FactoryMethodTest {
    public static void main(String[] args) {
        // Python課程工廠
        ICourseFactory factory = new PythonCourseFactory();
        ICourse course = factory.create();
        course.record();

        // Java課程工廠
        factory = new JavaCourseFactory();
        course = factory.create();
        course.record();
    }
}

public class JavaCourseFactory implements ICourseFactory {
    public ICourse create() {
        return new JavaCourse();
    }
}

public interface ICourseFactory {
    ICourse create();
}

public class JavaCourse implements ICourse {
    public void record() {
        System.out.println("錄製Java課程");
    }
}

public interface ICourse {
    void record();
}

<img src="http://woshiamiaojiang.gitee.io/image-hosting/FactoryMethod.png" style="zoom: 50%;" />

3.3.3.源碼

ApplicationContext就是工廠方法模式

再來看看logback中工廠方法模式的應用,看看類圖就OK了:

<img src="http://woshiamiaojiang.gitee.io/image-hosting/FactoryMethod2.png" style="zoom: 50%;" />

3.3.4.優缺點

  • 工廠方法適用於如下場景:

    1. 建立對象須要大量重複的代碼。
    2. 客戶端(應用層)不依賴於產品類實例如何被建立、實現等細節。
    3. 一個類經過其子類來指定建立哪一個對象。
  • 工廠方法也有缺點:

    1. 類的個數容易過多,增長複雜度。
    2. 增長了系統的抽象性和理解難度。

3.4.抽象工廠模式

3.4.1.定義

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

講解抽象工廠以前,咱們要了解兩個概念產品等級結構產品族,看下面的圖:

<img src="http://woshiamiaojiang.gitee.io/image-hosting/image-20200225191403439.png" alt="image-20200225191403439" style="zoom:50%;" />

從上圖中看出有正方形,圓形和菱形三種圖形,相同顏色深淺的就表明同一個產品族,相同形狀的表明
同一個產品等級結構。一樣能夠從生活中來舉例,好比,美的電器生產多種家用電器。那麼上圖中,顏
色最深的正方形就表明美的洗衣機、顏色最深的圓形表明美的空調、顏色最深的菱形表明美的熱水器,
顏色最深的一排都屬於美的品牌,都是美的電器這個產品族。再看最右側的菱形,顏色最深的咱們指定
了表明美的熱水器,那麼第二排顏色稍微淺一點的菱形,表明海信的熱水器。同理,同一產品結構下還
有格力熱水器,格力空調,格力洗衣機。

再看下面的這張圖,最左側的小房子咱們就認爲具體的工廠,有美的工廠,有海信工廠,有格力工廠
每一個品牌的工廠都生產洗衣機熱水器空調

<img src="http://woshiamiaojiang.gitee.io/image-hosting/image-20200225191741688.png" alt="image-20200225191741688" style="zoom:50%;" />

3.4.2.demo

public class AbstractFactoryTest {
    public static void main(String[] args) {
        JavaCourseFactory factory = new JavaCourseFactory();
        factory.createNote().edit();
        factory.createVideo().record();
    }
}

/**
 * 抽象工廠CourseFactory類:
 * 抽象工廠是用戶的主入口
 * 在Spring中應用得最爲普遍的一種設計模式
 * 易於擴展
 */
public abstract class CourseFactory {
    public void init(){
        System.out.println("初始化基礎數據");
    }
    protected abstract INote createNote();
    protected abstract IVideo createVideo();
}

/**
 * 建立Java產品族的具體工廠JavaCourseFactory
 */
public class JavaCourseFactory extends CourseFactory {
    public INote createNote() {
        super.init();
        return new JavaNote();
    }
    public IVideo createVideo() {
        super.init();
        return new JavaVideo();
    }
}

/**
 * 建立Java產品族,Java視頻JavaVideo類:Java視頻
 */
public class JavaVideo implements IVideo {
    public void record() {
        System.out.println("錄製Java視頻");
    }
}

/**
 * 錄播視頻:IVideo接口
 */
public interface IVideo {
    void record();
}

/**
 * 擴展產品等級Java課堂筆記JavaNote類:Java筆記
 */
public class JavaNote implements INote {
    public void edit() {
        System.out.println("編寫Java筆記");
    }
}

/**
 * 課堂筆記:INote接口
 */
public interface INote {
    void edit();
}

// 建立Python產品族的具體工廠PythonCourseFactory省略。。。

<img src="http://woshiamiaojiang.gitee.io/image-hosting/AbstractFactory.png" style="zoom: 33%;" />

上面的代碼完整地描述了兩個產品族Java課程和Python課程,也描述了兩個產品等級視頻和手記。抽象工廠很是完美清晰地描述這樣一層複雜的關係。可是,不知道你們有沒有發現,若是咱們再繼續擴展
產品等級,將源碼 Source也加入到課程中,那麼咱們的代碼從抽象工廠,到具體工廠要所有調整,很顯然不符合開閉原則。

3.4.3.源碼

AbstractFactory

AnnotationApplicationContext

Xml

適合長時間不變更的場景

3.4.3.優缺點

抽象工廠缺點

  1. 規定了全部可能被建立的產品集合,產品族中擴展新的產品困難,須要修改抽象工廠的接口。
  2. 增長了系統的抽象性和理解難度。

3.5.簡單工廠 vs 工廠方法 vs 抽象工廠

簡單工廠:產品的工廠

工廠方法:工廠的工廠

抽象工廠:複雜產品的工廠

簡單工廠:工廠是一個實體類,內部直接根據邏輯建立對應的產品。

工廠方法:工廠首先有個接口定義規範。不一樣的產品使用不一樣的實體類工廠根據規範和需求建立對應的產品。這就是它們的區別。

工廠方法是生產一類產品,抽象工廠是生產一個產品族

3.6.做業

一、工廠類必定須要將構造方法私有化嗎,爲何?

不必定。抽象工廠類就不能,不然父類的私有構造方法就不能被子類調用。

二、用工廠模式設計支付業務場景,包含跨境支付,支付寶、微信、銀聯支付,並畫出類圖。

/**
 * description: 支付接口
 */
public interface IPay {
    /**
     * 支付方法
     */
    void pay();
}

/**
 * description: 支付寶支付
 */
public class AliPay implements IPay {
    public void pay() {
        System.out.println("支付寶支付");
    }
}

/**
 * description: 微信支付
 */
public class WxPay implements IPay {
    public void pay() {
        System.out.println("微信支付");
    }
}

/**
 * description: 銀聯支付
 */
public class UniPay implements IPay {
    public void pay() {
        System.out.println("銀聯支付");
    }
}

/**
 * description: 蘋果支付
 */
public class ApplePay implements IPay {
    public void pay() {
        System.out.println("蘋果支付");
    }
}

/**
 * description: 支付抽象工廠
 */
public abstract class AbstractPayFactory {
    public void init() {
        System.out.println("初始化基礎數據");
    }
}

/**
 * description: 國內支付
 */
public class ChinaPayFactory extends AbstractPayFactory {
    protected IPay createAliPay() {
        super.init();
        return new AliPay();
    }

    protected IPay createWxPay() {
        super.init();
        return new WxPay();
    }

    protected IPay createUniPay() {
        super.init();
        return new UniPay();
    }
}

/**
 * description: 國外支付
 */
public class ForeignPayFactory extends AbstractPayFactory {
    protected IPay createApplePay() {
        super.init();
        return new ApplePay();
    }
}

/**
 * description: 抽象工廠方法測試
 */
public class AbstractPayFactoryTest {
    public static void main(String[] args) {
        ChinaPayFactory chinaPayFactory = new ChinaPayFactory();
        chinaPayFactory.createAliPay().pay();
        chinaPayFactory.createWxPay().pay();
        chinaPayFactory.createUniPay().pay();

        ForeignPayFactory foreignPayFactory = new ForeignPayFactory();
        foreignPayFactory.createApplePay().pay();
    }
}

<img src="http://woshiamiaojiang.gitee.io/image-hosting/FactoryPatternHomework.png" style="zoom:50%;" />

相關文章
相關標籤/搜索