天天看點

二、設計模式總覽及工廠模式詳解二、架構師内功心法之設計模式

二、架構師内功心法之設計模式

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

2.1.課程目标

1、通過對本章内容的學習,了解設計模式的由來。

2、介紹設計模式能幫我們解決哪些問題。

3、剖析工廠模式的曆史由來及應用場景。

2.2.内容定位

不用設計模式并非不可以,但是用好設計模式能幫助我們更好地解決實際問題,設計模式最重要的

是解耦。設計模式天天都在用,但自己卻無感覺。我們把設計模式作為一個專題,主要是學習設計模式

是如何總結經驗的,把經驗為自己所用。學設計模式也是鍛煉将業務需求轉換技術實作的一種非常有效

的方式。

2.3.回顧軟體設計原則

設計原則 解釋
開閉原則 對擴充開放,對修改關閉
依賴倒置原則 通過抽象使各個類或者子產品不互相影響,實作松耦合。
單一職責原則 一個類、接口、方法隻做一件事。
接口隔離原則 盡量保證接口的純潔性,用戶端不應該依賴不需要的接口。
迪米特法則 又叫最少知道原則,一個類對其所依賴的類知道得越少越好。
裡氏替換原則 子類可以擴充父類的功能但不能改變父類原有的功能。
合成複用原則 盡量使用對象組合、聚合,而不使用繼承關系達到代碼複用的目的。

2.4.設計模式總覽

寫出優雅的代碼

更好地重構項目

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

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

設計模式名稱 舉例
工廠模式 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;
    }
}           
二、設計模式總覽及工廠模式詳解二、架構師内功心法之設計模式

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();
}           
二、設計模式總覽及工廠模式詳解二、架構師内功心法之設計模式

3.3.3.源碼

ApplicationContext就是工廠方法模式

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

二、設計模式總覽及工廠模式詳解二、架構師内功心法之設計模式

3.3.4.優缺點

  • 工廠方法适用于以下場景:
    1. 建立對象需要大量重複的代碼。
    2. 用戶端(應用層)不依賴于産品類執行個體如何被建立、實作等細節。
    3. 一個類通過其子類來指定建立哪個對象。
  • 工廠方法也有缺點:
    1. 類的個數容易過多,增加複雜度。
    2. 增加了系統的抽象性和了解難度。

3.4.抽象工廠模式

3.4.1.定義

抽象工廠模式(AbastractFactory Pattern)是指提供一個建立一系列相關或互相依賴對象的接口,無須指定他們具體的類。用戶端(應用層)不依賴于産品類執行個體如何被建立、實作等細節,強調的是一

系列相關的産品對象(屬于同一産品族)一起使用建立對象需要大量重複的代碼。需要提供一個産品類

的庫,所有的産品以同樣的接口出現,進而使用戶端不依賴于具體實作。

講解抽象工廠之前,我們要了解兩個概念産品等級結構和産品族,看下面的圖:

二、設計模式總覽及工廠模式詳解二、架構師内功心法之設計模式

從上圖中看出有正方形,圓形和菱形三種圖形,相同顔色深淺的就代表同一個産品族,相同形狀的代表

同一個産品等級結構。同樣可以從生活中來舉例,比如,美的電器生産多種家用電器。那麼上圖中,顔

色最深的正方形就代表美的洗衣機、顔色最深的圓形代表美的空調、顔色最深的菱形代表美的熱水器,

顔色最深的一排都屬于美的品牌,都是美的電器這個産品族。再看最右側的菱形,顔色最深的我們指定

了代表美的熱水器,那麼第二排顔色稍微淺一點的菱形,代表海信的熱水器。同理,同一産品結構下還

有格力熱水器,格力空調,格力洗衣機。

再看下面的這張圖,最左側的小房子我們就認為具體的工廠,有美的工廠,有海信工廠,有格力工廠。

每個品牌的工廠都生産洗衣機、熱水器和空調。

二、設計模式總覽及工廠模式詳解二、架構師内功心法之設計模式

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省略。。。           
二、設計模式總覽及工廠模式詳解二、架構師内功心法之設計模式

上面的代碼完整地描述了兩個産品族Java課程和Python課程,也描述了兩個産品等級視訊和手記。抽象工廠非常完美清晰地描述這樣一層複雜的關系。但是,不知道大家有沒有發現,如果我們再繼續擴充

産品等級,将源碼 Source也加入到課程中,那麼我們的代碼從抽象工廠,到具體工廠要全部調整,很顯然不符合開閉原則。

3.4.3.源碼

AbstractFactory

AnnotationApplicationContext

Xml

适合長時間不變動的場景

3.4.3.優缺點

抽象工廠缺點

  1. 規定了所有可能被建立的産品集合,産品族中擴充新的産品困難,需要修改抽象工廠的接口。

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

簡單工廠:産品的工廠

工廠方法:工廠的工廠

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

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

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

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

3.6.作業

1、工廠類一定需要将構造方法私有化嗎,為什麼?

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

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

/**
 * 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();
    }
}           
二、設計模式總覽及工廠模式詳解二、架構師内功心法之設計模式