常用設計模式系列(十二)—享元模式

常用設計模式系列(十二)—享元模式

第一節

前言

昏昏沉沉的兩天過去了,也不知道為什么,突然總覺得很困,可能之前熬夜熬的多了,所以現在可能年紀大了,需要蹦一蹦才能把自己從頹廢的邊緣拉扯回來,伸出你的手,讓我們向于和偉老師學習,來個養生迪(斗圖時刻)。

image

蹦完有沒有覺得賊精神,精神恢復,我們繼續講解我們的結構型設計模式,今天講解結構型設計模式第五章節—“享元模式”,“享元”和“外觀”一樣讓人琢磨不透,就像你追你愛慕的女孩的時候,和在一起之后的對比,雖然兩段關系不太一樣,但是她的心思,那可不是你能一眼猜透的;如何破冰?還是要深入了解,才能更有效的掌握核心信息。那么,“享元”如何代入呢?

可以這么理解,將一個系統中共同的內容抽象出來,多個使用者共用,減少共用內容的冗余,例如:現在國家倡導綠色出行,十個人都擁有自己的私家車,但是國家提供地鐵,可以運輸幾百個人,這樣把大家的交通工具抽象出來,抽象為公共交通,減少了街上車子的數量,減少了道路上車子的冗余,這個模式就是享元模式,“將公共元素進行抽象,然后共享這些公共元素”即享元。

第二節

享元模式概念

享元模式(Flyweight
Pattern)主要用于減少創建對象的數量,以減少內存占用和提高性能。這種類型的設計模式屬于結構型模式,它提供了減少對象數量從而改善應用所需的對象結構的方式,享元模式嘗試重用現有的同類對象,如果未找到匹配的對象,則創建新對象。是不是聽起來有點像單例模式?

在這里插入圖片描述

1.實現方式不一樣,單例是一個類只有一個唯一的實例,而享元可以有多個實例,只是通過一個共享容器來存儲不同的對象。

2.使用場景不一樣,單例是強調減少實例化提升性能,因此一般用于一些需要頻繁創建和銷毀實例化對象或創建和銷毀實例化對象非常消耗資源的類中,如連接池、線程池。而享元則是強調共享相同對象或對象屬性,節約內存使用空間。

在這里插入圖片描述
在這里插入圖片描述

在系統運行時有大量對象的情況下,有可能會造成堆內存溢出,我們把其中共同的部分抽象出來,抽象為公用對象,如果有相同的業務請求,直接返回在內存中已有的對象,避免重新創建。我們常用的線程池、數據庫連接池、常量池等等相關,防止堆內存中對象被重復多次的無限制的創建,進行高效利用,哪里需要往哪里搬。

在這里插入圖片描述

目前在我們身邊,共享的產品已經滲入我們身邊,共享單車、共享汽車、共享充電寶,當共享產品的廠家提供了共享產品時,就意味著這個產品開始了它的輪班生涯,例如共享廠家提供了一個充電寶,我們從這個充電寶的角度去看,第一次用充電寶,如果沒有,商家補貨,有了就拿走用,這個充電寶屬于共享池中,每個廠家規定的派發出去的也是有數量的,不能一直無限的擴展,就意味著池子的大小有限制,如果全部派發出去了,那你就需要等別人還回來才能使用,這些共享產品都統一管理起來在一個池子進行分配,達到共享的目的,這個模式就是享元模式。

在這里插入圖片描述

image

此圖中抽象充電寶屬于抽象享元角色,充電寶A和B是具體享元角色,品牌屬于不需要抽象的,傳遞給享元角色使用,則為非享元角色,充電寶池則為享元工廠。

第三節

代碼實現

1.創建非享元對象-品牌

