設計模式面試題 (史上最全、持續更新、吐血推薦)


瘋狂創客圈 史上最強 面試題 系列
Java基礎
JVM面試題(史上最強、持續更新、吐血推薦) http://www.javashuo.com/article/p-vhnpdnhb-vd.html
Java基礎面試題(史上最全、持續更新、吐血推薦) https://www.cnblogs.com/crazymakercircle/p/14366081.html
死鎖面試題(史上最強、持續更新) http://www.javashuo.com/article/p-uyudvdol-vd.html
多線程、線程池、內置鎖 面試題 (史上最強、持續更新) http://www.javashuo.com/article/p-tecubweb-vd.html
JUC併發包與容器類 - 面試題(史上最強、持續更新) http://www.javashuo.com/article/p-xcyjkbgi-vd.html
SpringBoot - 面試題(史上最強、持續更新) http://www.javashuo.com/article/p-xjwdvxjl-vd.html
Linux面試題(史上最全、持續更新、吐血推薦) https://www.cnblogs.com/crazymakercircle/p/14366893.html
分佈式、高併發、設計模式、架構
Zookeeper 面試題(史上最強、持續更新) http://www.javashuo.com/article/p-fvxlillp-vd.html
Mysql 面試題(史上最強、持續更新) http://www.javashuo.com/article/p-xhmrgfwk-vd.html
Redis 面試題 - 收藏版(史上最強、持續更新) http://www.javashuo.com/article/p-vkywkhov-vd.html
SpringCloud 面試題 - 收藏版(史上最強、持續更新) http://www.javashuo.com/article/p-pptrkvmu-vd.html
Netty 面試題 (史上最強、持續更新) http://www.javashuo.com/article/p-guuqkocg-vd.html
消息隊列、RabbitMQ、Kafka、RocketMQ面試題 (史上最全、持續更新、吐血推薦) https://www.cnblogs.com/crazymakercircle/p/14367425.html
設計模式面試題 (史上最全、持續更新、吐血推薦) https://www.cnblogs.com/crazymakercircle/p/14367101.html
架構設計面試題 (史上最全、持續更新、吐血推薦) http://www.javashuo.com/article/p-dlpjqbmg-vd.html

國內著名電商面試題:你的項目中用到了哪些設計模式,如何使用?

讀完本文,你會有一個最佳的答案。html

設計模式基礎

什麼是設計模式

  • 設計模式,是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、程序的重用性。

爲何要學習設計模式

  • 看懂源代碼:若是你不懂設計模式去看Jdk、Spring、SpringMVC、IO等等等等的源碼,你會很迷茫,你會步履維艱
  • 看看前輩的代碼:你去個公司難道都是新項目讓你接手?頗有多是接盤的,前輩的開發難道不用設計模式?
  • 編寫本身的理想中的好代碼:我我的反正是這樣的,對於我本身開發的項目我會很認真,我對他比對我女友還好,把項目當成本身的兒子同樣

設計模式分類

img

  • 建立型模式,共五種:工廠方法模式、抽象工廠模式單例模式、建造者模式、原型模式。
  • 結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
  • 行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。

設計模式的六大原則

在這裏插入圖片描述

開放封閉原則(Open Close Principle)

  • 原則思想:儘可能經過擴展軟件實體來解決需求變化,而不是經過修改已有的代碼來完成變化
  • 描述:一個軟件產品在生命週期內,都會發生變化,既然變化是一個既定的事實,咱們就應該在設計的時候儘可能適應這些變化,以提升項目的穩定性和靈活性。
  • 優勢:單一原則告訴咱們,每一個類都有本身負責的職責,里氏替換原則不能破壞繼承關係的體系。

里氏代換原則(Liskov Substitution Principle)

  • 原則思想:使用的基類能夠在任何地方使用繼承的子類,完美的替換基類。
  • 大概意思是:子類能夠擴展父類的功能,但不能改變父類原有的功能。子類能夠實現父類的抽象方法,但不能覆蓋父類的非抽象方法,子類中能夠增長本身特有的方法。
  • 優勢:增長程序的健壯性,即便增長了子類,原有的子類還能夠繼續運行,互不影響。

依賴倒轉原則(Dependence Inversion Principle)

  • 依賴倒置原則的核心思想是面向接口編程.
  • 依賴倒轉原則要求咱們在程序代碼中傳遞參數時或在關聯關係中,儘可能引用層次高的抽象層類,
  • 這個是開放封閉原則的基礎,具體內容是:對接口編程,依賴於抽象而不依賴於具體。

接口隔離原則(Interface Segregation Principle)

  • 這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。仍是一個下降類之間的耦合度的意思,從這兒咱們看出,其實設計模式就是一個軟件的設計思想,從大型軟件架構出發,爲了升級和維護方便。因此上文中屢次出現:下降依賴,下降耦合。
  • 例如:支付類的接口和訂單類的接口,須要把這倆個類別的接口變成倆個隔離的接口

迪米特法則(最少知道原則)(Demeter Principle)

  • 原則思想:一個對象應當對其餘對象有儘量少地瞭解,簡稱類間解耦
  • 大概意思就是一個類儘可能減小本身對其餘對象的依賴,原則是低耦合,高內聚,只有使各個模塊之間的耦合儘可能的低,才能提升代碼的複用率。
  • 優勢:低耦合,高內聚。

單一職責原則(Principle of single responsibility)

  • 原則思想:一個方法只負責一件事情。
  • 描述:單一職責原則很簡單,一個方法 一個類只負責一個職責,各個職責的程序改動,不影響其它程序。 這是常識,幾乎全部程序員都會遵循這個原則。
  • 優勢:下降類和類的耦合,提升可讀性,增長可維護性和可拓展性,下降可變性的風險。

J2EE中使用了哪些設計模式?

Structural(結構模式)

Adapter:java

  • java.util.Arrays#asList()
  • javax.swing.JTable(TableModel)
  • java.io.InputStreamReader(InputStream)
  • java.io.OutputStreamWriter(OutputStream)
  • javax.xml.bind.annotation.adapters.XmlAdapter#marshal()
  • javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()

Bridge:
把抽象和實現解藕,因而接口和實現可在徹底獨立開來。程序員

  • AWT (提供了抽象層映射於實際的操做系統)
  • JDBC

Composite:
讓使用者把單獨的對象和組合對象混用。web

  • javax.swing.JComponent#add(Component)
  • java.awt.Container#add(Component)
  • java.util.Map#putAll(Map)
  • java.util.List#addAll(Collection)
  • java.util.Set#addAll(Collection)

Decorator:
爲一個對象動態的加上一系列的動做,而不須要由於這些動做的不一樣而產生大量的繼承類。這個模式在JDK中幾乎無處不在,因此,下面的列表只是一些典型的。面試

  • java.io.BufferedInputStream(InputStream)
  • java.io.DataInputStream(InputStream)
  • java.io.BufferedOutputStream(OutputStream)
  • java.util.zip.ZipOutputStream(OutputStream)
  • java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap

Facade:
用一個簡單的接口包狀一組組件,接口,抽象或是子系統。算法

  • java.lang.Class
  • javax.faces.webapp.FacesServlet

Flyweight:
有效率地存儲大量的小的對象。spring

  • java.lang.Integer#valueOf(int)
  • java.lang.Boolean#valueOf(boolean)
  • java.lang.Byte#valueOf(byte)
  • java.lang.Character#valueOf(char)

Proxy:
用一個簡單的對象來代替一個複雜的對象。sql

  • java.lang.reflect.Proxy
  • RMI

