享元模式(Flyweight Pattern)

一、定义

享元模式(Flyweight Pattern):结构型模式之一,运用共享技术有效地支持大量细粒度的对象

二、UML类图

享元模式(Flyweight Pattern)_第1张图片

三、角色职责

  • 抽象享元角色(IFlyweight):享元对象抽象基类或接口,同时定义出对象的外部状态和内部状态的接口或实现。
  • 具体享元角色(ConcreteFlyweight):实现抽象角色定义的业务。该角色的内部状态处理应该与环境无关,不会出现一个操作改变内部状态,同时修改了外部状态的情况。
  • 享元工厂(FlyweightFactory):负责管理享元对象池和创建享元对象。

四、代码实现

前言:举个栗子,现在共享网络玩家开业大酬宾,前三天上网一律免费,这时有三个网瘾少年,张三、李四、王五,这三个网瘾少年来到网吧,三个人找了一台机器开始上网,这时候李四就开始与张三争辩电脑应该谁玩。为了解决这种情况,我们就可以使用享元模式。享元模式就是运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。每一台电脑都相当于一个实例对象,每来一个人就分配一个已经存在的实例对象,如果目前没有实例对象 (已开机电脑),那就创建一个实例对象并返回 (开电脑)。享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态。内部状态 (是否使用) 指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变。外部状态 (上机动作) 指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

抽象网吧(抽象享元角色 IFlyweight)

@Data
abstract class NetFlyWeight {
    // 内部状态 0未使用 1使用中
    protected Integer state = 0;

    // 外部状态
    abstract void play(String userName);

    abstract void leave();

    public Integer getState() {
        return state;
    }
}

共享网络玩家(具体享元角色 ConcreteFlyweight)

public class ShareNetFlyWeight extends NetFlyWeight {
    // 定义新的内部状态,电脑工号
    private String computerId;

    ShareNetFlyWeight(String computerId) {
        this.computerId = computerId;
    }

    @Override
    void play(String userName) {
        state = 1;
        System.out.println(userName + "玩家在" + computerId + "号电脑上机啦");
    }

    @Override
    void leave() {
        state = 0;
        System.out.println(computerId + "号电脑已下机");
    }
}

网吧工厂(享元工厂 FlyweightFactory)

public class NetFlyWeightFactory {
    private static NetFlyWeightFactory instance = new NetFlyWeightFactory();
    private List<NetFlyWeight> pool = new ArrayList<>();
    private int id;

    public static NetFlyWeightFactory getInstance() {
        return instance;
    }

    public NetFlyWeight getComputer() {
        for (NetFlyWeight NetFlyWeight : pool) {
            if (NetFlyWeight.getState() == 0) {
                return NetFlyWeight;
            }
        }
        NetFlyWeight netFlyWeight = new ShareNetFlyWeight(String.valueOf(id));
        id++;
        pool.add(netFlyWeight);
        return netFlyWeight;
    }
}

测试类

public class FlyweightTest {
    public static void main(String[] args) {
        NetFlyWeight NetFlyWeight = NetFlyWeightFactory.getInstance().getComputer();
        NetFlyWeight.play("张三");

        NetFlyWeight NetFlyWeight2 = NetFlyWeightFactory.getInstance().getComputer();
        NetFlyWeight2.play("李四");
        NetFlyWeight2.leave();

        NetFlyWeight NetFlyWeight3 = NetFlyWeightFactory.getInstance().getComputer();
        NetFlyWeight3.play("王五");
    }
}

输出结果

张三玩家在0号电脑上机啦
李四玩家在1号电脑上机啦
1号电脑已下机
王五玩家在1号电脑上机啦

五、源码分析

在Integer类中,就使用了享元模式。
享元模式(Flyweight Pattern)_第2张图片
Integer类会有一个缓存池IntegerCache,是Integer内部的私有静态类,里面的cache[]就是jdk事先缓存的Integer。后面如果是是在区间内的数直接从缓存数组中取,否则才构造新的Integer。

六、优缺点分析

优点

  • 可以减少内存中对象的数量,使得相同或者相似的对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。

缺点

  • 使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。

七、适用场景

在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,在需要多次重复使用享元对象时才值得使用享元模式。

八、总结

当系统中存在大量相同或者相似的对象时,享元模式是一种较好的解决方案,它通过共享技术实现相同或相似的细粒度对象的复用,从而节约了内存空间,提高了系统性能。相比其他结构型设计模式,享元模式的使用频率并不算太高,但是作为一种以“节约内存,提高性能”为出发点的设计模式,它在软件开发中还是得到了一定程度的应用。

你可能感兴趣的:(设计模式,享元模式)