享元模式——七种结构型模式之一

1.前言

大家还记得本文集最早提的单例模式吗?通过一个全局变量来避免重复创建对象而产生的消耗,若系统存在大量的相似对象时,又该如何处理?参照单例模式,可通过对象池缓存可共享的对象,避免创建多对象,尽可能减少内存的使用,提升性能,防止内存溢出。

2.概念

享元模式使用共享对象可有效地支持大量细粒度的对象。为了解释一下,何为细粒度对象,引用《Android源码设计模式解析与实战》中的描述。

对象中的部分状态是可以共享的,这些状态称为内部状态,它们不会随着环境变化;相反,不可共享的状态称为外部状态,它们会随着环境的改变而改变。

由于共享的不是一个对象,所以需要容器,最经典的便是Map。它的键是内部状态,值为对象本身。获取对象的过程:先判断容器中是否有缓存,若有,直接使用;若没有,创建对象并存入容器。

3.场景

一家网络会所想订购一批电脑,为了满足不同档次的用户需求,要多种配置和款式:

  • 会员与普通用户使用的电脑配置基本相似,仅内存更大;
  • 超级会员则从机箱开始完全不同。

老板便联系电脑销售点,希望服务人员带几款样机过来体验。服务人员详细询问老板的需求后,带了两个配置好的机箱,几张内存条就过来了。

4.写法

// 定义所有对象具有的行为
public interface Mainboard {
    void showConfig();
}
// 被创建的具体对象
public class Computer implements Mainboard {

    private String memery;
    private String computerCase;
    private String cpu;

    public Computer(String memery, String computerCase, String cpu) {
        this.memery = memery;
        this.computerCase = computerCase;
        this.cpu = cpu;
    }

    public void setMemery(String memery) {
        this.memery = memery;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    @Override
    public void showConfig() {
        System.out.println("电脑的配置是: " + computerCase + " / " + cpu + " / " + memery);
    }
}

若展示会员机,则只需要更换内存即可;若展示超级会员机,只能使用另一个机箱。

// 享元工厂,管理对象池和创建对象
public class Service {

    static Map<String, Mainboard> computerMap = new ConcurrentHashMap<>();

    public static void getInfo(String memery, String computerCase, String cpu) {
        if (computerMap.containsKey(computerCase)) {
            System.out.println("使用缓存->");
            Mainboard mainboard = computerMap.get(computerCase);
            if (mainboard instanceof Computer) {
                Computer computer = (Computer) mainboard;
                computer.setMemery(memery);
                computer.setCpu(cpu);
                computer.showConfig();
            }
        } else {
            System.out.println("创建对象->");
            Mainboard mainboard = new Computer(memery, computerCase, cpu);
            computerMap.put(computerCase, mainboard);
            mainboard.showConfig();
        }
    }
}
public class Client {
    public static void main(String[] args) {
        // 开始向会所老板展示
        Service.getInfo("8G", "技嘉", "i5");
        Service.getInfo("16G", "技嘉", "i5");
        Service.getInfo("16G", "华硕", "i7");
    }
}

Java中的String是存在常量池中的,逻辑与这个类似。当其它地方使用相同字符串时,则直接使用缓存中的,不会重复创建。这种只适合字面值赋值,即直接通过双引号设置的字符串值;若是通过new构建,则是新的对象。

5.总结

通过享元对象的内部状态来判断此对象是否需要创建,将“对象是否相同”细化到属性上。优势很明显,但需将部分状态(属性)外部化,也增加了系统的复杂性,还得确保与内部状态互不影响

    原文作者:lanceJin
    原文地址: https://www.jianshu.com/p/95e2a448eeb8
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