Creational(建立模式)

Abstract factory:**數據庫

  • java.util.Calendar#getInstance()
  • java.util.Arrays#asList()
  • java.util.ResourceBundle#getBundle()
  • java.sql.DriverManager#getConnection()
  • java.sql.Connection#createStatement()
  • java.sql.Statement#executeQuery()
  • java.text.NumberFormat#getInstance()
  • javax.xml.transform.TransformerFactory#newInstance()

Builder:
主要用來簡化一個複雜的對象的建立。這個模式也能夠用來實現一個 Fluent Interface編程

  • java.lang.StringBuilder#append()
  • java.lang.StringBuffer#append()
  • java.sql.PreparedStatement
  • javax.swing.GroupLayout.Group#addComponent()

Factory:
簡單來講,按照需求返回一個類型的實例。

  • java.lang.Proxy#newProxyInstance()
  • java.lang.Object#toString()
  • java.lang.Class#newInstance()
  • java.lang.reflect.Array#newInstance()
  • java.lang.reflect.Constructor#newInstance()
  • java.lang.Boolean#valueOf(String)
  • java.lang.Class#forName()

Prototype:
使用本身的實例建立另外一個實例。有時候,建立一個實例而後再把已有實例的值拷貝過去,是一個很複雜的動做。因此,使用這個模式能夠避免這樣的複雜性。

  • java.lang.Object#clone()
  • java.lang.Cloneable

Singleton:
只容許一個實例。在 Effective Java中建議使用Emun.

  • java.lang.Runtime#getRuntime()
  • java.awt.Toolkit#getDefaultToolkit()
  • java.awt.GraphicsEnvironment#getLocalGraphicsEnvironment()
  • java.awt.Desktop#getDesktop()

Behavioral(行爲模式)

Chain of responsibility:
把一個對象在一個連接傳遞直到被處理。在這個鏈上的全部的對象有相同的接口(抽象類)但卻有不一樣的實現。

  • java.util.logging.Logger#log()
  • javax.servlet.Filter#doFilter()

Command:
把一個或一些命令封裝到一個對象中。

  • java.lang.Runnable
  • javax.swing.Action

Interpreter:
一個語法解釋器的模式。

  • java.util.Pattern
  • java.text.Normalizer
  • java.text.Format

Iterator:
提供一種一致的方法來順序遍歷一個容器中的全部元素。

  • java.util.Iterator
  • java.util.Enumeration

Mediator:
用來減小對象單的直接通信的依賴關係。使用一箇中間類來管理消息的方向。

  • java.util.Timer
  • java.util.concurrent.Executor#execute()
  • java.util.concurrent.ExecutorService#submit()
  • java.lang.reflect.Method#invoke()

Memento:
給一個對象的狀態作一個快照。Date類在內部使用了一個long型來作這個快照。

  • java.util.Date
  • java.io.Serializable

Null Object:
這個模式用來解決若是一個Collection中沒有元素的狀況。

  • java.util.Collections#emptyList()
  • java.util.Collections#emptyMap()
  • java.util.Collections#emptySet()

Observer:
容許一個對象向全部的偵聽的對象廣播本身的消息或事件。

  • java.util.EventListener
  • javax.servlet.http.HttpSessionBindingListener
  • javax.servlet.http.HttpSessionAttributeListener
  • javax.faces.event.PhaseListener

State:
這個模式容許你能夠在運行時很容易地根據自身內部的狀態改變對象的行爲。

  • java.util.Iterator
  • javax.faces.lifecycle.LifeCycle#execute()

Strategy:
定義一組算法,並把其封裝到一個對象中。而後在運行時,能夠靈活的使用其中的一個算法。

  • java.util.Comparator#compare()
  • javax.servlet.http.HttpServlet
  • javax.servlet.Filter#doFilter()

Template method:
容許子類重載部分父類而不須要徹底重寫。

  • java.util.Collections#sort()
  • java.io.InputStream#skip()
  • java.io.InputStream#read()
  • java.util.AbstractList#indexOf()

Visitor:

做用於某個對象羣中各個對象的操做. 它可使你在不改變這些對象自己的狀況下,定義做用於這些對象的新操做.

  • javax.lang.model.element.Element 和javax.lang.model.element.ElementVisitor
  • javax.lang.model.type.TypeMirror 和javax.lang.model.type.TypeVisitor

mvc模式

MVC 模式表明 Model-View-Controller(模型-視圖-控制器) 模式。這種模式用於應用程序的分層開發。

  • Model(模型) - 模型表明一個存取數據的對象或 JAVA POJO。它也能夠帶有邏輯,在數據變化時更新控制器。
  • View(視圖) - 視圖表明模型包含的數據的可視化。
  • Controller(控制器) - 控制器做用於模型和視圖上。它控制數據流向模型對象,並在數據變化時更新視圖。它使視圖與模型分離開。

img

攔截過濾器模式

攔截過濾器模式(Intercepting Filter Pattern)用於對應用程序的請求或響應作一些預處理/後處理。定義過濾器,並在把請求傳給實際目標應用程序以前應用在請求上。過濾器能夠作認證/受權/記錄日誌,或者跟蹤請求,而後把請求傳給相應的處理程序。如下是這種設計模式的實體。

  • 過濾器(Filter) - 過濾器在請求處理程序執行請求以前或以後,執行某些任務。
  • 過濾器鏈(Filter Chain) - 過濾器鏈帶有多個過濾器,並在 Target 上按照定義的順序執行這些過濾器。
  • Target - Target 對象是請求處理程序。
  • 過濾管理器(Filter Manager) - 過濾管理器管理過濾器和過濾器鏈。
  • 客戶端(Client) - Client 是向 Target 對象發送請求的對象。

實現

咱們將建立 FilterChainFilterManagerTargetClient 做爲表示實體的各類對象。AuthenticationFilterDebugFilter 表示實體過濾器。

InterceptingFilterDemo,咱們的演示類使用 Client 來演示攔截過濾器設計模式。

攔截過濾器模式

步驟 1

建立過濾器接口 Filter。

Filter.java

public interface Filter {

    void execute(String request);

}
12345

步驟 2

建立實體過濾器。

AuthenticationFilter.java

public class AuthenticationFilter implements Filter {

    @Override
    public void execute(String request){
        System.out.println("Authenticating request: " + request);
    }

}

DebugFilter.java

public class DebugFilter implements Filter {

    @Override
    public void execute(String request){
        System.out.println("request log: " + request);
    }

}

步驟 3

建立 Target。

Target.java

*/
public class Target {

    public void execute(String request){
        System.out.println("Executing request: " + request);
    }

}

步驟 4

建立過濾器鏈。

FilterChain.java

public class FilterChain {

    private List<Filter> filters = new ArrayList();
    private Target target;

    public void addFilter(Filter filter){
        filters.add(filter);
    }

    public void execute(String request){
        for (Filter filter : filters) {
            filter.execute(request);
        }
        target.execute(request);
    }

    public void setTarget(Target target){
        this.target = target;
    }

}

步驟 5

建立過濾管理器。

FilterManager.java

public class FilterManager {

    FilterChain filterChain;

    public FilterManager(Target target){
        filterChain = new FilterChain();
        filterChain.setTarget(target);
    }
    public void setFilter(Filter filter){
        filterChain.addFilter(filter);
    }

    public void filterRequest(String request){
        filterChain.execute(request);
    }

}

步驟 6

建立客戶端 Client。

Client.java

public class Client {

    FilterManager filterManager;

