Java設計模式(13):享元模式(蠅量模式)

傳說中的靖哥哥發表於2020-12-19

13,享元模式(FlyWeight)

13.1,問題引入

13.1.1,展示網站專案需求

​ 小型的外包專案,給客戶A做一個產品展示網站,客戶A的朋友覺得效果不錯,也需要這樣的產品展示網站,但是需求有些變化:

  • 有客戶要求以新聞的形式釋出
  • 有客戶要求以部落格的形式釋出
  • 有客戶要求以微信小程式的形式釋出

13.1.2,傳統方式解決網站專案

  • 直接將專案複製一份,根據不同客戶的需求,進行定製化修改
    在這裡插入圖片描述

13.1.3,問題分析

  • 需要的網站相似度很高,而且都不是高訪問量網站,如果分成多個虛擬機器進行部署,相當於一個相同網站的例項有很多,造成伺服器資源浪費
  • 可以將程式碼和資料整合到一個網站中,對於硬碟,記憶體,CPU等資源進行共享,減少伺服器資源

13.2,享元模式基本概述

  • 享元模式,也叫蠅量模式:運用共享技術有效的支援大量細粒度的物件。“享”表示共享,“元”表示物件
  • 常用於系統底層開發,解決系統的效能問題。像資料庫連線池,裡面都是已經建立好的資料庫連線物件,這些連線物件在我們需要的時候可以直接拿來用,避免重新建立,如果沒有我們需要的,則新建立一個
  • 享元模型能夠解決重複物件的記憶體浪費問題,當系統中存在大量的物件需要緩衝池時。不需要不斷的建立新物件,可以直接從緩衝池中拿。可以降低系統記憶體,提升效率。如JVM中的常量池
  • 在JDK中的應用,Integer的快取池-127~127

13.3,類圖

在這裡插入圖片描述

  • FlyWeightFactory:享元工廠,用於提供一個池容器,並從池中獲取物件的方法
  • IFlyWeight:享元抽象介面,提供產品的抽象介面,並同時定義出物件內部狀態和外部狀態的介面和實現
  • FlyWeight:具體享元角色,共享的角色,即具體的產品類,實現內部狀態,內部狀態角色是被享元工廠託管的角色
  • UnsharedFlyWeight:特殊享元角色,不可共享的角色,實現外部狀態,該部分不會被享元工廠託管

13.4,內部狀態和外部狀態

  • 享元模式提出了兩個要求:細粒度和共享物件,這就涉及到內部狀態和外部狀態了
  • 內部狀態是物件共享出來的資訊,儲存在享元物件內並不會隨著環境的不同而改變
  • 外部狀態是物件得以依賴的一個標記,隨環境改變而改變,不可共享的狀態
  • 舉例:圍棋理論上是有361個空位可以放棋子,每盤棋都有可能有兩三百個棋子產生,因為記憶體空間有限,一臺伺服器很難支撐更多的玩家玩圍棋遊戲,如果用享元模式來處理棋子,則棋子物件減少到兩個,空棋盤共用,具體的棋盤佈局是特殊角色

