設計模式之享元模式

享元模式

舉例分析1:

展示網站項目需求
小型的外包項目,給客戶A做一個產品展示網站,客戶A的朋友感覺效果不錯,也希
望做這樣的產品展示網站,但是要求都有些不同:

  1. 有客戶要求以新聞的形式發佈
  2. 有客戶人要求以博客的形式發佈
  3. 有客戶希望以微信公衆號的形式發佈
舉例分析2:
在大學時代,估計每個人都去圖書館借過書。借書的流程很簡單,如果書架上有這本書直接拿走,到借閱機上借閱就好了,如果沒有,可以到圖書管理處去拿一本新書。對於整個圖書館來說,書其實就是共享的,但是我們會發現其實每次借的書都是那些破舊一點的書,而不是新書,這是因爲學生太多了,如果我們每一次借書都拿出來一本新書,那整個圖書館估計會放不下,對於我們借書的流程和圖書共享的方式就是享元模式。

============================================================

傳統方案解決網站展現項目

  1. 直接複製粘貼一份,然後根據客戶不同要求,進行定製修改
  2. 給每個網站租用一個空間
  3. 方案設計示意圖

在這裏插入圖片描述

傳統方案解決網站展現項目-問題分析
  1. 需要的網站結構相似度很高,而且都不是高訪問量網站,如果分成多個虛擬空間來
    處理,相當於一個相同網站的實例對象很多,造成服務器的資源浪費
  2. 解決思路:整合到一個網站中,共享其相關的代碼和數據,對於硬盤、內存、CPU、
    數據庫空間等服務器資源都可以達成共享,減少服務器資源
  3. 對於代碼來說,由於是一份實例,維護和擴展都更加容易
  4. 上面的解決思路就可以使用 享元模式 來解決
基本介紹

如果在一個系統中存在多個相同的對象,那麼只需要共享一份對象的拷貝,而不必爲每一次使用都創建新的對象。目的是提高系統性能。

  1. 享元模式(Flyweight Pattern) 也叫 蠅量模式: 運
    用共享技術有效地支持大量細粒度的對象
  2. 常用於系統底層開發,解決系統的性能問題。像
    數據庫連接池,裏面都是創建好的連接對象,在
    這些連接對象中有我們需要的則直接拿來用,避
    免重新創建,如果沒有我們需要的,則創建一個
    3) 享元模式能夠解決重複對象的內存浪費的問題
    當系統中有大量相似對象,需要緩衝池時。不需
    總是創建新對象,可以從緩衝池裏拿。這樣可以
    降低系統內存,同時提高效率
  3. 享元模式經典的應用場景就是池技術了,String常
    量池、數據庫連接池、緩衝池
    等等都是享元模式
    的應用,享元模式是池技術的重要實現方式
享元模式的原理類圖

在這裏插入圖片描述

1)享元工廠(Llibrary):用於創建具體享元類,維護相同的享元對象。當請求對象已經存在時,直接返回對象,不存在時,在創建對象。在例子中的解釋就是圖書館,保存了所有的書,當學生借書時,有就拿走,沒有買一本新書。這裏面其實是使用了單例模式的。
2) FlyWeight 抽象的享元角色, 他是產品的抽象類, 同時定義出對象的外部狀態和內部狀態(後面介紹) 的接口或實現
3) ConcreteFlyWeight 是具體的享元角色,是具體的產品類,實現抽象角色定義相關業務
4) UnSharedConcreteFlyWeight 是不可共享的角色,一般不會出現在享元工廠

代碼編寫:

FlyWeight 定義需要共享的對象業務接口。享元類被創建出來總是爲了實現某些特定的業務邏輯.

/**
* 定義抽象享元類(Book)
*
* 抽象享元(Book):定義需要共享的對象業務接口。享元類被創建出來總是爲了實現某些特定的業務邏輯.
*/
public interface Book {
    public void borrow();
}

ConcreteFlyWeight 實現抽象享元類的接口,完成某一具體邏輯。

/**
 * @author 孫一鳴 on 2020/2/13
 * 具體享元(ConcreteBook):實現抽象享元類的接口,完成某一具體邏輯。在這裏表示可以被借出。
 *
 */
public class ConcreteBook  implements Book{
    //被借出的書名
    public  String name;


    public ConcreteBook(String name) {
        this.name = name;
    }

    @Override
    public void borrow() {
        System.out.println("圖書館借出一本書,書名爲:"+this.name);
    }
}

享元工廠(Llibrary)圖書館,保存了所有的書,當學生借書時,有就拿走,沒有買一本新書。


