[23种设计模式][结构型]23.享元模式

111 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情

1.享元(Flyweight)模式概念:

面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题。但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价。那么我们如何去避免大量细粒度的对象,同时又不影响客户程序面向对象的方式进行操作?

2.意图:

运行共享技术有效地支持大量细粒度的对象

3.享元模式的构成:

  1. 抽象享元角色(Flyweight):声明一个接口,通过它可以接收外来的参数(状态),并对新状态作出处理
  2. 共享具体享元类(Shared Concrete Flyweight):实现Flyweight接口,并为内部状态(如果有的话)增加存储空间,Shared Concrete Flyweight对象必须是可共享的,它所存储的状态必须是内部的,即它独立存在于自己的环境中
  3. 不共享具体享元类(Unshared Concrete Flyweight):不能共享的享元类,又叫做复合享元类。一个复合享元对象是由多个单享元对象组成,这些组成的对象是可以共享的,但是复合享元类本身并不能共享
  4. 轻量级类工厂(Flyweight Factory):
    • 创建并管理Flyweight对象
    • 确保享用Flyweight。当用户请求一个Flyweight时,Flyweight Factory提供一个已创建的示例或创建一个实例(如果不存在)
  5. 客户端(Client):
    • 维持一个对Flyweight的引用
    • 计算或存储一个或多个Flyweight的外部状态。

4.享元模式UML类图:

Image.png

5.享元模式UML序列图:

Image.png

6.处理过程:

客户初次从轻量级类工厂取Flyweight时,轻量级类工厂创建一个新的具体Flyweight对象,并保存起来,下次客户取用时,就无需再进行创建了,直接在保存池中返回。客户端负责处理Flyweight的状态。

7.享元模式:

  • 享元的英文是Flyweight,它是一个来自体育方面的专业用语,在拳击、摔跤和举重比赛中特指最轻量级的级别。把这个单词移植到软件工程里面,也是用来表示特别小的对象,即细粒度对象。至于为什么我们把Flyweight翻译为“享元”,可以理解为共享元对象,也就是共享细粒度对象。享元模式就是通过使用共享的方式,达到高效地支持大量的细粒度对象。它的目的就是节省占用的空间资源,从而实现系统性能的改善。
  • 我们把享元对象的所有状态分为两类,其实前面的例子中letter和fantasize属性在运行时,就形成了两类不同的状态。
  • 享元对象的第一类状态称为内蕴状态(Internal State)。它不会随环境改变而改变,存储在享元对象内部,因此内蕴状态是可以共享的,对于任何一个享元对象来说,它的值是完全相同的。我们例子中Character类的letter属性,它代表的状态就是内蕴状态。
  • 享元对象的第二类状态称为外蕴状态(External State)。它会随环境的改变而改变,因此是不可以共享的状态,对于不同的享元对象来讲,它的值可能是不同的。享元对象的外蕴状态必须有客户端保存,在享元对象被创建之后,需要使用的时候在传入到享元对象内部。我们例子中Character类的fantasize属性,它代表的状态就是外蕴状态。
  • 享元的外蕴状态于内蕴状态是两类互相独立的状态,彼此没有关联。

8.适用性:

  • 系统需要存在大量的对象而共享某些本质的,不变的信息。
  • 对象可以同时用于多个环境下。
  • 在每个实例下,Flyweight可以作为一个独立的对象。

9.代码示例:

Image.png

抽象享元角色:

public abstract class Character {

    protected char letter;

    protected int fontSize;

    public abstract void display();

    public abstract void mySetFontSize(int fontSize);

    protected int getFontSize() {
        return fontSize;
    }

    protected void setFontSize(int fontSize) {
        this.fontSize = fontSize;
    }
}

共享具体享元角色A:

public class CharacterA extends Character {

    public CharacterA() {
        this.letter = 'A';
        this.fontSize = 12;
    }

    @Override
    public void display() {
        System.out.println(this.letter + " " + this.fontSize);
    }

    @Override
    public void mySetFontSize(int fontSize) {
        this.fontSize = fontSize;
    }
}

共享具体享元角色B:

public class CharacterB extends Character {

    public CharacterB() {
        this.letter = 'B';
        this.fontSize = 12;
    }

    @Override
    public void display() {
        System.out.println(this.letter + " " + this.fontSize);
    }

    @Override
    public void mySetFontSize(int fontSize) {
        this.fontSize = fontSize;
    }
}

轻量级类工厂:

public class CharacterFactory {

    private Hashtable characters = new Hashtable();

    private static CharacterFactory instance;

    private CharacterFactory() {
        characters.put("A", new CharacterA());
        characters.put("B", new CharacterB());
    }

    // 一定要保证工厂是单例的
    public static CharacterFactory getInstance() {
        if (null == instance) {
            instance = new CharacterFactory();
        }

        return instance;
    }

    // 此处才是享元模式的关键所在
    public Character getCharacter(String key) {
        Character character = (Character) characters.get(key);

        if (null == character) {
            switch (key) {
                case "A":
                    character = new CharacterA();
                    break;
                case "B":
                    character = new CharacterB();
                    break;
            }
            characters.put(key, character);
        }
        return character;
    }
}

客户角色:

public class Client {

    public static void main(String[] args) {

        CharacterFactory factory = CharacterFactory.getInstance();

        Character a = factory.getCharacter("A");
        a.setFontSize(20);
        a.display();

        System.out.println("-------");

//        Character a2 = factory.getCharacter("A");
//        a2.display();

        Character b = factory.getCharacter("B");
        b.setFontSize(24);
        b.display();
    }
}