Java設計模式學習記錄-享元模式 Java設計模式學習記錄-GoF設計模式概述

前言

享元模式也是一種結構型模式,這篇是介紹結構型模式的最後一篇了(由於代理模式很早以前就已經寫過了)。享元模式採用一個共享來避免大量擁有相同內容對象的開銷。這種開銷最多見、最直觀的就是內存損耗。html

享元模式

定義

享元模式是指運用共享技術有效的支持大量細粒度對象的複用。系統只使用少許的對象,而這些對象都很類似,狀態變化很小,能夠實現對象的屢次複用。因爲享元模式要求可以共享的對象必須是細粒度對象,所以它又稱爲輕量級模式,它是一種對象結構型模式。java

舉例

咖啡問題,在一家咖啡店裏有幾種口味的咖啡,例如:拿鐵、摩卡、卡布奇諾等等。最近這家店在搞促銷活動,一中午能賣出幾百杯咖啡,那麼咖啡的口味就是一種共享的元素。下面用代碼來實現一下這個例子。面試

定義訂單接口設計模式

/**
 * 訂單接口
 */
public interface Order {

    //賣出咖啡
    void sell();

}

具體的訂單實現ide

public class CoffeeOrder implements Order {

    //咖啡口味
    public String flavor;

    public CoffeeOrder(String flavor){
        this.flavor = flavor;
    }

    @Override
    public void sell() {
        System.out.println("賣出了一份"+flavor+"的咖啡。");
    }
}

訂單工廠類post

import com.google.common.collect.Maps;
import java.util.Map;
import java.util.Objects;

/**
 * 訂單工廠類
 */
public class CoffeeOrderFactory {

    private static Map<String,Order> cof = Maps.newHashMap();

    /**
     * 得到訂單
     * @param flavor 口味
     * @return
     */
    public static Order getOrder(String flavor){

        Order order = cof.get(flavor);

        if(Objects.isNull(order)){
            order = new CoffeeOrder(flavor);
            cof.put(flavor,order);
        }
        return order;
    }

    /**
     * 獲取最終建立的對象個數
     * @return
     */
    public static int getSize(){

        return cof.size();
    }

}

測試類性能

import org.assertj.core.util.Lists;
import java.util.List;

/**
 * 測試買咖啡
 */
public class MyTest {

    public static void main(String[] args) {


        buyCoffee("拿鐵");
        buyCoffee("卡布奇諾");
        buyCoffee("摩卡");

        buyCoffee("拿鐵");
        buyCoffee("拿鐵");
        buyCoffee("拿鐵");

        buyCoffee("卡布奇諾");
        buyCoffee("卡布奇諾");
        buyCoffee("卡布奇諾");

        buyCoffee("摩卡");
        buyCoffee("摩卡");
        buyCoffee("摩卡");

        //打印出賣出的咖啡
        coffeeOrderList.stream().forEach(Order::sell);


        System.out.println("一共賣出去"+coffeeOrderList.size()+"杯咖啡!");
        System.out.println("一共生成了"+CoffeeOrderFactory.getSize()+"個Java對象!");


    }
    //訂單列表
    public static List<Order> coffeeOrderList = Lists.newArrayList();

    /**
     * 買咖啡
     * @param flavor 口味
     */
    public static void buyCoffee(String flavor){
        coffeeOrderList.add(CoffeeOrderFactory.getOrder(flavor));
    }

}

運行結果學習

賣出了一份拿鐵的咖啡。
賣出了一份卡布奇諾的咖啡。
賣出了一份摩卡的咖啡。
賣出了一份拿鐵的咖啡。
賣出了一份拿鐵的咖啡。
賣出了一份拿鐵的咖啡。
賣出了一份卡布奇諾的咖啡。
賣出了一份卡布奇諾的咖啡。
賣出了一份卡布奇諾的咖啡。
賣出了一份摩卡的咖啡。
賣出了一份摩卡的咖啡。
賣出了一份摩卡的咖啡。
一共賣出去12杯咖啡!
一共生成了3個Java對象!