package com.yang.flyweight;/*** @ClassName Power* @Description 非享元對象——品牌* @Author IT小白架構師之路* @Date 2020/12/24* @Version 1.0**/
public class Version {//品牌名稱private String name;public Version(String name){this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

2.創建充電寶抽象對象

package com.yang.flyweight;/*** @ClassName AbstractRecharge* @Description 抽象充電寶對象* @Author IT小白架構師之路* @Date 2020/12/24* @Version 1.0**/
public abstract class AbstractRecharge {//品牌屬性protected Version version;//狀態 0閑置 1使用中private String status;public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}public AbstractRecharge(String status,Version version){this.status = status;this.version = version;}/*** 充電方法*/public abstract void recharge();
}

3.創建A類充電寶

package com.yang.flyweight;/*** @ClassName Recharge* @Description 充電寶類A* @Author IT小白架構師之路* @Date 2020/12/24* @Version 1.0**/
public class RechargeA extends AbstractRecharge{public RechargeA(String status,Version version){super(status,version);}/*** 充電方法*/public void recharge(){System.out.println("我是"+super.version.getName()+"充電寶,我可以充電");}
}

4.創建B類充電寶

package com.yang.flyweight;/*** @ClassName RechargeB* @Description B類* @Author IT小白架構師之路* @Date 2020/12/24* @Version 1.0**/
public class RechargeB extends AbstractRecharge{public RechargeB(String status,Version version){super(status,version);}/*** 充電方法*/public void recharge(){System.out.println("我是"+super.version.getName()+"充電寶,我可以充電");}
}

5.創建充電寶池(類似數據庫連接池的方式,自動擴容)

package com.yang.flyweight;import java.util.ArrayList;
import java.util.List;/*** @ClassName RechargeFactory* @Description 充電寶池子* @Author IT小白架構師之路* @Date 2020/12/24* @Version 1.0**/
public class RechargeFactory {/*** 充電寶池子最大數量*/private static int maxNum = 5;/*** 初始化數量*/private static int initNum = 2;private static List<AbstractRecharge> list = new ArrayList<AbstractRecharge>();static {for (int i=0;i<initNum;i++){if(i%2==0){Version version = new Version("A品牌");list.add(new RechargeA("0",version));}else{Version version = new Version("B品牌");list.add(new RechargeB("0",version));}}System.out.println("充電寶數量初始化成功");}//獲取對象public static AbstractRecharge getRecharge() throws Exception{//總數大小int size = list.size();System.out.print("充電寶總數大小"+size+",");//查看閑置大小getHasNum();//是否需要等待boolean wait = true;while (true){boolean haveStatus = false;for (AbstractRecharge recharge : list){//有閑置的對象,直接返回if("0".equals(recharge.getStatus())){haveStatus = true;wait = false;recharge.setStatus("1");System.out.println("有可用充電寶,請取走");return  recharge;}}if(!haveStatus){if (list.size()>=maxNum){System.out.println("目前無充電寶可用,等待2s");Thread.sleep(2000);}else{//新增對象并返回AbstractRecharge recharge;if (size%2==0){Version version = new Version("A品牌");recharge = new RechargeA("0",version);}else{Version version = new Version("B品牌");recharge = new RechargeB("0",version);}wait = false;recharge.setStatus("1");list.add(recharge);return recharge;}}}}/*** 歸還充電寶* @param recharge*/public static void backRecharge(AbstractRecharge recharge){//置閑recharge.setStatus("0");System.out.println("充電結束,充電寶已歸還");}/*** 獲取閑置數量*/public static void getHasNum() {int num = 0;for (AbstractRecharge recharge : list){//有閑置的對象,直接返回if("0".equals(recharge.getStatus())){num++;}}System.out.println("目前閑置數量"+num);}
}

6.創建runbale用來測試,執行充電方法

package com.yang.flyweight;/*** @ClassName RechaegeRunnable* @Description 注釋* @Author IT小白架構師之路* @Date 2020/12/24* @Version 1.0**/
public class RechargeRunnable implements Runnable{//傳入充電寶對象private AbstractRecharge recharge;private int i;public RechargeRunnable(AbstractRecharge recharge,int i){this.recharge = recharge;this.i = i;}@Overridepublic synchronized void run() {try {Thread.sleep(5000);//調用充電方法recharge.recharge();int num = i+1;//歸還充電寶RechargeFactory.backRecharge(recharge);System.out.println("第"+num+"個線程執行完畢");}catch (Exception e){e.printStackTrace();}}
}
  1. 創建客戶端測試,創建20個線程測試
package com.yang.flyweight;import java.util.ArrayList;
import java.util.List;/*** @ClassName Client* @Description 注釋* @Author IT小白架構師之路* @Date 2020/12/24* @Version 1.0**/
public class Client {public static void main(String[] args) throws Exception{List<Thread> threadList = new ArrayList<Thread>();//20個線程同時處理for (int i=0;i<20;i++){//獲取對象AbstractRecharge recharge = RechargeFactory.getRecharge();//創建新的線程去使用RechargeRunnable runnable = new RechargeRunnable(recharge,i);//啟動線程Thread thread = new Thread(runnable);thread.start();threadList.add(thread);}//設置當子線程處理完再執行主線程for (Thread temp:threadList){temp.join();}System.out.print("結束測試,");RechargeFactory.getHasNum();}
}

8.測試結果如下,使用享元模式對公用對象進行抽象

充電寶數量初始化成功
充電寶總數大小2,目前閑置數量2
有可用充電寶,請取走
充電寶總數大小2,目前閑置數量1
有可用充電寶,請取走
充電寶總數大小2,目前閑置數量0
充電寶總數大小3,目前閑置數量0
充電寶總數大小4,目前閑置數量0
充電寶總數大小5,目前閑置數量0
目前無充電寶可用,等待2s
目前無充電寶可用,等待2s
目前無充電寶可用,等待2s
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
我是B品牌充電寶,我可以充電
充電結束,充電寶已歸還
第4個線程執行完畢
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
第1個線程執行完畢
我是B品牌充電寶,我可以充電
充電結束,充電寶已歸還
第2個線程執行完畢
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
第5個線程執行完畢
第3個線程執行完畢
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量4
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量3
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量2
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量1
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量0
目前無充電寶可用,等待2s
目前無充電寶可用,等待2s
目前無充電寶可用,等待2s
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
第6個線程執行完畢
我是B品牌充電寶,我可以充電
充電結束,充電寶已歸還
第7個線程執行完畢
我是B品牌充電寶,我可以充電
充電結束,充電寶已歸還
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
第10個線程執行完畢
第9個線程執行完畢
第8個線程執行完畢
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量4
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量3
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量2
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量1
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量0
目前無充電寶可用,等待2s
目前無充電寶可用,等待2s
目前無充電寶可用,等待2s
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
我是B品牌充電寶,我可以充電
充電結束,充電寶已歸還
第12個線程執行完畢
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
第13個線程執行完畢
我是B品牌充電寶,我可以充電
充電結束,充電寶已歸還
第11個線程執行完畢
第14個線程執行完畢
第15個線程執行完畢
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量4
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量3
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量2
有可用充電寶,請取走
充電寶總數大小5,目前閑置數量1
有可用充電寶,請取走
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
我是B品牌充電寶,我可以充電
充電結束,充電寶已歸還
第17個線程執行完畢
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
第16個線程執行完畢
我是B品牌充電寶,我可以充電
充電結束,充電寶已歸還
第19個線程執行完畢
我是A品牌充電寶,我可以充電
充電結束,充電寶已歸還
第18個線程執行完畢
第20個線程執行完畢
結束測試,目前閑置數量5

第四節

享元模式優缺點及適用場景

優點:

1.相同對象可以進行管理,防止創建重復的對象導致內存溢出,這種方式提高了效率。

2.對象統一管理,需要做統一處理的時候則比較方便。

缺點:

1.為了使對象可以共享,需要將一些不能共享的狀態外部化,這將增加程序的復雜性。
2.讀取享元模式的外部狀態會使得運行時間稍微變長。

適用場景:

1.系統中存在大量相同或者相似對象,且占用較大的內存資源,優化方案可采用享元。

2.大部分的對象可以按照內部狀態進行分組,且可將不同的部分分割為外部對象的時候。

3.線程池、數據庫連接池等相關場景。

掃描二維碼

關注我吧

IT小白架構師之路

image

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916789.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916789.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916789.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于spring boot的醫院掛號就診系統(源碼+論文)

一、開發環境 技術/工具描述MYSQL數據庫1. 體積小&#xff0c;安裝便捷&#xff1a;MySQL數據庫體積小&#xff0c;占用內存小&#xff0c;不影響電腦上其他軟件的運行&#xff0c;并且不需要因為安裝維護MySQL數據庫而重裝系統。2. 適合老舊電腦&#xff1a;作為學習開發的電…

spring-security

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>spring: security: user: name: root password: 123456 這個配置在訪問接口時候根據您提供的Spring Secur…

搭建一個自定義的 React 圖標庫

搭建一個自定義的 React 圖標庫可以讓你在多個項目中復用統一的圖標資源&#xff0c;同時支持按需加載、主題化和靈活的配置。以下是詳細的步驟指南&#xff1a; 1. 設計圖標庫結構 首先規劃圖標庫的目錄結構和功能&#xff1a; my-react-icons/ ├── src/ │ ├── ico…

寶塔面板如何升級OpenSSL

寶塔面板如何升級OpenSSL&#xff08;親測可用&#xff09;目前一些服務器的OpenSSL還是1.0.1e版本&#xff0c;今天進行服務器漏洞檢測出現OpenSSL存在漏洞&#xff0c;那只能升級OpenSSL了。1、登錄SSH&#xff0c;查看OpenSSL版本openssl version2、下載源代碼wget https://…

深入理解 C++ 紅黑樹:從理論到實踐

引言 在計算機科學領域&#xff0c;數據結構是構建高效算法的基石。而在眾多的數據結構中&#xff0c;平衡二叉搜索樹因其優秀的查找、插入和刪除性能而備受關注。紅黑樹&#xff08;Red-Black Tree&#xff09;作為一種自平衡的二叉搜索樹&#xff0c;更是在 C 標準庫&#x…

外星人筆記本裝win11哪個版本好_外星人筆記本裝win11專業版教程

外星人筆記本安裝win11哪個版本好&#xff1f;答&#xff1a;外星人筆記本還是建議安裝win11專業版。Win分為多個版本&#xff0c;其中家庭版&#xff08;Home&#xff09;和專業版&#xff08;Pro&#xff09;是用戶選擇最多的兩個版本。win11專業版在功能以及安全性方面有著明…

自學嵌入式 day37 HTML

HTML:超文本標記語言HyperText Markup Language一種用于創建網頁的標準標記語言HTML 運行在瀏覽器上&#xff0c;由瀏覽器來解析。https://www.runoob.com/html/html-tutorial.html1.格式 <!DOCTYPE html> <html><head><meta charset"utf-8"&g…

【車聯網kafka】Kafka核心架構與實戰經驗(第一篇)

目錄 一、我與kafka的緣分-初識Kafka 二、Kafka深入探討-了解kafka ?編輯2.1 kafka 生產者框架 2.1.1 生產者在生活中的實例 2.1.2 kafka生產者流程及框架 1. 主線程處理階段 2. Sender線程處理階段 設計優勢總結 2.2 kafka 生產者框架中的一些關鍵參數 2.3 kafka 生…

Go 語言變量作用域

Go 語言變量作用域 引言 在編程語言中&#xff0c;變量作用域是定義變量可以使用和不可使用的區域。在Go語言中&#xff0c;理解變量的作用域對于編寫高效且易于維護的代碼至關重要。本文將詳細介紹Go語言中的變量作用域&#xff0c;包括其規則、類型以及實際應用。 一、變量作…

單卡10分鐘部署MiniCPM4-0.5B:輕量級大模型本地運行指南

一、介紹 MiniCPM 4 是一個極其高效的邊緣側大型模型&#xff0c;經過了模型架構、學習算法、訓練數據和推理系統四個維度的高效優化&#xff0c;實現了極致的效率提升。 &#x1f3d7;? 高效的模型架構&#xff1a; InfLLM v2 – 可訓練的稀疏注意力機制&#xff1a;采用可…

CSS變量與Houdini自定義屬性:解鎖樣式編程新維度

在前端開發中&#xff0c;CSS變量和Houdini自定義屬性正在徹底改變我們編寫和管理樣式的方式。這些技術不僅提高了樣式代碼的可維護性&#xff0c;更為CSS帶來了編程語言的強大能力。一、CSS變量&#xff1a;原生樣式的革命 CSS變量&#xff08;CSS Custom Properties&#xff…

Android中PID與UID的區別和聯系(2)

一、核心概念對比特性PID (Process ID)UID (User ID)本質進程唯一標識符應用身份標識符分配時機進程啟動時動態分配應用安裝時靜態分配生命周期進程結束時回收應用卸載時才回收變化性每次啟動都可能不同長期保持不變作用范圍單進程內唯一全設備范圍唯一核心作用系統資源管理&am…

TCPDump實戰手冊:協議/端口/IP過濾與組合分析指南

目錄 一、基礎過濾速查表 1. 協議過濾&#xff08;單協議&#xff09; 2. 端口過濾 3. IP地址過濾 二、組合過濾實戰示例 1. 協議端口組合 2. IP端口組合 3. 復雜邏輯組合 三、高級協議分析示例 1. HTTP請求分析 2. DNS問題排查 3. TCP連接問題分析 四、組合過濾場…

【智能協同云圖庫】智能協同云圖庫第八彈:基于阿里云百煉大模型—實現 AI 擴圖功能

AI 擴圖功能 需求分析 隨著 AI 的高速發展&#xff0c;AI 幾乎可以應用到任何傳統業務中&#xff0c;增強應用的功能&#xff0c;帶給用戶更好的體驗。 對于圖庫網站來說&#xff0c;AI 也有非常多的應用空間&#xff0c;比如可以利用 AI 繪圖大模型來編輯圖片&#xff0c;實現…

2025年Solar應急響應公益月賽-7月筆記ing

應急響應身為顏狗的我是真心覺得lovelymem的ui寫得~~~~【任務1】應急大師題目描述&#xff1a;請提交隱藏用戶的名稱&#xff1f;print打印注冊表&#xff0c;或者開啟環境是就有【任務4】應急大師題目描述&#xff1a;請提交黑客創建隱藏用戶的TargetSid&#xff08;目標賬戶安…

C++/CLI vs 標準 C++ vs C# 語法對照手冊

&#x1f680; C/CLI vs 標準 C vs C# 語法對照手冊&#x1f9e9; 核心類型系統對比 // 類型聲明語法對比 標準 C C/CLI C# ─────────────────────────────────────────────────…

倉庫管理系統-2-后端之基于繼承基類的方式實現增刪改查

文章目錄 1 數據庫表user 2 后端通用框架 2.1 User.java(實體類) 2.2 使用封裝的方法(繼承基類) 2.2.1 UserMapper.java(mapper接口) 2.2.2 UserService.java(service接口) 2.2.3 UserServiceImpl.java(service實現類) 2.2.4 UserController.java(控制器) 3 增刪改查(封裝的方法…

【el-table滾動事件】el-table表格滾動時,獲取可視窗口內的行數據

一個簡單的獲取內容的辦法 表格部分&#xff0c;主要是ref寫一下<el-table :data"tableData" ref"tableRef"> </el-table>進入頁面的時候綁定監聽 mounted(){ // 綁定滾動事件this.$nextTick(() > {const table this.$refs.tableRef;const…

OCR 賦能自動閱卷:讓評分更高效精準

考試閱卷中&#xff0c;OCR 技術正成為高效助手&#xff0c;尤其在客觀題和標準化答題場景中表現亮眼。將考生答題卡掃描后&#xff0c;OCR 能快速識別填涂的選項、手寫數字或特定符號&#xff0c;與標準答案比對后自動判分。相比人工閱卷&#xff0c;它能在短時間內完成成百上…

在docker中安裝frp實現內網穿透

服務端frps 1.首先在服務器端安裝frps docker pull snowdreamtech/frps2.本地創建frps的配置文件frps.ini [common] bind_port 7000 # frp 服務端控制端口 token xxxxx # 客戶端認證密鑰3.啟動frps docker run -d --name frps \ --network host \ --restartalwa…