一塊兒學設計模式 - 享元模式

享元模式(Flyweight Pattern)屬於結構型模式的一種,又稱輕量級模式,經過共享技術有效地實現了大量細粒度對象的複用...java

<!-- more -->git

概述

兩種結構狀態編程

  • 內部狀態:享元對象內部不隨外界環境改變而改變的共享部分。微信

  • 外部狀態:享元對象內部環境的改變而改變,不可以共享的狀態就是外部狀態。ide

結構圖測試

經典結構圖

存在的角色this

  • Flyweight(抽象享元類):一般是一個接口或抽象類,向外界提供享元對象的內部數據(內部狀態)或外部數據(外部狀態)。spa

  • ConcreteFlyweight(具體享元類):實現了Flyweight(抽象享元類)也稱爲享元對象,指定內部狀態,爲內部狀態增長存儲空間。一般咱們能夠結合單例模式來設計具體享元類,爲每個具體享元類提供惟一的享元對象。設計

  • UnsharedConcreteFlyweight(非共享具體享元類):指不須要共享的Flyweight子類。code

  • FlyweightFactory(享元工廠類):用於建立並管理享元對象,針對抽象享元類編程,將各類類型的具體享元對象存儲在一個享元池中,享元池通常設計爲鍵值對的集合(也能夠是其餘類型的集合),同時能夠結合工廠模式進行設計。

享元模式的核心在於享元工廠類,享元工廠類的做用在於提供一個用於存儲享元對象的享元池,用戶須要對象時,首先從享元池中獲取,若是享元池中不存在,則建立一個新的享元對象返回給用戶,並在享元池中保存該新增對象。

案例

案例UML圖以下:

UML圖

1.建立Flyweight接口/抽象類

interface Flyweight {
    void operation(String name);

    String getType();
}

2.建立ConcreteFlyweight(享元對象),實現了Flyweight接口

class ConcreteFlyweight implements Flyweight {
    private String type;

    public ConcreteFlyweight(String type) {
        this.type = type;
    }

    @Override
    public void operation(String name) {
        System.out.printf("[類型(內在狀態)] - [%s] - [名字(外在狀態)] - [%s]\n", type, name);
    }

    @Override
    public String getType() {
        return type;
    }
}

3.建立FlyweightFactory(享元工廠類),用來維護享元池

class FlyweightFactory {
    private static final Map<String, Flyweight> FLYWEIGHT_MAP = new HashMap<>();

    public static Flyweight getFlyweight(String type) {
        if (FLYWEIGHT_MAP.containsKey(type)) {
            return FLYWEIGHT_MAP.get(type);
        } else {
            ConcreteFlyweight flyweight = new ConcreteFlyweight(type);
            FLYWEIGHT_MAP.put(type, flyweight);
            return flyweight;
        }
    }

    public static int total() {
        return FLYWEIGHT_MAP.size();
    }
}

4.建立Client(測試類)

public class Client {

    public static void main(String[] args) {
        Flyweight fw0 = FlyweightFactory.getFlyweight("戰士");
        Flyweight fw1 = FlyweightFactory.getFlyweight("戰士");
        Flyweight fw2 = FlyweightFactory.getFlyweight("法師");
        Flyweight fw3 = FlyweightFactory.getFlyweight("坦克");
        fw1.operation("瑞文");
        fw1.operation("鱷魚");
        fw2.operation("光輝");
        fw3.operation("泰坦");
        fw3.operation("蓋倫");
        System.out.printf("[結果(對象對比)] - [%s]\n", fw0 == fw1);
        System.out.printf("[結果(內在狀態)] - [%s]\n", fw1.getType());
        System.out.printf("一共召喚 %s 種類型英雄\n", FlyweightFactory.total());
    }
}

5.運行結果,從結果中能夠發現,只生成了3個不一樣的類

[類型(內在狀態)] - [戰士] - [名字(外在狀態)] - [瑞文]
[類型(內在狀態)] - [戰士] - [名字(外在狀態)] - [鱷魚]
[類型(內在狀態)] - [法師] - [名字(外在狀態)] - [光輝]
[類型(內在狀態)] - [坦克] - [名字(外在狀態)] - [泰坦]
[類型(內在狀態)] - [坦克] - [名字(外在狀態)] - [蓋倫]
[結果(對象對比)] - [true]
[結果(內在狀態)] - [戰士]
一共召喚 3 種類型英雄

JDK中使用

在JAVA語言中,String類型就是使用了享元模式。String對象是final類型,對象一旦建立就不可改變。在JAVA中字符串常量都是存在常量池中的,JAVA會確保一個字符串常量在常量池中只有一個拷貝。

public class Client {
    public static void main(String[] args) {
        String a = "蓋倫";
        String b = "蓋倫";
        System.out.println(a==b);// 返回 true
    }
}

上面的例子中結果爲:true,這就說明a和b兩個引用都指向了常量池中的同一個字符串常量。這樣設計的目的就是:爲了不在建立多相同對象時,所產生的沒必要要的資源消耗。

總結

若是程序裏使用大量相同或者類似的對象,形成內存的大量耗費,且大多都是外部狀態,這時候就應該考慮使用享元模式了,好比圍棋、五子棋等小遊戲

優勢

  • 可以極大的減小系統中對象的個數。

  • 外部狀態相對獨立,不會影響到內部狀態,可以使享元對象在不一樣的環境被共享。

缺點

  • 因爲享元模式須要區分外部狀態和內部狀態,使得程序在某種程度上來講更加複雜化了。

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

- 說點什麼

全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter9/battcn-flyweight

  • 我的QQ:1837307557

  • battcn開源羣(適合新手):391619659

微信公衆號:battcn(歡迎調戲)

相關文章
相關標籤/搜索