    public void setFilterManager(FilterManager filterManager){
        this.filterManager = filterManager;
    }

    public void sendRequest(String request){
        filterManager.filterRequest(request);
    }

}

步驟 7

使用 Client 來演示攔截過濾器設計模式。

InterceptingFilterDemo.java

public class InterceptingFilterPatternDemo {

    public static void main(String[] args) {
        FilterManager filterManager = new FilterManager(new Target());
        filterManager.setFilter(new AuthenticationFilter());
        filterManager.setFilter(new DebugFilter());

        Client client = new Client();
        client.setFilterManager(filterManager);
        client.sendRequest("HOME");
    }

}

步驟 8

驗證輸出。

Authenticating request: HOME
request log: HOME
Executing request: HOME

單例模式

1.什麼是單例

  • 保證一個類只有一個實例,而且提供一個訪問該全局訪問點

2.那些地方用到了單例模式

  1. 網站的計數器,通常也是採用單例模式實現,不然難以同步。
  2. 應用程序的日誌應用,通常都是單例模式實現,只有一個實例去操做纔好,不然內容很差追加顯示。
  3. 多線程的線程池的設計通常也是採用單例模式,由於線程池要方便對池中的線程進行控制
  4. Windows的(任務管理器)就是很典型的單例模式,他不能打開倆個
  5. windows的(回收站)也是典型的單例應用。在整個系統運行過程當中,回收站只維護一個實例。

3.單例優缺點

優勢:

  1. 在單例模式中,活動的單例只有一個實例,對單例類的全部實例化獲得的都是相同的一個實例。這樣就防止其它對象對本身的實例化,確保全部的對象都訪問一個實例
  2. 單例模式具備必定的伸縮性,類本身來控制實例化進程,類就在改變實例化進程上有相應的伸縮性。
  3. 提供了對惟一實例的受控訪問。
  4. 因爲在系統內存中只存在一個對象,所以能夠節約系統資源,當須要頻繁建立和銷燬的對象時單例模式無疑能夠提升系統的性能。
  5. 容許可變數目的實例。
  6. 避免對共享資源的多重佔用。

缺點:

  1. 不適用於變化的對象,若是同一類型的對象老是要在不一樣的用例場景發生變化,單例就會引發數據的錯誤,不能保存彼此的狀態。
  2. 因爲單利模式中沒有抽象層,所以單例類的擴展有很大的困難。
  3. 單例類的職責太重,在必定程度上違背了「單一職責原則」。
  4. 濫用單例將帶來一些負面問題,如爲了節省資源將數據庫鏈接池對象設計爲的單例類,可能會致使共享鏈接池對象的程序過多而出現鏈接池溢出;若是實例化的對象長時間不被利用,系統會認爲是垃圾而被回收,這將致使對象狀態的丟失。

4.單例模式使用注意事項:

  1. 使用時不能用反射模式建立單例,不然會實例化一個新的對象
  2. 使用懶單例模式時注意線程安全問題
  3. 餓單例模式和懶單例模式構造方法都是私有的,於是是不能被繼承的,有些單例模式能夠被繼承(如登記式模式)

5.單例防止反射漏洞攻擊

private static boolean flag = false;

private Singleton() {

	if (flag == false) {
		flag = !flag;
	} else {
		throw new RuntimeException("單例模式被侵犯!");
	}
}

public static void main(String[] args) {

}

6.如何選擇單例建立方式

  • 若是不須要延遲加載單例,可使用枚舉或者餓漢式,相對來講枚舉性好於餓漢式。
    若是須要延遲加載,可使用靜態內部類或者懶漢式,相對來講靜態內部類好於懶韓式。
    最好使用餓漢式

7.單例建立方式

(主要使用懶漢和懶漢式)

  1. 餓漢式:類初始化時,會當即加載該對象,線程天生安全,調用效率高。
  2. 懶漢式: 類初始化時,不會初始化該對象,真正須要使用的時候纔會建立該對象,具有懶加載功能。
  3. 靜態內部方式:結合了懶漢式和餓漢式各自的優勢,真正須要對象的時候纔會加載,加載類是線程安全的。
  4. 枚舉單例: 使用枚舉實現單例模式 優勢:實現簡單、調用效率高,枚舉自己就是單例,由jvm從根本上提供保障!避免經過反射和反序列化的漏洞, 缺點沒有延遲加載。
  5. 雙重檢測鎖方式 (由於JVM本質重排序的緣由,可能會初始化屢次,不推薦使用)

1.餓漢式

  1. 餓漢式:類初始化時,會當即加載該對象,線程天生安全,調用效率高。
package com.lijie;

//餓漢式
public class Demo1 {

    // 類初始化時,會當即加載該對象,線程安全,調用效率高
    private static Demo1 demo1 = new Demo1();

    private Demo1() {
        System.out.println("私有Demo1構造參數初始化");
    }

    public static Demo1 getInstance() {
        return demo1;
    }

    public static void main(String[] args) {
        Demo1 s1 = Demo1.getInstance();
        Demo1 s2 = Demo1.getInstance();
        System.out.println(s1 == s2);
    }
}

2.懶漢式

  1. 懶漢式: 類初始化時,不會初始化該對象,真正須要使用的時候纔會建立該對象,具有懶加載功能。
package com.lijie;

//懶漢式
public class Demo2 {

    //類初始化時,不會初始化該對象,真正須要使用的時候纔會建立該對象。
    private static Demo2 demo2;

    private Demo2() {
        System.out.println("私有Demo2構造參數初始化");
    }

    public synchronized static Demo2 getInstance() {
        if (demo2 == null) {
            demo2 = new Demo2();
        }
        return demo2;
    }

    public static void main(String[] args) {
        Demo2 s1 = Demo2.getInstance();
        Demo2 s2 = Demo2.getInstance();
        System.out.println(s1 == s2);
    }
}

3.靜態內部類

  1. 靜態內部方式:結合了懶漢式和餓漢式各自的優勢,真正須要對象的時候纔會加載,加載類是線程安全的。
package com.lijie;

// 靜態內部類方式
public class Demo3 {

    private Demo3() {
        System.out.println("私有Demo3構造參數初始化");
    }

    public static class SingletonClassInstance {
        private static final Demo3 DEMO_3 = new Demo3();
    }

    // 方法沒有同步
    public static Demo3 getInstance() {
        return SingletonClassInstance.DEMO_3;
    }

    public static void main(String[] args) {
        Demo3 s1 = Demo3.getInstance();
        Demo3 s2 = Demo3.getInstance();
        System.out.println(s1 == s2);
    }
}

4.枚舉單例式

  1. 枚舉單例: 使用枚舉實現單例模式 優勢:實現簡單、調用效率高,枚舉自己就是單例,由jvm從根本上提供保障!避免經過反射和反序列化的漏洞, 缺點沒有延遲加載。
package com.lijie;

//使用枚舉實現單例模式 優勢:實現簡單、枚舉自己就是單例,由jvm從根本上提供保障!避免經過反射和反序列化的漏洞 缺點沒有延遲加載
public class Demo4 {

    public static Demo4 getInstance() {
        return Demo.INSTANCE.getInstance();
    }

    public static void main(String[] args) {
        Demo4 s1 = Demo4.getInstance();
        Demo4 s2 = Demo4.getInstance();
        System.out.println(s1 == s2);
    }

    //定義枚舉
	private static enum Demo {
		INSTANCE;
		// 枚舉元素爲單例
		private Demo4 demo4;

		private Demo() {
			System.out.println("枚舉Demo私有構造參數");
			demo4 = new Demo4();
		}

