Java设计模式之结构型-享元模式 (Flyweight)

👉文章示例代码👈

附链

你也可以在这些平台阅读本文:

定义

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

“享”即共享,“元”指的是元件,也就是小颗粒的东西。“享元”顾名思义就是共享小部件。

很多的系统程序包含大量的对象,但是这些对象绝大多数都是差不多的,除了一些极个别的属性外。

那么也就是说,在一个系统程序中有多个相同对象的时候,我们只需要共享一份就可以了,不必去实例化每一个对象。

内部状态与外部状态

享元模式是区分内部状态与外部状态的:

  • 内部状态:可以共享,在享元对象的内部,状态不会随着环境改变而改变
  • 外部状态:不可以共享,在享元对象的外部,状态会随着环境改变而改变

场景示例

笔者这里以衣服为例。将衣服的品牌作为内部属性共享,将衣服的大小、成分、价格作为外部属性使用。

创建衣服类

/**
 * @author zhh
 * @description 衣服类
 * @date 2020-02-16 22:36
 */
public interface Clothes {

    /**
     * 衣服信息
     */
    void info();
}

创建衬衣类

/**
 * @author zhh
 * @description 衬衣类
 * @date 2020-02-16 22:37
 */
public class Shirt implements Clothes {

    /**
     * 品牌
     */
    private String brand;

    /**
     * 大小
     */
    private String size;

    /**
     * 成分
     */
    private String component;

    /**
     * 价格
     */
    private String price;

    public Shirt(String brand) {
        this.brand = brand;
    }

    public void setSize(String size) {
        this.size = size;
    }

    public void setComponent(String component) {
        this.component = component;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public void info() {
        System.out.println(String.format("当前衬衣信息: {品牌: %s, 大小: %s, 成分: %s, 价格: %s}", brand, size, component, price));
    }
}

在该场景中,笔者将品牌属性作为其内部状态,在对象创建的初期就初始化完成,状态不会随着外部环境变化,可以共享。

而将大小、成分、价格这些属性当做外部状态,通过 setter 方法供外部修改,状态会随外部环境变化,这些属性不能够共享。

创建衬衣工厂类

享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池。

/**
 * @author zhh
 * @description 衣服工厂类
 * @date 2020-02-16 22:48
 */
public class ClothesFactory {

    private static final Map<String, Clothes> CLOTHES_MAP = new HashMap<String, Clothes>();

    /**
     * 获取衬衣
     * @param brand 品牌
     * @return
     */
    public static Clothes getShirt(String brand) {
        Shirt shirt = (Shirt) CLOTHES_MAP.get(brand);

        if (shirt == null) {
            shirt = new Shirt(brand);
            CLOTHES_MAP.put(brand, shirt);
            System.out.println("创建新的品牌衬衣, 品牌为: " + brand);
        }

        return shirt;
    }
}

测试类及输出

方便起见,在该测试类中随机模拟数据。

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-16 23:04
 */
public class Test {

    private static final String[] BRAND = {"YOUNGOR雅戈尔", "FIRS杉杉", "ROMON罗蒙", "Hodo红豆"};

    private static final String[] SIZE = {"S", "M", "L", "XL", "XXL"};

    private static final String[] COMPONENT = {"棉", "涤纶", "真丝"};

    private static final String[] PRICE = { "¥99", "¥129", "¥149", "¥199" };

    /**
     * 随机模拟
     * @param strs 随机模拟数组
     * @return
     */
    public static String getRandom(String[] strs) {
        return strs[new Random().nextInt(strs.length)];
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Shirt shirt = (Shirt) ClothesFactory.getShirt(getRandom(BRAND));
            shirt.setSize(getRandom(SIZE));
            shirt.setComponent(getRandom(COMPONENT));
            shirt.setPrice(getRandom(PRICE));
            shirt.info();
        }
    }
}

测试类的输出结果如下:

创建新的品牌衬衣, 品牌为: ROMON罗蒙
当前衬衣信息: {品牌: ROMON罗蒙, 大小: M, 成分: 真丝, 价格: ¥129}
创建新的品牌衬衣, 品牌为: FIRS杉杉
当前衬衣信息: {品牌: FIRS杉杉, 大小: S, 成分: 棉, 价格: ¥199}
当前衬衣信息: {品牌: ROMON罗蒙, 大小: M, 成分: 棉, 价格: ¥99}
创建新的品牌衬衣, 品牌为: Hodo红豆
当前衬衣信息: {品牌: Hodo红豆, 大小: M, 成分: 棉, 价格: ¥99}
当前衬衣信息: {品牌: FIRS杉杉, 大小: XL, 成分: 真丝, 价格: ¥99}
当前衬衣信息: {品牌: ROMON罗蒙, 大小: XXL, 成分: 真丝, 价格: ¥99}
当前衬衣信息: {品牌: Hodo红豆, 大小: M, 成分: 真丝, 价格: ¥99}
当前衬衣信息: {品牌: Hodo红豆, 大小: M, 成分: 涤纶, 价格: ¥149}
创建新的品牌衬衣, 品牌为: YOUNGOR雅戈尔
当前衬衣信息: {品牌: YOUNGOR雅戈尔, 大小: XXL, 成分: 真丝, 价格: ¥129}
当前衬衣信息: {品牌: ROMON罗蒙, 大小: XL, 成分: 涤纶, 价格: ¥199}

类结构图

以上示例类的结构图如下所示

总结

适用场景

  • 应用于系统底层开发,解决系统性能问题。
  • 系统有大量相似对象,需要缓冲池。

优点

  • 共享相同或者相似的细粒度对象,减少对象的创建,降低系统内存,提高效率。
  • 外部状态相对独立,对象可以在不同的环境中被复用。

缺点

  • 读取外部状态使得运行时间变长。
  • 内部状态与外部状态分离,使得程序的逻辑复杂化。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值