在Java中使用享元模式

首先

我将总结我通过阅读的《増補改訂版 Java言語で学ぶデザインパターン入門》,该书介绍了GoF设计模式的内容。

Flyweight模式

“Flyweight” 是什么意思?

在拳击中,轻量级指的是“蝇量级”。轻量级意味着“轻巧”,指的是消耗内存较少的情况。在创建对象时,需要使用new来分配内存(实例化),但是如果创建了大量的对象,会消耗大量的内存,导致处理速度变慢。为了解决这个问题,最好能够重复使用已经实例化的对象。通过尽量不通过new来共享对象来减少内存消耗的模式被称为享元模式。

登场的角色

image.png

实现类

    • Flyweight

 

    • 共有して利用するクラスを表します。

 

    • 実装すべきメソッドなどは特にありませんので、難しい点はありません。

 

    • FlyweightFactory

 

    • Flyweightを生成するための工場役となるクラスです。

 

    • この工場役を通してFlyweight役を作るとインスタンスを共有できるメソッドを持ちます。

 

    • 共有するインスタンスを格納するpoolフィールドと、Flyweightを取得するためのgetFlyewightメソッドを持ちます。

 

    • Client

 

    • FlyweightFactoryを利用してFlyweightを利用するクラスです。

 

    Flyweightと同様に実装すべきメソッドなどは特に定まられていないため、難しい点はありません。

具体的例子

image.png

实现类

Stampクラス

package sample;

public class Stamp {
    // 文字
    private char charname;
    // 利用回数
    private int useCount = 0;
    // 生成回数
    private int newCount = 0;

    public int getUseCount() {
        return useCount;
    }

    public void setUseCount(int useCount) {
        this.useCount = useCount;
    }

    public int getNewCount() {
        return newCount;
    }

    public void setNewCount(int newCount) {
        this.newCount = newCount;
    }

    // コンストラクタ
    public Stamp(char charname) {
        this.charname = charname;
    }

    // 文字を表示する
    public void print() {
        System.out.println("charname:" + this.charname);
    }
}

Stamp类是一个用于共享和使用的Flyweight角色的类。
它接收字符charname并生成一个字符,然后通过print方法显示该字符。
此外,为了方便理解,它还具有记录使用次数(useCount)和生成次数(newCount)的字段,但这并非必需。

StampFactoryクラス

package sample;

import java.util.HashMap;
import java.util.Map.Entry;

public class StampFactory {
    // 既に生成したStampインスタンスを管理
    private HashMap<String, Stamp> pool = new HashMap<>();
    // Singletonパターン
    private static StampFactory singleton = new StampFactory();

    // コンストラクタ
    private StampFactory() {
    }

    // シングルトンインスタンスを取得
    public static StampFactory getInstance() {
        return singleton;
    }

    // Stampのインスタンス生成(共有)
    public synchronized Stamp getStamp(char charname) {
        // キー(文字)に紐づく値(Stampインスタンス)を取得する
        Stamp bc = pool.get("" + charname);
        // キー(文字)に紐づく値(Stampインスタンス)が取得できなかった場合
        if (bc == null) {
            // ここでStampのインスタンスを生成
            bc = new Stamp(charname);
            // newした回数をカウント
            bc.setNewCount(bc.getNewCount() + 1);
            // HashMapに格納
            pool.put("" + charname, bc);
        }
        // newの有無にかかわらず利用した回数をカウント
        bc.setUseCount(bc.getUseCount() + 1);
        return bc;
    }

    // HashMapが管理しているStampインスタンスを全件出力
    public void printAllPool() {
        for (Entry<String, Stamp> entry : pool.entrySet()) {
            System.out.println(
                    entry.getKey() + " : " + entry.getValue().getUseCount() + " : " + entry.getValue().getNewCount());
        }
    }
}

FlayweightFactory是一个用于创建Stamp类的工厂类。
它有一个用于管理生成的实例的Map字段pool,以及一个用于表示自身的singleton字段,由于应用了Singleton模式。
作为这个类的使用方法,首先从外部调用getInstance方法以返回表示自身的StampFactory实例。
然后对返回的StampFactory实例调用getStamp方法,参数为charname。
如果已经生成了与传入的charname作为键值对应的Stamp实例,就从pool中获取;如果尚未生成实例,则生成新的实例并存储到pool中。
此外,如果生成了实例,则将newCount加1,并将useCount加1以计算每个Stamp实例的生成和使用次数。
此外,还实现了一个printAllPool方法,用于输出存储在pool中的所有Stamp实例。
输出内容为“键值字符:使用次数:生成次数”。

执行类

Mainクラス

package sample;

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // Stampインスタンスの準備
        StampFactory factory = StampFactory.getInstance();
        ArrayList<Stamp> stamps = new ArrayList<>();
        stamps.add(factory.getStamp('a'));
        stamps.add(factory.getStamp('b'));
        stamps.add(factory.getStamp('c'));
        stamps.add(factory.getStamp('f'));
        stamps.add(factory.getStamp('e'));
        stamps.add(factory.getStamp('a'));
        stamps.add(factory.getStamp('b'));
        stamps.add(factory.getStamp('c'));
        stamps.add(factory.getStamp('d'));
        stamps.add(factory.getStamp('f'));
        stamps.add(factory.getStamp('a'));
        for (Stamp s : stamps) {
            s.print();
        }
        System.out.println("-------------------------------");
        System.out.println("charname : useCount : newCount");
        // HashMapで管理されているStampインスタンスを全件出力
        factory.printAllPool();
    }
}

这个类是使用Flyweight和FlyweightFactory的Client角色类。
通过StampFactory类的static方法getInstance调用getInstance方法来获取stampFactory实例,并对获取的stampFactory实例调用getStamp方法,从而将stamp实例存储到pool字段中。
最后通过调用printAllPool()方法来输出pool字段的所有数据。

执行结果

运行Main.java的结果如下所示。通过对于useCount大于1的字母来看,我们可以发现newCount只出现了一次,说明实例被重复利用了一次。

charname:a
charname:b
charname:c
charname:f
charname:e
charname:a
charname:b
charname:c
charname:d
charname:f
charname:a
-------------------------------
charname : useCount : newCount
a : 3 : 1
b : 2 : 1
c : 2 : 1
d : 1 : 1
e : 1 : 1
f : 2 : 1

优点和缺点

使用享元模式可以减少创建对象的次数,节约内存。然而,存储在池中的对象不会成为垃圾收集的目标,会一直占用内存,因此需要有意识地管理以避免内存不足的问题。

总结

我学习了使用享元模式来共享实例以减少内存消耗的知识。如果您感兴趣的话,我在下面提供了样例代码供参考。

    Flyweightサンプルコード

另外,关于其他设计模式,我也在下面进行了总结,供您参考。

    [随時更新]Javaでデザインパターンまとめ

请提供文献参考.

    増補改訂版 Java言語で学ぶデザインパターン入門
广告
将在 10 秒后关闭
bannerAds