当前位置:首页 > 科技  > 软件

一文搞懂设计模式—享元模式

来源: 责编: 时间:2024-02-29 14:43:15 252观看
导读当系统中存在大量相似对象时,每个对象都需要占用一定的内存空间,如果这些对象的大部分属性是相同的,那么频繁创建这些对象会导致内存消耗过大。享元模式将这些相同部分抽取出来作为共享的内部状态,在需要时进行共享,从而减

当系统中存在大量相似对象时,每个对象都需要占用一定的内存空间,如果这些对象的大部分属性是相同的,那么频繁创建这些对象会导致内存消耗过大。享元模式将这些相同部分抽取出来作为共享的内部状态,在需要时进行共享,从而减少内存占用。CiO28资讯网——每日最新资讯28at.com

享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来最大化内存利用和性能提升,享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。CiO28资讯网——每日最新资讯28at.com

使用场景

  • 当系统中存在大量相似对象且造成了内存浪费时,可以考虑使用享元模式。
  • 对象的状态可以外部化,并且剥离出共享部分和特有部分。
  • 需要缓冲池的场景。

享元模式在对象池中的使用是一种常见的场景,通过对象池管理和复用对象实例,可以提高系统性能和资源利用率。对象池通常用于缓存、连接池等场景,其中对象的创建成本较高或者频繁创建销毁会影响性能时,对象池就显得尤为重要。CiO28资讯网——每日最新资讯28at.com

在 Java 中,String 类的 intern() 方法是享元模式的一个应用。intern() 方法返回字符串对象的规范化表示形式,即返回字符串池中与调用字符串等效的字符串。如果字符串池中已经存在等效的字符串,则返回该字符串;否则,将此字符串添加到字符串池中,并返回新的字符串引用。CiO28资讯网——每日最新资讯28at.com

下面是一个示例代码,演示了 String 类的 intern() 方法的应用:CiO28资讯网——每日最新资讯28at.com

public class StringInternExample {    public static void main(String[] args) {        String str1 = "hello";        String str2 = new String("hello");        String str3 = str2.intern();        System.out.println("str1 == str2: " + (str1 == str2)); // false        System.out.println("str1 == str3: " + (str1 == str3)); // true    }}

在上述示例中,str1 和 str2 是两个不同的字符串对象,尽管它们的值相同,但由于 str2 使用了 new String() 构造方法创建,在堆内存中会生成一个新的对象。而通过调用 intern() 方法后,str3 返回的是字符串池中已存在的字符串对象,因此 str1 和 str3 指向的是同一个对象,所以输出结果为 "str1 == str3: true"。这就是 intern() 方法的享元模式应用,避免了重复创建相同的字符串对象,节省了内存空间。CiO28资讯网——每日最新资讯28at.com

具体实现

享元模式包含以下几个角色:CiO28资讯网——每日最新资讯28at.com

  • 抽象享元(Flyweight): 定义了享元对象的外部状态和内部状态,通过这个抽象类可以接受并作用于外部状态。
  • 具体享元(Concrete Flyweight): 继承了抽象享元类,包含内部状态和外部状态。具体享元对象需要确保内部状态是可以共享的,同时提供操作外部状态的方法。
  • 非共享具体享元(Unshared Concrete Flyweight): 与共享具体享元相对应,非共享具体享元是不能被共享的享元对象,通常是在具体享元中无法共享的情况下使用。
  • 享元工厂(Flyweight Factory): 负责创建和管理享元对象,在请求时返回已经创建的享元对象实例或者新创建一个享元对象。享元工厂通常会维护一个享元池用于存储已经创建的享元对象。

在享元模式中,核心在于区分内部状态和外部状态。内部状态是可以共享的部分,而外部状态是对象的非共享部分。CiO28资讯网——每日最新资讯28at.com