		public Demo4 getInstance() {
			return demo4;
		}
	}
}

5.雙重檢測鎖方式

  1. 雙重檢測鎖方式 (由於JVM本質重排序的緣由,可能會初始化屢次,不推薦使用)
package com.lijie;

//雙重檢測鎖方式
public class Demo5 {

	private static Demo5 demo5;

	private Demo5() {
		System.out.println("私有Demo4構造參數初始化");
	}

	public static Demo5 getInstance() {
		if (demo5 == null) {
			synchronized (Demo5.class) {
				if (demo5 == null) {
					demo5 = new Demo5();
				}
			}
		}
		return demo5;
	}

	public static void main(String[] args) {
		Demo5 s1 = Demo5.getInstance();
		Demo5 s2 = Demo5.getInstance();
		System.out.println(s1 == s2);
	}
}

8 單例模式懶漢式和餓漢式有哪些區別?(美團)

單例模式是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
這種模式涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。

注意:
一、單例類只能有一個實例。
二、單例類必須本身建立本身的惟一實例。
三、單例類必須給全部其餘對象提供這一實例。

明肯定義後,看一下代碼:

餓漢式

public class SingletonEH {
    /**
     *是否 Lazy 初始化:否
     *是否多線程安全:是
     *實現難度:易
     *描述:這種方式比較經常使用,但容易產生垃圾對象。
     *優勢:沒有加鎖,執行效率會提升。
     *缺點:類加載時就初始化,浪費內存。
     *它基於 classloder 機制避免了多線程的同步問題,
     * 不過,instance 在類裝載時就實例化,雖然致使類裝載的緣由有不少種,
    * 在單例模式中大多數都是調用 getInstance 方法,
     * 可是也不能肯定有其餘的方式(或者其餘的靜態方法)致使類裝載,
     * 這時候初始化 instance 顯然沒有達到 lazy loading 的效果。
     */
    private static SingletonEH instance = new SingletonEH();
    private SingletonEH (){}
    public static SingletonEH getInstance() {
        System.out.println("instance:"+instance);
        System.out.println("加載餓漢式....");
        return instance;
    }
}

餓漢就是類一旦加載,就把單例初始化完成,保證getInstance的時候,單例是已經存在的了。

懶漢式

public class SingletonLH {
    /**
     *是否 Lazy 初始化:是
     *是否多線程安全:否
     *實現難度:易
     *描述:這種方式是最基本的實現方式,這種實現最大的問題就是不支持多線程。由於沒有加鎖 synchronized,因此嚴格意義上它並不算單例模式。
     *這種方式 lazy loading 很明顯,不要求線程安全,在多線程不能正常工做。
     */
    private static SingletonLH instance;
    private SingletonLH (){}
 
    public static SingletonLH getInstance() {
        if (instance == null) {
            instance = new SingletonLH();
        }
        return instance;
    }
}

而懶漢比較懶,只有當調用getInstance的時候,纔回去初始化這個單例。

一、線程安全:
餓漢式天生就是線程安全的,能夠直接用於多線程而不會出現問題,
懶漢式自己是非線程安全的,爲了實現線程安全有幾種寫法。
例:

public class SingletonLHsyn {
    /**
     *是否 Lazy 初始化:是
     *是否多線程安全:是
     *實現難度:易
     *描述:這種方式具有很好的 lazy loading,可以在多線程中很好的工做,可是,效率很低,99% 狀況下不須要同步。
     *優勢:第一次調用才初始化,避免內存浪費。
     *缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。
     *getInstance() 的性能對應用程序不是很關鍵(該方法使用不太頻繁)。
     */
    private static SingletonLHsyn instance;
    private SingletonLHsyn (){}
    public static synchronized SingletonLHsyn getInstance() {
        if (instance == null) {
            instance = new SingletonLHsyn();
        }
        return instance;
    }
}

二、資源加載和性能:
餓漢式在類建立的同時就實例化一個靜態對象出來,無論以後會不會使用這個單例,都會佔據必定的內存,可是相應的,在第一次調用時速度也會更快,由於其資源已經初始化完成。
而懶漢式顧名思義,會延遲加載,在第一次使用該單例的時候纔會實例化對象出來,第一次調用時要作初始化,若是要作的工做比較多,性能上會有些延遲,以後就和餓漢式同樣了。

  • 意圖:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

  • 主要解決:一個全局使用的類頻繁地建立與銷燬。

  • 什麼時候使用:當您想控制實例數目,節省系統資源的時候。

  • 如何解決:判斷系統是否已經有這個單例,若是有則返回,若是沒有則建立。

  • 關鍵代碼:構造函數是私有的。

  • 應用實例:

    一、一個黨只能有一個主席。
    二、Windows是多進程多線程的,在操做一個文件的時候,就不可避免地出現多個進程或線程同時操做一個文件的現象,因此全部文件的處理必須經過惟一的實例來進行。
    三、一些設備管理器經常設計爲單例模式,好比一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。

  • 優勢:

    一、在內存裏只有一個實例,減小了內存的開銷,尤爲是頻繁的建立和銷燬實例(好比管理學院首頁頁面緩存)。
    二、避免對資源的多重佔用(好比寫文件操做)。
    缺點:沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。

  • 使用場景:

    一、要求生產惟一序列號。
    二、WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
    三、建立的一個對象須要消耗的資源過多,好比 I/O 與數據庫的鏈接等。 注意事項:getInstance() 方法中須要使用同步鎖,synchronized (Singleton.class) 防止多線程同時進入形成instance 被屢次實例化。

工廠模式

1.什麼是工廠模式

  • 它提供了一種建立對象的最佳方式。在工廠模式中,咱們在建立對象時不會對客戶端暴露建立邏輯,而且是經過使用一個共同的接口來指向新建立的對象。實現了建立者和調用者分離,工廠模式分爲簡單工廠、工廠方法、抽象工廠模式

2.工廠模式好處

  • 工廠模式是咱們最經常使用的實例化對象模式了,是用工廠方法代替new操做的一種模式。
  • 利用工廠模式能夠下降程序的耦合性,爲後期的維護修改提供了很大的便利。
  • 將選擇實現類、建立對象統一管理和控制。從而將調用者跟咱們的實現類解耦。

3.爲何要學習工廠設計模式

  • 不知道大家面試題問到過源碼沒有,你知道Spring的源碼嗎,MyBatis的源碼嗎,等等等
    若是你想學習不少框架的源碼,或者你想本身開發本身的框架,就必須先掌握設計模式(工廠設計模式用的是很是很是普遍的)

4.Spring開發中的工廠設計模式

1.Spring IOC

  • 看過Spring源碼就知道,在Spring IOC容器建立bean的過程是使用了工廠設計模式
  • Spring中不管是經過xml配置仍是經過配置類仍是註解進行建立bean,大部分都是經過簡單工廠來進行建立的。
  • 當容器拿到了beanName和class類型後,動態的經過反射建立具體的某個對象,最後將建立的對象放到Map中。

2.爲何Spring IOC要使用工廠設計模式建立Bean呢

  • 在實際開發中,若是咱們A對象調用B,B調用C,C調用D的話咱們程序的耦合性就會變高。(耦合大體分爲類與類之間的依賴,方法與方法之間的依賴。)
  • 在好久之前的三層架構編程時,都是控制層調用業務層,業務層調用數據訪問層時,都是是直接new對象,耦合性大大提高,代碼重複量很高,對象滿天飛
  • 爲了不這種狀況,Spring使用工廠模式編程,寫一個工廠,由工廠建立Bean,之後咱們若是要對象就直接管工廠要就能夠,剩下的事情不歸咱們管了。Spring IOC容器的工廠中有個靜態的Map集合,是爲了讓工廠符合單例設計模式,即每一個對象只生產一次,生產出對象後就存入到Map集合中,保證了實例不會重複影響程序效率。

