日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

還在重復(fù)創(chuàng)建對(duì)象?快試試通過(guò)享元模式減少對(duì)象創(chuàng)建

來(lái)源: 責(zé)編: 時(shí)間:2024-03-21 09:45:51 217觀看
導(dǎo)讀享元模式享元模式是一種結(jié)構(gòu)型設(shè)計(jì)模式,旨在通過(guò)共享盡可能多的數(shù)據(jù)來(lái)最小化內(nèi)存使用和提高性能。在享元模式中,對(duì)象被分為內(nèi)部狀態(tài)和外部狀態(tài)。內(nèi)部狀態(tài)是可以共享的,而外部狀態(tài)是根據(jù)對(duì)象的上下文而變化的。在實(shí)現(xiàn)享元

享元模式

享元模式是一種結(jié)構(gòu)型設(shè)計(jì)模式,旨在通過(guò)共享盡可能多的數(shù)據(jù)來(lái)最小化內(nèi)存使用和提高性能。在享元模式中,對(duì)象被分為內(nèi)部狀態(tài)和外部狀態(tài)。內(nèi)部狀態(tài)是可以共享的,而外部狀態(tài)是根據(jù)對(duì)象的上下文而變化的。ibP28資訊網(wǎng)——每日最新資訊28at.com

在實(shí)現(xiàn)享元模式時(shí),通常會(huì)創(chuàng)建一個(gè)工廠類來(lái)管理共享的對(duì)象實(shí)例,并在需要時(shí)返回已存在的實(shí)例,而不是創(chuàng)建新的實(shí)例。這樣可以減少內(nèi)存占用,并且可以提高系統(tǒng)的性能。ibP28資訊網(wǎng)——每日最新資訊28at.com

應(yīng)用場(chǎng)景

享元模式適用于需要共享大量對(duì)象、減少內(nèi)存占用、優(yōu)化性能的場(chǎng)景。ibP28資訊網(wǎng)——每日最新資訊28at.com

  1. 對(duì)象的數(shù)量非常大,且占用大量?jī)?nèi)存。通過(guò)享元模式可以共享對(duì)象,減少內(nèi)存占用。
  2. 對(duì)象的大部分狀態(tài)可以外部狀態(tài),而少部分狀態(tài)可以內(nèi)部狀態(tài)。通過(guò)享元模式可以將內(nèi)部狀態(tài)和外部狀態(tài)分離,減少對(duì)象數(shù)量。
  3. 對(duì)象的狀態(tài)可以被多個(gè)對(duì)象共享。通過(guò)享元模式可以將狀態(tài)共享,減少重復(fù)創(chuàng)建對(duì)象。
  4. 對(duì)象的創(chuàng)建和銷毀頻繁,需要優(yōu)化性能。通過(guò)享元模式可以減少對(duì)象的創(chuàng)建和銷毀,提高性能。

場(chǎng)景示例

過(guò)年回家買(mǎi)火車票是一件很困難的事,無(wú)數(shù)人用刷票軟件向服務(wù)端發(fā)出請(qǐng)求,對(duì)于每一個(gè)請(qǐng)求服務(wù)器都必須做出應(yīng)答。在用戶設(shè)置好出發(fā)地和目的地之后,每次請(qǐng)求都返回一個(gè)查詢的車票結(jié)果。為了便于理解,我們假設(shè)每次返回的只有一趟列車的車票。那么當(dāng)數(shù)以萬(wàn)計(jì)的人不問(wèn)斷在請(qǐng)求數(shù)據(jù)時(shí),如果每次都重新創(chuàng)建一個(gè)查詢的車票結(jié)果,那么必然會(huì)造成大量重復(fù)對(duì)象的創(chuàng)建、銷毀,使得 GC 任務(wù)繁重、內(nèi)存占用率高居不下。而這類問(wèn)題通過(guò)享元模式就能夠得到很好地改善,從城市 A 到城市 B 的車輛是有限的,車上的鋪位也就是硬臥、硬臥、坐票 3 種。我們將這些可以公用的對(duì)象緩存起來(lái),在用戶查詢時(shí)優(yōu)先使用緩存,如果沒(méi)有緩存則重新創(chuàng)建。這樣就將成千上萬(wàn)的對(duì)象變?yōu)榱丝蛇x擇的有限數(shù)量。ibP28資訊網(wǎng)——每日最新資訊28at.com