  • 内部状态(Intrinsic State): 内部状态是享元对象固有的、可以共享的状态,它存储在享元对象内部并且不会随着外部环境的变化而改变。内部状态可以被多个享元对象共享,因此通常将其设计为不可变的属性。内部状态对于享元对象的具体实现是必需的,但不会随着外部环境的变化而改变。
  • 外部状态(Extrinsic State): 外部状态是享元对象的可变部分,它随着外部环境的变化而变化,需要通过客户端传入享元对象来进行处理。外部状态并不影响享元对象的内部结构或行为,它只是作为享元对象行为的参数或上下文信息传入。外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱。

通过区分内部状态和外部状态,享元模式实现了将对象的共享部分和变化部分分离的目的,有效地减少了系统中重复对象的数量,提高了系统的性能和资源利用率。内部状态是享元对象本身的属性,而外部状态则是根据具体情况动态变化的参数。CiO28资讯网——每日最新资讯28at.com

实现步骤和示例代码如下:CiO28资讯网——每日最新资讯28at.com

1.首先定义抽象享元角色。CiO28资讯网——每日最新资讯28at.com

public abstract class Flyweight {    //内部状态    private String intrinsic;    //外部状态    protected final String extrinsic;    //要求享元角色必须接受外部状态    public Flyweight(String extrinsic){        this.extrinsic = extrinsic;    }    //定义业务操作    public abstract void operate();    //内部状态的getter/setter    public String getIntrinsic() {        return intrinsic;    }    public void setIntrinsic(String intrinsic) {        this.intrinsic = intrinsic;    }}

抽象享元角色一般为抽象类,它是描述一类事物的方法。CiO28资讯网——每日最新资讯28at.com

2.具体享元角色。CiO28资讯网——每日最新资讯28at.com

public class ConcreteFlyweight1 extends Flyweight{    //接受外部状态    public ConcreteFlyweight1(String extrinsic){        super(extrinsic);    }    //根据外部状态进行逻辑处理    public void operate(){    //业务逻辑    }}
public class ConcreteFlyweight2 extends Flyweight{    //接受外部状态    public ConcreteFlyweight2(String extrinsic){        super(extrinsic);    }    //根据外部状态进行逻辑处理    public void operate(){    //业务逻辑    }}

具体享元角色实现自己的业务逻辑,然后接收外部状态,以便内部业务逻辑对外部状态的依赖。CiO28资讯网——每日最新资讯28at.com

3.享元工厂。CiO28资讯网——每日最新资讯28at.com

public class FlyweightFactory {    //定义一个池容器    private static Map<String, Flyweight> pool = new HashMap<>();    //享元工厂    public static Flyweight getFlyweight(String extrinsic) {        //需要返回的对象        Flyweight flyweight;        //在池中没有该对象        if (pool.containsKey(extrinsic)) {            flyweight = pool.get(extrinsic);        } else {            //根据外部状态创建享元对象            flyweight = new ConcreteFlyweight1(extrinsic);            //放置到池中            pool.put(extrinsic, flyweight);        }        return flyweight;    }}

4.客户端调用CiO28资讯网——每日最新资讯28at.com

public static void main(String[] args) {        Flyweight flyweight1 = FlyweightFactory.getFlyweight("hello world");        System.out.println(flyweight1.hashCode());        Flyweight flyweight2 = FlyweightFactory.getFlyweight("hello world");        System.out.println(flyweight2.hashCode());    }    Output:    1705736037    1705736037

可以发现对象打印的 hashCode 一致,说明对象得到了复用。CiO28资讯网——每日最新资讯28at.com

Tips:外部状态最好以Java的基本类型作为标志,如String、int等,可以大幅地提升效率。如果使用自己编写的类作为外部状态,则必须覆写equals方法和hashCode方法,否则会出现通过键值搜索失败的情况,例如map.get(object)、map.contains(object)等会返回失败的结果。CiO28资讯网——每日最新资讯28at.com

线程安全问题

享元模式在多线程环境下可能存在线程安全问题,主要原因是享元对象的内部状态和外部状态被多个线程共享和修改,可能导致数据竞争和不一致性。具体来说,如果多个线程同时尝试修改同一个享元对象的外部状态,就会引发线程安全问题。CiO28资讯网——每日最新资讯28at.com

下面是示例代码:CiO28资讯网——每日最新资讯28at.com

public static void main(String[] args) {        for (int i = 0; i < 10; i++) {            new Thread(() -> {                Flyweight flyweight1 = FlyweightFactory.getFlyweight("hello world");                Flyweight flyweight2 = FlyweightFactory.getFlyweight("hello world");                System.out.println(flyweight1 == flyweight2);            }).start();        }    }Output:truefalsetruetruetruetruetruetruetruetrue

这段代码展示了多线程环境下使用享元模式的示例。在 main 方法中,通过循环创建了 10 个线程,在每个线程中尝试获取表示 "hello world" 的享元对象,并比较两个获取的对象是否相等。CiO28资讯网——每日最新资讯28at.com

可以观察到输出中存在 false,说明对象不一样了,存在线程安全问题。CiO28资讯网——每日最新资讯28at.com

要想实现线程安全,需要对享元工厂类稍加改造,代码如下:CiO28资讯网——每日最新资讯28at.com

public class FlyweightFactory {    //定义一个池容器    private static Map<String, Flyweight> pool = new ConcurrentHashMap<>();    //享元工厂    public static synchronized Flyweight getFlyweight(String extrinsic) {        Flyweight flyweight = pool.putIfAbsent(extrinsic, new ConcreteFlyweight1(extrinsic));        if (flyweight == null) {            return pool.get(extrinsic);        }        return flyweight;    }}

这样就解决了线程安全问题,不过性能上会有所降低,在需要的地方考虑一下线程安全即可,在大部分的场景下都不用考虑。CiO28资讯网——每日最新资讯28at.com

总结

享元模式通过共享相似对象来减少内存消耗,提高系统性能。它适用于存在大量相似对象且造成内存浪费的场景,但需要注意对内部状态和外部状态的管理。合理应用享元模式可以有效优化系统架构,提升性能。CiO28资讯网——每日最新资讯28at.com

优点CiO28资讯网——每日最新资讯28at.com