5.工廠模式分類

  • 工廠模式分爲簡單工廠、工廠方法、抽象工廠模式
簡單工廠 :用來生產同一等級結構中的任意產品。(不支持拓展增長產品)
工廠方法 :用來生產同一等級結構中的固定產品。(支持拓展增長產品)   
抽象工廠 :用來生產不一樣產品族的所有產品。(不支持拓展增長產品;支持增長產品族)
123
我下面來使用代碼演示一下:

5.1 簡單工廠模式

什麼是簡單工廠模式

  • 簡單工廠模式至關因而一個工廠中有各類產品,建立在一個類中,客戶無需知道具體產品的名稱,只須要知道產品類所對應的參數便可。可是工廠的職責太重,並且當類型過多時不利於系統的擴展維護。

代碼演示:

  1. 建立工廠
package com.lijie;

public interface Car {
	public void run();
}
  1. 建立工廠的產品(寶馬)
package com.lijie;

public class Bmw implements Car {
	public void run() {
		System.out.println("我是寶馬汽車...");
	}
}
  1. 建立工另一種產品(奧迪)
package com.lijie;

public class AoDi implements Car {
	public void run() {
		System.out.println("我是奧迪汽車..");
	}
}
  1. 建立核心工廠類,由他決定具體調用哪產品
package com.lijie;

public class CarFactory {

	 public static Car createCar(String name) {
		if ("".equals(name)) {
             return null;
		}
		if(name.equals("奧迪")){
			return new AoDi();
		}
		if(name.equals("寶馬")){
			return new Bmw();
		}
		return null;
	}
}
  1. 演示建立工廠的具體實例
package com.lijie;

public class Client01 {

	public static void main(String[] args) {
		Car aodi  =CarFactory.createCar("奧迪");
		Car bmw  =CarFactory.createCar("寶馬");
		aodi.run();
		bmw.run();
	}
}

單工廠的優勢/缺點

  • 優勢:簡單工廠模式可以根據外界給定的信息,決定究竟應該建立哪一個具體類的對象。明確區分了各自的職責和權力,有利於整個軟件體系結構的優化。
  • 缺點:很明顯工廠類集中了全部實例的建立邏輯,容易違反GRASPR的高內聚的責任分配原則

5.2 工廠方法模式

什麼是工廠方法模式

  • 工廠方法模式Factory Method,又稱多態性工廠模式。在工廠方法模式中,核心的工廠類再也不負責全部的產品的建立,而是將具體建立的工做交給子類去作。該核心類成爲一個抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸哪個產品類應當被實例化這種細節

代碼演示:

  1. 建立工廠
package com.lijie;

public interface Car {
	public void run();
}
  1. 建立工廠方法調用接口(全部的產品須要new出來必須繼承他來實現方法)
package com.lijie;

public interface CarFactory {

	Car createCar();

}
  1. 建立工廠的產品(奧迪)
package com.lijie;

public class AoDi implements Car {
	public void run() {
		System.out.println("我是奧迪汽車..");
	}
}
  1. 建立工廠另一種產品(寶馬)
package com.lijie;

public class Bmw implements Car {
	public void run() {
		System.out.println("我是寶馬汽車...");
	}
}
  1. 建立工廠方法調用接口的實例(奧迪)
package com.lijie;

public class AoDiFactory implements CarFactory {

	public Car createCar() {
	
		return new AoDi();
	}
}
  1. 建立工廠方法調用接口的實例(寶馬)
package com.lijie;

public class BmwFactory implements CarFactory {

	public Car createCar() {

		return new Bmw();
	}

}
  1. 演示建立工廠的具體實例
package com.lijie;

public class Client {

	public static void main(String[] args) {
		Car aodi = new AoDiFactory().createCar();
		Car jili = new BmwFactory().createCar();
		aodi.run();
		jili.run();
	}
}

5.3 抽象工廠模式

什麼是抽象工廠模式

  • 抽象工廠簡單地說是工廠的工廠,抽象工廠能夠建立具體工廠,由具體工廠來產生具體產品。
    在這裏插入圖片描述
    代碼演示:
  1. 建立第一個子工廠,及實現類
package com.lijie;

//汽車
public interface Car {
	   void run();
}

 class CarA implements Car{

	public void run() {
		System.out.println("寶馬");
	}
	
}
 class CarB implements Car{

	public void run() {
		System.out.println("摩拜");
	}
	
}
  1. 建立第二個子工廠,及實現類
package com.lijie;

//發動機
public interface Engine {

    void run();

}

class EngineA implements Engine {

    public void run() {
        System.out.println("轉的快!");
    }

}

class EngineB implements Engine {

    public void run() {
        System.out.println("轉的慢!");
    }

}
  1. 建立一個總工廠,及實現類(由總工廠的實現類決定調用那個工廠的那個實例)
package com.lijie;

public interface TotalFactory {
	// 建立汽車
	Car createChair();
	// 建立發動機
	Engine createEngine();
}

//總工廠實現類,由他決定調用哪一個工廠的那個實例
class TotalFactoryReally implements TotalFactory {

	public Engine createEngine() {

		return new EngineA();
	}

	public Car createChair() {

		return new CarA();
	}
}
  1. 運行測試
package com.lijie;

public class Test {

    public static void main(String[] args) {
        TotalFactory totalFactory2 = new TotalFactoryReally();
        Car car = totalFactory2.createChair();
        car.run();

        TotalFactory totalFactory = new TotalFactoryReally();
        Engine engine = totalFactory.createEngine();
        engine.run();
    }
}

代理模式

1.什麼是代理模式

  • 經過代理控制對象的訪問,能夠在這個對象調用方法以前、調用方法以後去處理/添加新的功能。(也就是AO的P微實現)
  • 代理在原有代碼乃至原業務流程都不修改的狀況下,直接在業務流程中切入新代碼,增長新功能,這也和Spring的(面向切面編程)很類似

2.代理模式應用場景

  • Spring AOP、日誌打印、異常處理、事務控制、權限控制等

3.代理的分類

  • 靜態代理(靜態定義代理類)
  • 動態代理(動態生成代理類,也稱爲Jdk自帶動態代理)
  • Cglib 、javaassist(字節碼操做庫)

4.三種代理的區別

  1. 靜態代理:簡單代理模式,是動態代理的理論基礎。常見使用在代理模式
  2. jdk動態代理:使用反射完成代理。須要有頂層接口才能使用,常見是mybatis的mapper文件是代理。
  3. cglib動態代理:也是使用反射完成代理,能夠直接代理類(jdk動態代理不行),使用字節碼技術,不能對 final類進行繼承。(須要導入jar包)

5.用代碼演示三種代理

5.1.靜態代理

什麼是靜態代理

  • 由程序員建立或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就肯定了。

代碼演示:

  • 有一段這樣的代碼:(如何能在不修改UserDao接口類的狀況下開事務和關閉事務呢)
package com.lijie;

//接口類
public class UserDao{
	public void save() {
		System.out.println("保存數據方法");
	}
}
12345678
package com.lijie;

//運行測試類
public  class Test{
	public static void main(String[] args) {
		UserDao userDao = new UserDao();
		userDao.save();
	}
}

修改代碼,添加代理類

package com.lijie;