從上面的運行結果能夠看出來,雖然賣出去了12杯咖啡,可是最終的口味對象只有3個,由於咖啡口味只有在第一次使用的時候建立,後面就直接使用不會再建立了。測試

享元模式的分析

下面仍是來分析一下享元模式的結構吧,結構圖以下:this

 

 

享元模式涉及到的角色有抽象享元角色、具體享元角色、複合享元角色、享元工廠角色,以及客戶端角色。具體說明以下:

  • 抽象享元角色(FlyWeight):此角色是全部具體享元類的父級,爲這些類規定出須要實現的公共接口或抽象類。上面例子中的Order接口就是表明的這個角色。
  • 具體享元角色(ConcreteFlyweight):實現抽象享元角色所規定的接口。有時候具體享元角色有稱爲單純具體享元角色,由於複合享元角色是由單純具體享元角色經過複合而成的。上面例子中的CoffeeOrder就是表明的這個角色。
  • 複合享元角色(UnsharableFlyweight):複合享元角色所表明的對象是不能夠共享的,可是一個複合享元對象能夠分解成爲多個自己是單純享元對象的組合。複合對象又稱爲不可共享對象。這個角色通常不多用。 
  • 享元工廠角色(FlyweightFactory):負責建立和管理享元角色。此角色必須保證享元對象能夠被系統適當地共享。當客戶端對象請求一個享元對象時,享元工廠角色須要檢查系統中是否已經有一個符合要求的享元對象,若是已經有了享元工廠角色就應當提供這個已有的享元對象;若是系統中沒有一個適當的享元對象的話,享元工廠角色就應當建立一個新的合適的享元對象。上面例子中的CoffeeOrderFactory就是表明的這個角色。
  • 客戶端角色(client):此角色調用享元工廠角色來使用具體享元角色。

享元模式總結

單純享元模式和複合享元模式

標準的享元模式中既包含享元對象又包含非享元對象,可是在實際使用過程當中咱們會用到具體兩種特殊形式的享元模式:單純享元模式和複合享元模式。

單純享元模式是指,全部的具體享元對象都是能夠共享的,不包括非享元對象。

複合享元模式是指,將一些單純享元對象使用組合模式加以組合,還能夠造成組合享元對象,這樣的複合享元對象不能共享,可是它能夠分解成單純享元對象,分解後就能夠共享了。

享元模式的優勢

一、能夠極大的減小內存中對象的數量,使得相同或類似的對象在內存中只保存一份,從而能夠節約系統資源,提升系統性能。

二、享元模式的外部狀態相對獨立,並且不會影響其內部狀態,從而使得享元對象能夠在不一樣的環境中被共享。

享元模式的缺點

一、享元模式使得系統變得複雜,須要分離出內部狀態和外部狀態,這使得程序的邏輯複雜化。

二、爲了使對象能夠共享,享元模式須要將享元對象的部分狀態外部化,而讀取外部狀態將使得運行時間變長。

適用場景

一個系統有大量相同或者類似的對象,形成內存的大量耗費。

對象的大部分狀態均可之外部化,能夠將這些外部狀態傳入對象中。

在使用享元模式時須要維護一個存儲享元對象的享元池,而這須要耗費必定的系統資源,所以,應當在須要屢次重複使用享元對象時才值得使用享元模式。

 延伸

在JDK中就有使用享元模式的例子,最多見的就是咱們使用的String類,你們都知道String類是被final修飾的,因此不會被繼承,每次變動都會生成一個新的字符串,這樣就有點佔內存了。因此若是直接寫出了一個字符串,當後面又寫出了一個一樣的字符串時會自動去堆中(JDK7以上)查看是否已經存在這個字符串了,若是已經存在則直接使用,若是不存在這個時候纔在堆中再給開闢一塊空間存儲字符串。

例以下面的例子:

        String testStr = "享元模式";
        String testString = "享元模式";

        System.out.println(testStr == testString);
        System.out.println(testStr.equals(testString));    

運行結果:

true
true

 

 


想了解更多的設計模式請查看Java設計模式學習記錄-GoF設計模式概述

 

 

 

 

 

 

 

 

 

面試面到懷疑人生,繼續加油吧!

相關文章
相關標籤/搜索