  • 大幅减少内存使用,提高系统性能,实现了对象的复用,节约资源。
  • 在一定程度上实现了对象状态的外部化,方便对对象状态的管理和维护。

缺点CiO28资讯网——每日最新资讯28at.com

  • 对象状态的外部化可能导致系统不稳定,需要谨慎设计。
  • 提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

本文链接:http://www.28at.com/showinfo-26-75345-0.html一文搞懂设计模式—享元模式

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 前端开发:Visual Studio Code和Visual studio如何选?

下一篇: 六大前端自动化测试框架推荐,提升你的开发效率与质量

标签:
  • 热门焦点
  • 6月安卓手机好评榜:魅族20 Pro蝉联冠军

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年6月1日至6月30日,仅限国内市场。第一名:魅族20 Pro好评率:95%5月份的时候魅族20 Pro就是
  • 把LangChain跑起来的三个方法

    使用LangChain开发LLM应用时,需要机器进行GLM部署,好多同学第一步就被劝退了,那么如何绕过这个步骤先学习LLM模型的应用,对Langchain进行快速上手?本片讲解3个把LangChain跑起来
  • K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • 中国家电海外掘金正当时|出海专题

    作者|吴南南编辑|胡展嘉运营|陈佳慧出品|零态LT(ID:LingTai_LT)2023年,出海市场战况空前,中国创业者在海外纷纷摩拳擦掌,以期能够把中国的商业模式、创业理念、战略打法输出海外,他们依
  • “又被陈思诚骗了”

    作者|张思齐 出品|众面(ID:ZhongMian_ZM)如今的国产悬疑电影,成了陈思诚的天下。最近大爆电影《消失的她》票房突破30亿断层夺魁暑期档,陈思诚再度风头无两。你可以说陈思诚的
  • 造车两年股价跌六成,小米的估值逻辑变了吗?

    如果从小米官宣造车后的首个交易日起持有小米集团的股票,那么截至2023年上半年最后一个交易日,投资者将浮亏59.16%,同区间的恒生科技指数跌幅为52.78%
  • 国行版三星Galaxy Z Fold5/Z Flip5发布 售价7499元起

    2023年8月3日,三星电子举行Galaxy新品中国发布会,正式在国内推出了新一代折叠屏智能手机三星Galaxy Z Fold5与Galaxy Z Flip5,以及三星Galaxy Tab S9
  • 质感不错!OPPO K11渲染图曝光:旗舰IMX890传感器首次下放

    一直以来,OPPO K系列机型都保持着较为均衡的产品体验,历来都是2K价位的明星机型,去年推出的OPPO K10和OPPO K10 Pro两款机型凭借各自的出色配置,堪称有
  • onebot M24巧系列一体机采用轻薄机身设计,现已在各平台开售

    onebot M24 巧系列一体机目前已在线上线下各平台同步开售。onebot M24 巧系列采用一体化轻薄机身设计,最薄处为 10.15mm,拥有宝石红、午夜蓝、石墨绿、雅致
Top