//代理類
public class UserDaoProxy extends UserDao {
	private UserDao userDao;

	public UserDaoProxy(UserDao userDao) {
		this.userDao = userDao;
	}

	public void save() {
		System.out.println("開啓事物...");
		userDao.save();
		System.out.println("關閉事物...");
	}

}
1234567891011121314151617
//添加完靜態代理的測試類
public class Test{
	public static void main(String[] args) {
		UserDao userDao = new UserDao();
		UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
		userDaoProxy.save();
	}
}
  • 缺點:每一個須要代理的對象都須要本身重複編寫代理,很不舒服,
  • 優勢:可是能夠面相實際對象或者是接口的方式實現代理

2.2.動態代理

什麼是動態代理

  • 動態代理也叫作,JDK代理、接口代理。
  • 動態代理的對象,是利用JDK的API,動態的在內存中構建代理對象(是根據被代理的接口來動態生成代理類的class文件,並加載運行的過程),這就叫動態代理
package com.lijie;

//接口
public interface UserDao {
    void save();
}
123456
package com.lijie;

//接口實現類
public class UserDaoImpl implements UserDao {
	public void save() {
		System.out.println("保存數據方法");
	}
}
  • //下面是代理類,可重複使用,不像靜態代理那樣要本身重複編寫代理
package com.lijie;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// 每次生成動態代理類對象時,實現了InvocationHandler接口的調用處理器對象
public class InvocationHandlerImpl implements InvocationHandler {

	// 這其實業務實現類對象,用來調用具體的業務方法
    private Object target;

    // 經過構造函數傳入目標對象
    public InvocationHandlerImpl(Object target) {
        this.target = target;
    }

    //動態代理實際運行的代理方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("調用開始處理");
        //下面invoke()方法是以反射的方式來建立對象,第一個參數是要建立的對象,第二個是構成方法的參數,由第二個參數來決定建立對象使用哪一個構造方法
		Object result = method.invoke(target, args);
        System.out.println("調用結束處理");
        return result;
    }
}
  • //利用動態代理使用代理方法
package com.lijie;

import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        // 被代理對象
        UserDao userDaoImpl = new UserDaoImpl();
        InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDaoImpl);

        //類加載器
        ClassLoader loader = userDaoImpl.getClass().getClassLoader();
        Class<?>[] interfaces = userDaoImpl.getClass().getInterfaces();

        // 主要裝載器、一組接口及調用處理動態代理實例
        UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
        newProxyInstance.save();
    }
}
  • 缺點:必須是面向接口,目標業務類必須實現接口
  • 優勢:不用關心代理類,只須要在運行階段才指定代理哪個對象

5.3.CGLIB動態代理

CGLIB動態代理原理:

  • 利用asm開源包,對代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理。

什麼是CGLIB動態代理

  • CGLIB動態代理和jdk代理同樣,使用反射完成代理,不一樣的是他能夠直接代理類(jdk動態代理不行,他必須目標業務類必須實現接口),CGLIB動態代理底層使用字節碼技術,CGLIB動態代理不能對 final類進行繼承。(CGLIB動態代理須要導入jar包)

代碼演示:

package com.lijie;

//接口
public interface UserDao {
    void save();
}
123456
package com.lijie;

//接口實現類
public class UserDaoImpl implements UserDao {
	public void save() {
		System.out.println("保存數據方法");
	}
}
12345678
package com.lijie;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

//代理主要類
public class CglibProxy implements MethodInterceptor {
	private Object targetObject;
	// 這裏的目標類型爲Object,則能夠接受任意一種參數做爲被代理類,實現了動態代理
	public Object getInstance(Object target) {
		// 設置須要建立子類的類
		this.targetObject = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}

	//代理實際方法
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("開啓事物");
		Object result = proxy.invoke(targetObject, args);
		System.out.println("關閉事物");
		// 返回代理對象
		return result;
	}
}

123456789101112131415161718192021222324252627282930
package com.lijie;

//測試CGLIB動態代理
public class Test {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDaoImpl());
        userDao.save();
    }
}

建造者模式

1.什麼是建造者模式

  • 建造者模式:是將一個複雜的對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的方式進行建立。
  • 工廠類模式是提供的是建立單個類的產品
  • 而建造者模式則是將各類產品集中起來進行管理,用來具備不一樣的屬性的產品

建造者模式一般包括下面幾個角色:

  1. uilder:給出一個抽象接口,以規範產品對象的各個組成成分的建造。這個接口規定要實現複雜對象的哪些部分的建立,並不涉及具體的對象部件的建立。
  2. ConcreteBuilder:實現Builder接口,針對不一樣的商業邏輯,具體化複雜對象的各部分的建立。 在建造過程完成後,提供產品的實例。
  3. Director:調用具體建造者來建立複雜對象的各個部分,在指導者中不涉及具體產品的信息,只負責保證對象各部分完整建立或按某種順序建立。
  4. Product:要建立的複雜對象。

2.建造者模式的使用場景

使用場景:

  1. 須要生成的對象具備複雜的內部結構。
  2. 須要生成的對象內部屬性自己相互依賴。
  • 與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。
  • JAVA 中的 StringBuilder就是建造者模式建立的,他把一個單個字符的char數組組合起來
  • Spring不是建造者模式,它提供的操做應該是對於字符串自己的一些操做,而不是建立或改變一個字符串。

3.代碼案例

  1. 創建一個裝備對象Arms
package com.lijie;

//裝備類
public class Arms {
	//頭盔
	private String helmet;
	//鎧甲
	private String armor;
	//武器
	private String weapon;
	
	//省略Git和Set方法...........
}
  1. 建立Builder接口(給出一個抽象接口,以規範產品對象的各個組成成分的建造,這個接口只是規範)
package com.lijie;

public interface PersonBuilder {

	void builderHelmetMurder();

	void builderArmorMurder();

	void builderWeaponMurder();

	void builderHelmetYanLong();

	void builderArmorYanLong();

	void builderWeaponYanLong();

	Arms BuilderArms(); //組裝
}
  1. 建立Builder實現類(這個類主要實現複雜對象建立的哪些部分須要什麼屬性)
package com.lijie;

public class ArmsBuilder implements PersonBuilder {
    private Arms arms;

    //建立一個Arms實例,用於調用set方法
    public ArmsBuilder() {
        arms = new Arms();
    }

    public void builderHelmetMurder() {
        arms.setHelmet("奪命頭盔");
    }

    public void builderArmorMurder() {
        arms.setArmor("奪命鎧甲");
    }

    public void builderWeaponMurder() {
        arms.setWeapon("奪命寶刀");
    }

    public void builderHelmetYanLong() {
        arms.setHelmet("炎龍頭盔");
    }

    public void builderArmorYanLong() {
        arms.setArmor("炎龍鎧甲");
    }

    public void builderWeaponYanLong() {
        arms.setWeapon("炎龍寶刀");
    }

    public Arms BuilderArms() {
        return arms;
    }
}
  1. Director(調用具體建造者來建立複雜對象的各個部分,在指導者中不涉及具體產品的信息,只負責保證對象各部分完整建立或按某種順序建立)
package com.lijie;

public class PersonDirector {
	
	//組裝
	public Arms constructPerson(PersonBuilder pb) {
		pb.builderHelmetYanLong();
		pb.builderArmorMurder();
		pb.builderWeaponMurder();
		return pb.BuilderArms();
	}