/**
 * 享元工廠
 *
 * @author 孫一鳴 on 2020/2/13
 */
public class Library {
//圖書館維護一個圖書列表
    private Map<String,Book> bookPools = new HashMap<String, Book>();
    private static  Library libraryFactory = new Library();

    //圖書館只有一個
    public static Library getInstance(){
        return libraryFactory;
    }

    //圖書館外借圖書
    public Book libTo(String bookname){
        Book order = null;
        //如果書架 有,取出這本書
        if(bookPools.containsKey(bookname)){
            order = bookPools.get(bookname);
        }
        else {
            order = new ConcreteBook(bookname);
            bookPools.put(bookname,order);
        }
        return order;
    }

    //圖書館書架上書的數量
    public int getAllBooks(){
        return bookPools.size();
    }
}


學生類:


/**
 * @author 孫一鳴 on 2020/2/13
 */
public class Student {
    private  static List<Book> books =new ArrayList<>();
    private static  Library library;

    private static void studentBorrow(String name){
        books.add(library.libTo(name));
    }

    public static void main(String[] args) {
        library = Library.getInstance();
        studentBorrow("JAVA編程思想");
        studentBorrow("JAVA核心卷一");
        studentBorrow("JAVA從入門到精通");
        System.out.println("後兩本沒學會,又借一次");
        studentBorrow("JAVA核心卷一");
        studentBorrow("JAVA從入門到精通");

        //把每一本書借出去
        for (Book book: books){
            book.borrow();
        }
        System.out.println("學生一共借了"+books.size());
        System.out.println("圖書館實際借出"+library.getAllBooks());
    }
}
結果截圖:

在這裏插入圖片描述

內部狀態和外部狀態

比如圍棋、五子棋、跳棋,它們都有大量的棋子對象,圍棋和五子棋只有黑白兩色,跳棋顏色多一
點,所以棋子顏色就是棋子的內部狀態;而各個棋子之間的差別就是位置的不同,當我們落子後,
落子顏色是定的,但位置是變化的,所以棋子座標就是棋子的外部狀態

  1. 享元模式提出了兩個要求:細粒度和共享對象。這裏就涉及到內部狀態和外部狀態
    了,即將對象的信息分爲兩個部分:內部狀態和外部狀態
  2. 內部狀態指對象共享出來的信息,存儲在享元對象內部且不會隨環境的改變而改變
  3. 外部狀態指對象得以依賴的一個標記,是隨環境改變而改變的、不可共享的狀態。
  4. 舉個例子:圍棋理論上有361個空位可以放棋子,每盤棋都有可能有兩三百個棋子對象產生,因爲內存空間有限,一臺服務器很難支持更多的玩家玩圍棋遊戲,如果用享元模式來處理棋子,那麼棋子對象就可以減少到只有兩個實例,這樣就很好的解決了對象的開銷問題
享元模式的注意事項和細節
  1. 在享元模式這樣理解,“享”就表示共享,“元”表示對象
  2. 系統中有大量對象,這些對象消耗大量內存,並且對象的狀態大部分可以外部化時,我們就可以考慮選用享元模式
  3. 唯一標識碼判斷,如果在內存中有,則返回這個唯一標識碼所標識的對象,用
    HashMap/HashTable存儲
  4. 享元模式大大減少了對象的創建,降低了程序內存的佔用,提高效率
  5. 享元模式提高了系統的複雜度。需要分離出內部狀態和外部狀態,而外部狀態具有固化特性,不應該隨着內部狀態的改變而改變,這是我們使用享元模式需要注意的地方.
  6. 使用享元模式時,注意劃分內部狀態和外部狀態,並且需要有一個工廠類加以控制。
  7. 享元模式經典的應用場景是需要緩衝池的場景,比如 String常量池、數據庫連接池

享元模式的優點

(1)節省內存空間,對於可重複的對象只會被創建一次,對象比較多時,就會極大的節省空間。

(2)提高效率,由於創建對象的數量減少,所以對系統內存的需求也減小,使得速度更快,效率更高。

享元模式的缺點

其實對於享元類有內部狀態和外部狀態,其區分就是圖書館的書一部分可以外借(外部狀態),一部分不可外借(內部狀態),兩個狀態的劃分對於書籍管理來說優點複雜化了。

享元模式與單例模式的區別

(1)享元設計模式是一個類有很多對象,而單例是一個類僅一個對象。

(2)享元模式是爲了節約內存空間,提升程序性能,而單例模式則主要是出於共享狀態的目的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章