首先我們創(chuàng)建一個(gè) Ticket 接口,該接口定義展示車票信息的函數(shù):ibP28資訊網(wǎng)——每日最新資訊28at.com

public interface Ticket {    public void showTicketInfo(String bunk);}

它的一個(gè)具體的實(shí)現(xiàn)類是 TrainTicket 類:ibP28資訊網(wǎng)——每日最新資訊28at.com

class TrainTicket implements Ticket {    public String from; // 始發(fā)地    public String to; // 目的地    public String bunk; // 鋪位    public int price;    TrainTicket(String from, String to) {        this.from = from;        this.to = to;    }    @Override    public void showTicketInfo(String bunk) {        price = new Random().nextInt(300);        System.out.println("購(gòu)買(mǎi) 從 " + from + " 到 " + to + "的 "                + bunk + " 火車票" + ", 價(jià)格 : " + price);    }}

數(shù)據(jù)庫(kù)中表示火車票的信息有出發(fā)地、目的地、鋪位、價(jià)格等字段,在購(gòu)票用戶每次查詢時(shí)如果沒(méi)有用某種緩存模式,那么返回車票數(shù)據(jù)的接口實(shí)現(xiàn)如下:ibP28資訊網(wǎng)——每日最新資訊28at.com

public class TicketFactory {    public static Ticket getTicket(String from, String to) {        return new TrainTicket(from, to);    }}

在 TicketFactory 的 getTicket 函數(shù)中每次會(huì) new 一個(gè) TrainTicket 對(duì)象,也就是說(shuō)如果在短時(shí)間內(nèi)有 10000 萬(wàn)用戶求購(gòu)北京到杭州的車票,那么北京到杭州的車票對(duì)象就會(huì)被創(chuàng)建 10000 次,當(dāng)數(shù)據(jù)返回之后這些對(duì)象變得無(wú)用了又會(huì)被虛擬機(jī)回收。此時(shí)就會(huì)造成大量的重復(fù)對(duì)象存在內(nèi)存中,GC 對(duì)這些對(duì)象的回收也會(huì)非常消耗資源。如果用戶的請(qǐng)求量很大可能導(dǎo)致系統(tǒng)變得極其緩慢,甚至可能導(dǎo)致 OOM。正如上文所說(shuō),享元模式通過(guò)消息池的形式有效地減少了重復(fù)對(duì)象的存在。它通過(guò)內(nèi)部狀態(tài)標(biāo)識(shí)某個(gè)種類的對(duì)象,外部程序根據(jù)這個(gè)不會(huì)變化的內(nèi)部狀態(tài)從消息池中取出對(duì)象。使得同一類對(duì)象可以被復(fù)用,避免大量重復(fù)對(duì)象。ibP28資訊網(wǎng)——每日最新資訊28at.com

使用享元模式很簡(jiǎn)單,只需要簡(jiǎn)單地改造一下 TicketFactory,具體代碼如下:ibP28資訊網(wǎng)——每日最新資訊28at.com

/** * 車票工廠,以出發(fā)地和目的地為key緩存車票 *  */public class TicketFactory {    static Map<String, Ticket> sTicketMap = new ConcurrentHashMap<String, Ticket>();    public static Ticket getTicket(String from, String to) {        String key = from + "-" + to;        if (sTicketMap.containsKey(key)) {            System.out.println("使用緩存 ==> " + key);            return sTicketMap.get(key);        } else {            System.out.println("創(chuàng)建對(duì)象 ==> " + key);            Ticket ticket = new TrainTicket(from, to);            sTicketMap.put(key, ticket);            return ticket;        }    }}

在 TicketFactory 中添加了一個(gè) map 容器,并且以出發(fā)地 + "-" + 日的地為鍵、以車票對(duì)象作為值存儲(chǔ)車票對(duì)象。這個(gè) map 的鍵就是我們說(shuō)的內(nèi)部狀態(tài),在這里就是出發(fā)地、橫杠、目的地拼接起來(lái)的字符串,如果沒(méi)有緩存則創(chuàng)建一個(gè)對(duì)象,并且將這個(gè)對(duì)象緩存到 map 中,下次再有這類請(qǐng)求時(shí)則直接從緩存中獲取。這樣即使有 10000 個(gè)請(qǐng)求北京到杭州的車票信息,那么出發(fā)地是北京、目的地是杭州的車票對(duì)象只有一個(gè)。這樣就從這個(gè)對(duì)象從 10000 減到了 1 個(gè),避免了大量的內(nèi)存占用及頻繁的 GC 操作。簡(jiǎn)單實(shí)現(xiàn)代碼如下:ibP28資訊網(wǎng)——每日最新資訊28at.com

public class Test {    public static void main(String[] args) {         Ticket ticket01 = TicketFactory.getTicket("北京", "杭州");         ticket01.showTicketInfo("上鋪");         Ticket ticket02 = TicketFactory.getTicket("北京", "杭州");         ticket02.showTicketInfo("下鋪");         Ticket ticket03 = TicketFactory.getTicket("北京", "杭州");         ticket03.showTicketInfo("坐票");    }}

運(yùn)行輸出:ibP28資訊網(wǎng)——每日最新資訊28at.com

創(chuàng)建對(duì)象二=>北京-杭州購(gòu)買(mǎi)從北京到杭州的上鋪火車票,價(jià)格:28使用緩存==>北京-杭州購(gòu)買(mǎi)從北京到杭州的下鋪火車票,價(jià)格:188使用緩存==>北京-杭州購(gòu)買(mǎi)從北京到杭州的坐票火車票,價(jià)格:148

從輸出結(jié)果可以看到,只有第一次查詢車票時(shí)創(chuàng)建了一次對(duì)象,后續(xù)的查詢都使用的是消息池中的對(duì)象。這其實(shí)就是相當(dāng)于一個(gè)對(duì)象緩存,避免了對(duì)象的重復(fù)創(chuàng)建與回收。在這個(gè)例子中,內(nèi)部狀態(tài)就是出發(fā)地和目的地,內(nèi)部狀態(tài)不會(huì)發(fā)生變化;外部狀態(tài)就是鋪位和價(jià)格,價(jià)格會(huì)隨著鋪位的變化而變化。ibP28資訊網(wǎng)——每日最新資訊28at.com

在 JDK 中 String 也是類似消息池,我們知道在 Java 中 String 是存在于常量池中。也就是說(shuō)一個(gè) String 被定義之后它就被緩存到了常量池中,當(dāng)其他地方要使用同樣的字符串時(shí),則直接使用的是緩存,而不會(huì)重復(fù)創(chuàng)建。例如下面這段代碼。ibP28資訊網(wǎng)——每日最新資訊28at.com

public class Test {    public static void main(String[] args) {        testString();    }    private static void testString() {        String str1 = new String("abc");        String str2 = "abc";        String str3 = new String("abc");        String str4 = "ab" + "c";        // 使用equals只判定字符值        System.out.println(str1.equals(str2));        System.out.println(str1.equals(str3));        System.out.println(str3.equals(str2));        // 等號(hào)判等,判定兩個(gè)對(duì)象是不是同一個(gè)地址        System.out.println(str1 == str2);        System.out.println(str1 == str3);        System.out.println(str3 == str2);        System.out.println(str4 == str2);    }}

輸出:ibP28資訊網(wǎng)——每日最新資訊28at.com

truetruetruefalsefalsefalsetrue


ibP28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-78299-0.html還在重復(fù)創(chuàng)建對(duì)象?快試試通過(guò)享元模式減少對(duì)象創(chuàng)建

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 你不可不知的 15 個(gè) JavaScript 小貼士

下一篇: HTML問(wèn)題:如何實(shí)現(xiàn)分享URL預(yù)覽?

標(biāo)簽:
  • 熱門(mén)焦點(diǎn)
Top 主站蜘蛛池模板: 富民县| 金乡县| 巩留县| 双江| 神池县| 太保市| 鹤山市| 尖扎县| 萨迦县| 于都县| 巴林右旗| 南郑县| 双辽市| 阿勒泰市| 会同县| 北川| 鸡西市| 义马市| 保靖县| 剑川县| 酒泉市| 河间市| 和林格尔县| 习水县| 武隆县| 揭阳市| 沙坪坝区| 苗栗市| 修文县| 江源县| 浪卡子县| 祁阳县| 平阳县| 万全县| 凯里市| 黎城县| 韶关市| 垫江县| 塔城市| 房山区| 于都县|