	//這裏進行測試
	public static void main(String[] args) {
		PersonDirector pb = new PersonDirector();
		Arms arms = pb.constructPerson(new ArmsBuilder());
		System.out.println(arms.getHelmet());
		System.out.println(arms.getArmor());
		System.out.println(arms.getWeapon());
	}
}

模板方法模式

1.什麼是模板方法

  • 模板方法模式:定義一個操做中的算法骨架(父類),而將一些步驟延遲到子類中。
    模板方法使得子類能夠不改變一個算法的結構來重定義該算法的

2.何時使用模板方法

  • 實現一些操做時,總體步驟很固定,可是呢。就是其中一小部分須要改變,這時候可使用模板方法模式,將容易變的部分抽象出來,供子類實現。

3.實際開發中應用場景哪裏用到了模板方法

  • 其實不少框架中都有用到了模板方法模式
  • 例如:數據庫訪問的封裝、Junit單元測試、servlet中關於doGet/doPost方法的調用等等

4.現實生活中的模板方法

例如:

  1. 去餐廳吃飯,餐廳給咱們提供了一個模板就是:看菜單,點菜,吃飯,付款,走人
    (這裏 「點菜和付款」 是不肯定的由子類來完成的,其餘的則是一個模板。)

5.代碼實現模板方法模式

  1. 先定義一個模板。把模板中的點菜和付款,讓子類來實現。
package com.lijie;

//模板方法
public abstract class RestaurantTemplate {

	// 1.看菜單
	public void menu() {
		System.out.println("看菜單");
	}

	// 2.點菜業務
	abstract void spotMenu();

	// 3.吃飯業務
	public void havingDinner(){ System.out.println("吃飯"); }

	// 3.付款業務
	abstract void payment();

	// 3.走人
	public void GoR() { System.out.println("走人"); }

	//模板通用結構
	public void process(){
		menu();
		spotMenu();
		havingDinner();
		payment();
		GoR();
	}
}
  1. 具體的模板方法子類 1
package com.lijie;

public class RestaurantGinsengImpl extends RestaurantTemplate {

    void spotMenu() {
        System.out.println("人蔘");
    }

    void payment() {
        System.out.println("5快");
    }
}
  1. 具體的模板方法子類 2
package com.lijie;

public class RestaurantLobsterImpl  extends RestaurantTemplate  {

    void spotMenu() {
        System.out.println("龍蝦");
    }

    void payment() {
        System.out.println("50塊");
    }
}
  1. 客戶端測試
package com.lijie;

public class Client {

    public static void main(String[] args) {
        //調用第一個模板實例
        RestaurantTemplate restaurantTemplate = new RestaurantGinsengImpl();
        restaurantTemplate.process();
    }
}

外觀模式

1.什麼是外觀模式

  • 外觀模式:也叫門面模式,隱藏系統的複雜性,並向客戶端提供了一個客戶端能夠訪問系統的接口。
  • 它向現有的系統添加一個接口,用這一個接口來隱藏實際的系統的複雜性。
  • 使用外觀模式,他外部看起來就是一個接口,其實他的內部有不少複雜的接口已經被實現

2.外觀模式例子

  • 用戶註冊完以後,須要調用阿里短信接口、郵件接口、微信推送接口。
  1. 建立阿里短信接口
package com.lijie;

//阿里短信消息
public interface AliSmsService {
	void sendSms();
}
123456
package com.lijie;

public class AliSmsServiceImpl implements AliSmsService {

    public void sendSms() {
        System.out.println("阿里短信消息");
    }

}
  1. 建立郵件接口
package com.lijie;

//發送郵件消息
public interface EamilSmsService {
	void sendSms();
}
123456
package com.lijie;

public class EamilSmsServiceImpl implements   EamilSmsService{
	public void sendSms() {
		System.out.println("發送郵件消息");
	}
}
  1. 建立微信推送接口
package com.lijie;

//微信消息推送
public interface WeiXinSmsService {
   void sendSms();
}
123456
package com.lijie;

public class WeiXinSmsServiceImpl implements  WeiXinSmsService {
    public void sendSms() {
        System.out.println("發送微信消息推送");
    }
}
  1. 建立門面(門面看起來很簡單使用,複雜的東西以及被門面給封裝好了)
package com.lijie;

public class Computer {
	AliSmsService aliSmsService;
	EamilSmsService eamilSmsService;
	WeiXinSmsService weiXinSmsService;

	public Computer() {
		aliSmsService = new AliSmsServiceImpl();
		eamilSmsService = new EamilSmsServiceImpl();
		weiXinSmsService = new WeiXinSmsServiceImpl();
	}

	//只須要調用它
	public void sendMsg() {
		aliSmsService.sendSms();
		eamilSmsService.sendSms();
		weiXinSmsService.sendSms();
	}
}
  1. 啓動測試
package com.lijie;

public class Client {

    public static void main(String[] args) {
        //普通模式須要這樣
        AliSmsService aliSmsService = new AliSmsServiceImpl();
        EamilSmsService eamilSmsService = new EamilSmsServiceImpl();
        WeiXinSmsService weiXinSmsService = new WeiXinSmsServiceImpl();
        aliSmsService.sendSms();
        eamilSmsService.sendSms();
        weiXinSmsService.sendSms();

        //利用外觀模式簡化方法
        new Computer().sendMsg();
    }
}

原型模式

1.什麼是原型模式

  • 原型設計模式簡單來講就是克隆
  • 原型代表了有一個樣板實例,這個原型是可定製的。原型模式多用於建立複雜的或者構造耗時的實例,由於這種狀況下,複製一個已經存在的實例可以使程序運行更高效。

2.原型模式的應用場景

  1. 類初始化須要消化很是多的資源,這個資源包括數據、硬件資源等。這時咱們就能夠經過原型拷貝避免這些消耗。
  2. 經過new產生的一個對象須要很是繁瑣的數據準備或者權限,這時可使用原型模式。
  3. 一個對象須要提供給其餘對象訪問,並且各個調用者可能都須要修改其值時,能夠考慮使用原型模式拷貝多個對象供調用者使用,即保護性拷貝。
咱們Spring框架中的多例就是使用原型。

3.原型模式的使用方式

  1. 實現Cloneable接口。在java語言有一個Cloneable接口,它的做用只有一個,就是在運行時通知虛擬機能夠安全地在實現了此接口的類上使用clone方法。在java虛擬機中,只有實現了這個接口的類才能夠被拷貝,不然在運行時會拋出CloneNotSupportedException異常。
  2. 重寫Object類中的clone方法。Java中,全部類的父類都是Object類,Object類中有一個clone方法,做用是返回對象的一個拷貝,可是其做用域protected類型的,通常的類沒法調用,所以Prototype類須要將clone方法的做用域修改成public類型。

3.1原型模式分爲淺複製和深複製

  1. (淺複製)只是拷貝了基本類型的數據,而引用類型數據,只是拷貝了一份引用地址。
  2. (深複製)在計算機中開闢了一塊新的內存地址用於存放複製的對象。

4.代碼演示

  1. 建立User類
package com.lijie;

import java.util.ArrayList;

public class User implements Cloneable {
    private String name;
    private String password;
    private ArrayList<String> phones;