13.5,程式碼實現

  • IFlyWeight:享元角色頂層類

    package com.self.designmode.flyweight;
    
    /**
     * 享元模式頂層介面
     * @author PJ_ZHANG
     * @create 2020-08-22 21:16
     **/
    public interface IFlyWeight {
    
        void use();
    
        void setUnsharedFlyWeight(UnsharedFlyWeight unsharedFlyWeight);
    
    }
    
  • FlyWeight:具體角色

    package com.self.designmode.flyweight;
    
    /**
     * 享元模式: 具體角色
     * @author PJ_ZHANG
     * @create 2020-08-22 21:20
     **/
    public class FlyWeight implements IFlyWeight {
    
        private String type = "";
    
        private UnsharedFlyWeight unsharedFlyWeight = null;
    
        public FlyWeight(String type) {
            this.type = type;
        }
    
        @Override
        public void use() {
            System.out.println("享元具體角色: " + type);
            if (null != unsharedFlyWeight) {
                unsharedFlyWeight.use();
            }
        }
    
        @Override
        public void setUnsharedFlyWeight(UnsharedFlyWeight unsharedFlyWeight) {
            this.unsharedFlyWeight = unsharedFlyWeight;
        }
    
    }
    
  • UnsharedFlyWeight:特殊角色

    package com.self.designmode.flyweight;
    
    /**
     * 享元模式: 特殊角色
     * @author PJ_ZHANG
     * @create 2020-08-22 21:26
     **/
    public class UnsharedFlyWeight implements IFlyWeight {
    
        private String type = "";
    
        private UnsharedFlyWeight unsharedFlyWeight = null;
    
        public UnsharedFlyWeight(String type) {
            this.type = type;
        }
    
        @Override
        public void use() {
            System.out.println("享元特殊角色: " + type);
            if (null != unsharedFlyWeight) {
                unsharedFlyWeight.use();
            }
        }
    
        @Override
        public void setUnsharedFlyWeight(UnsharedFlyWeight unsharedFlyWeight) {
            this.unsharedFlyWeight = unsharedFlyWeight;
        }
    
    }
    
  • FlyWeightFactory:享元工廠

    package com.self.designmode.flyweight;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * 享元工廠, 獲取具體享元角色
     * @author PJ_ZHANG
     * @create 2020-08-22 21:16
     **/
    public class FlyWeightFactory {
    
        private static Map<String, FlyWeight> map = new ConcurrentHashMap<>(16);
    
        private static Map<String, UnsharedFlyWeight> unsharedMap = new ConcurrentHashMap<>(16);
    
        public static FlyWeight getFlyWeight(String type) {
            if (!map.containsKey(type)) {
                map.put(type, new FlyWeight(type));
            }
            return map.get(type);
        }
    
        public static UnsharedFlyWeight getUnsharedFlyWeight(String type) {
            if (!unsharedMap.containsKey(type)) {
                unsharedMap.put(type, new UnsharedFlyWeight(type));
            }
            return unsharedMap.get(type);
        }
    
    }
    
  • Client:客戶端

    package com.self.designmode.flyweight;
    
    /**
     * 享元模式
     * @author PJ_ZHANG
     * @create 2020-08-22 21:02
     **/
    public class Client {
    
        public static void main(String[] args) {
            FlyWeight flyWeight = FlyWeightFactory.getFlyWeight("新聞");
            UnsharedFlyWeight unsharedFlyWeight = FlyWeightFactory.getUnsharedFlyWeight("特殊");
            flyWeight.setUnsharedFlyWeight(unsharedFlyWeight);
            flyWeight.use();
            System.out.println("----------------------------");
            flyWeight = FlyWeightFactory.getFlyWeight("部落格");
            unsharedFlyWeight = FlyWeightFactory.getUnsharedFlyWeight("特殊");
            flyWeight.setUnsharedFlyWeight(unsharedFlyWeight);
            flyWeight.use();
            System.out.println("----------------------------");
            flyWeight = FlyWeightFactory.getFlyWeight("小程式");
            unsharedFlyWeight = FlyWeightFactory.getUnsharedFlyWeight("特殊");
            flyWeight.setUnsharedFlyWeight(unsharedFlyWeight);
            flyWeight.use();
            System.out.println("----------------------------");
        }
    
    }
    

13.6,注意事項及細節

  • 當系統中存在大量物件,這些物件消耗大量記憶體,並且物件的狀態大部分可以外部化時,可以考慮使用享元模式
  • 享元模式可以大大減少物件的建立,降低了程式記憶體的佔用,提高效率
  • 享元模式提高了系統複雜度。需要分離出內部狀態和外部狀態,外部狀態具有固化特性,不會隨著內部狀態的改變而改變,這是使用享元模式需要注意的問題
  • 享元模式的經典使用場景:String常量池,資料庫連線池,執行緒池

相關文章