    protected User clone() {
        try {
            User user = (User) super.clone();
            //重點,若是要連帶引用類型一塊兒複製,須要添加底下一條代碼,若是不加就對因而複製了引用地址
            user.phones = (ArrayList<String>) this.phones.clone();//設置深複製
            return user;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
	
	//省略全部屬性Git Set方法......
}
  1. 測試複製
package com.lijie;

import java.util.ArrayList;

public class Client {
    public static void main(String[] args) {
        //建立User原型對象
        User user = new User();
        user.setName("李三");
        user.setPassword("123456");
        ArrayList<String> phones = new ArrayList<>();
        phones.add("17674553302");
        user.setPhones(phones);

        //copy一個user對象,而且對象的屬性
        User user2 = user.clone();
        user2.setPassword("654321");

        //查看倆個對象是不是一個
        System.out.println(user == user2);

        //查看屬性內容
        System.out.println(user.getName() + " | " + user2.getName());
        System.out.println(user.getPassword() + " | " + user2.getPassword());
        //查看對於引用類型拷貝
        System.out.println(user.getPhones() == user2.getPhones());
    }
}
  1. 若是不須要深複製,須要刪除User 中的
//默認引用類型爲淺複製,這是設置了深複製
user.phones = (ArrayList<String>) this.phones.clone();

策略模式

1.什麼是策略模式

  • 定義了一系列的算法 或 邏輯 或 相贊成義的操做,並將每個算法、邏輯、操做封裝起來,並且使它們還能夠相互替換。(其實策略模式Java中用的很是很是普遍)
  • 我以爲主要是爲了 簡化 if…else 所帶來的複雜和難以維護。

2.策略模式應用場景

  • 策略模式的用意是針對一組算法或邏輯,將每個算法或邏輯封裝到具備共同接口的獨立的類中,從而使得它們之間能夠相互替換。
  1. 例如:我要作一個不一樣會員打折力度不一樣的三種策略,初級會員,中級會員,高級會員(三種不一樣的計算)。
  2. 例如:我要一個支付模塊,我要有微信支付、支付寶支付、銀聯支付等

3.策略模式的優勢和缺點

  • 優勢: 一、算法能夠自由切換。 二、避免使用多重條件判斷。 三、擴展性很是良好。
  • 缺點: 一、策略類會增多。 二、全部策略類都須要對外暴露。

4.代碼演示

  • 模擬支付模塊有微信支付、支付寶支付、銀聯支付
  1. 定義抽象的公共方法
package com.lijie;

//策略模式 定義抽象方法 全部支持公共接口
abstract class PayStrategy {

	// 支付邏輯方法
	abstract void algorithmInterface();

}
  1. 定義實現微信支付
package com.lijie;

class PayStrategyA extends PayStrategy {

	void algorithmInterface() {
		System.out.println("微信支付");
	}
}
  1. 定義實現支付寶支付
package com.lijie;

class PayStrategyB extends PayStrategy {

	void algorithmInterface() {
		System.out.println("支付寶支付");
	}
}
  1. 定義實現銀聯支付
package com.lijie;

class PayStrategyC extends PayStrategy {

	void algorithmInterface() {
		System.out.println("銀聯支付");
	}
}
  1. 定義下文維護算法策略
package com.lijie;// 使用上下文維護算法策略

class Context {

	PayStrategy strategy;

	public Context(PayStrategy strategy) {
		this.strategy = strategy;
	}

	public void algorithmInterface() {
		strategy.algorithmInterface();
	}

}
  1. 運行測試
package com.lijie;

class ClientTestStrategy {
	public static void main(String[] args) {
		Context context;
		//使用支付邏輯A
		context = new Context(new PayStrategyA());
		context.algorithmInterface();
		//使用支付邏輯B
		context = new Context(new PayStrategyB());
		context.algorithmInterface();
		//使用支付邏輯C
		context = new Context(new PayStrategyC());
		context.algorithmInterface();
	}
}

觀察者模式

1.什麼是觀察者模式

  • 先講什麼是行爲性模型,行爲型模式關注的是系統中對象之間的相互交互,解決系統在運行時對象之間的相互通訊和協做,進一步明確對象的職責。
  • 觀察者模式,是一種行爲性模型,又叫發佈-訂閱模式,他定義對象之間一種一對多的依賴關係,使得當一個對象改變狀態,則全部依賴於它的對象都會獲得通知並自動更新。

2.模式的職責

  • 觀察者模式主要用於1對N的通知。當一個對象的狀態變化時,他須要及時告知一系列對象,令他們作出相應。

實現有兩種方式:

  1. 推:每次都會把通知以廣播的方式發送給全部觀察者,全部的觀察者只能被動接收。
  2. 拉:觀察者只要知道有狀況便可,至於何時獲取內容,獲取什麼內容,均可以自主決定。

3.觀察者模式應用場景

  1. 關聯行爲場景,須要注意的是,關聯行爲是可拆分的,而不是「組合」關係。事件多級觸發場景。
  2. 跨系統的消息交換場景,如消息隊列、事件總線的處理機制。

4.代碼實現觀察者模式

  1. 定義抽象觀察者,每個實現該接口的實現類都是具體觀察者。
package com.lijie;

//觀察者的接口,用來存放觀察者共有方法
public interface Observer {
    // 觀察者方法
    void update(int state);
}
  1. 定義具體觀察者
package com.lijie;

// 具體觀察者
public class ObserverImpl implements Observer {

    // 具體觀察者的屬性
    private int myState;

    public void update(int state) {
        myState=state;
        System.out.println("收到消息,myState值改成:"+state);
    }

    public int getMyState() {
        return myState;
    }
}
  1. 定義主題。主題定義觀察者數組,並實現增、刪及通知操做。
package com.lijie;

import java.util.Vector;

//定義主題,以及定義觀察者數組,並實現增、刪及通知操做。
public class Subjecct {
	//觀察者的存儲集合,不推薦ArrayList,線程不安全,
	private Vector<Observer> list = new Vector<>();

	// 註冊觀察者方法
	public void registerObserver(Observer obs) {
		list.add(obs);
	}
    // 刪除觀察者方法
	public void removeObserver(Observer obs) {
		list.remove(obs);
	}

	// 通知全部的觀察者更新
	public void notifyAllObserver(int state) {
		for (Observer observer : list) {
			observer.update(state);
		}
	}
}
  1. 定義具體的,他繼承繼承Subject類,在這裏實現具體業務,在具體項目中,該類會有不少。
package com.lijie;

//具體主題
public class RealObserver extends Subjecct {
    //被觀察對象的屬性
	 private int state;
	 public int getState(){
		 return state;
	 }
	 public void  setState(int state){
		 this.state=state;
		 //主題對象(目標對象)值發生改變
		 this.notifyAllObserver(state);
	 }
}
  1. 運行測試
package com.lijie;

public class Client {

	public static void main(String[] args) {
		// 目標對象
		RealObserver subject = new RealObserver();
		// 建立多個觀察者
		ObserverImpl obs1 = new ObserverImpl();
		ObserverImpl obs2 = new ObserverImpl();
		ObserverImpl obs3 = new ObserverImpl();
		// 註冊到觀察隊列中
		subject.registerObserver(obs1);
		subject.registerObserver(obs2);
		subject.registerObserver(obs3);
		// 改變State狀態
		subject.setState(300);
		System.out.println("obs1觀察者的MyState狀態值爲:"+obs1.getMyState());
		System.out.println("obs2觀察者的MyState狀態值爲:"+obs2.getMyState());
		System.out.println("obs3觀察者的MyState狀態值爲:"+obs3.getMyState());
		// 改變State狀態
		subject.setState(400);
		System.out.println("obs1觀察者的MyState狀態值爲:"+obs1.getMyState());
		System.out.println("obs2觀察者的MyState狀態值爲:"+obs2.getMyState());
		System.out.println("obs3觀察者的MyState狀態值爲:"+obs3.getMyState());
	}
}
相關文章
相關標籤/搜索