写点什么

FlyweightPattern- 享元模式

作者:梁歪歪 ♚
  • 2022 年 5 月 31 日
  • 本文字数:3253 字

    阅读完需:约 11 分钟

FlyweightPattern-享元模式

享元模式

享元模式(Flyweight Pattern):又称之为蝇量模式,是对象池的一种实现。主要用于减少创建对象的数量,以减少内存占用和提高性能。类似于我们的数据库连接池和线程池。


这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。


示例:我们通过购买火车票为例,来举例说明...


  • 车票接口ITicket.java


package cn.liangyy.flyweight.one;
/** * 车票接口 */public interface ITicket { /** * 查询车票信息 */ void info();}
复制代码


  • 火车票类TrainTicket.java


package cn.liangyy.flyweight.one;
/** * 火车票类 */public class TrainTicket implements ITicket { private String from; //起点 private String to; //终点
public TrainTicket(String from,String to){ this.from = from; this.to = to; }
/** * 查询车票信息 */ @Override public void info() { System.out.println(from + "-->" + to + ":硬座:100元,硬卧:200元"); }}
复制代码


  • 工厂类TicketFactory.java


package cn.liangyy.flyweight.one;
import java.util.HashMap;import java.util.Map;
/** * 工厂类 * 管理享元对象,使用一个Map来存储对象,把火车票的起点和终点作为key, * 如果存在就取出来,否则就创建新对象,并且加到Map中。 */public class TicketFactory { //缓存对象 private static Map<String,ITicket> CACHE_POOL = new HashMap<>();
public static ITicket getTicketInfo(String from,String to){ String key = from + "-->" + to; //如果对象存在缓存 if (TicketFactory.CACHE_POOL.containsKey(key)){ System.out.println("使用缓存"); return TicketFactory.CACHE_POOL.get(key); } //对象不存在缓存则创建一个对象,并加入缓存 System.out.println("未使用缓存"); ITicket ticket = new TrainTicket("成都","西宁"); CACHE_POOL.put(key,ticket); return ticket; }}
复制代码


  • 测试TestTicket.java


package cn.liangyy.flyweight.one;
/** * 享元模式-测试 */public class TestTicket { public static void main(String[] args) { //首次创建对象 ITicket ticket = TicketFactory.getTicketInfo("成都","西宁"); ticket.info(); //使用缓存 ticket = TicketFactory.getTicketInfo("成都","西宁"); ticket.info(); //换了终点,,无法使用缓存 ticket = TicketFactory.getTicketInfo("成都","上海"); ticket.info(); }}
复制代码


看到这儿,大家都会觉得这不就是一个简单的缓存使用嘛?确实如此,享元模式的核心思想就是使用缓存,它看起来和单例有点相似,单例模式关注的是整个对象只有一个实例,而享元模式关注的是状态,也就是说,同一个类中,只有状态一致。比如上述例子中成都到西宁的火车票,这就属于状态一致,所以使用缓存,而成都到上海又是另一个状态,所以无法使用缓存。那么大家会有疑问:享元模式的状态到底是什么呢?


享元模式的状态分为两种:


  • 内部状态:指对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变。

  • 外部状态:指对象需要依赖的一个标记,是随环境改变而改变的、不可共享的状态。


前面的例子中,我们可以把实例对象划分一下,比如上面车票的对象,可以把 from 和 to 两个属性作为可共享状态,不可改变。然后再新增一个属性用来对应座位,这个就属于外部状态。


状态示例:我们通过内部状态和外部状态改造上面的示例...


  • 车票接口IShareTicket.java


package cn.liangyy.flyweight.two;
/** * 车票接口 */public interface IShareTicket { /** * 获取车票信息 */ void info();
/** * 设置车票座位 * @param seatType */ void setSeat(String seatType);}
复制代码


  • 火车票类TrainShareTicket.java


package cn.liangyy.flyweight.two;
import java.math.BigDecimal;
/** * 火车票类 */public class TrainShareTicket implements IShareTicket { private String from; //内部状态 private String to; //内部状态
private String seatType = "站票"; //外部状态
public TrainShareTicket(String from, String to) { this.from = from; this.to = to; }
/** * 获取车票信息 */ @Override public void info() { System.out.println(from + "-->" + to + ":" + seatType + this.getPrice(seatType)); }

/** * 设置车票座位 * * @param seatType */ @Override public void setSeat(String seatType) { this.seatType = seatType; }
//获取不同座位的价格 private BigDecimal getPrice(String seatType) { BigDecimal value = null; switch (seatType) { case "硬座": value = new BigDecimal("100"); break; case "硬卧": value = new BigDecimal("200"); break; default: value = new BigDecimal("50"); } return value; }}
复制代码


  • 工厂类TicketShareFactory.java


package cn.liangyy.flyweight.two;
import java.util.HashMap;import java.util.Map;
/** * 工厂类 * 管理享元对象,使用一个Map来存储对象,把火车票的起点和终点作为key, * 如果存在就取出来,否则就创建新对象,并且加到Map中。 */public class TicketShareFactory { //存储缓存对象 private static Map<String,IShareTicket> CACHE_POOL = new HashMap<>();
public static IShareTicket getTicketInfo(String from,String to){ String key = from + "-->" + to; //如果对象存在缓存 if (TicketShareFactory.CACHE_POOL.containsKey(key)){ System.out.println("使用缓存"); return TicketShareFactory.CACHE_POOL.get(key); } //对象不存在缓存则创建一个对象,并加入缓存 System.out.println("未使用缓存"); IShareTicket ticket = new TrainShareTicket(from,to); CACHE_POOL.put(key,ticket); return ticket; }}
复制代码


  • 测试TestShareTicket.java


package cn.liangyy.flyweight.two;
/** * 享元模式-测试 */public class TestShareTicket { public static void main(String[] args) { IShareTicket ticket = TicketShareFactory.getTicketInfo("成都","西宁"); ticket.setSeat("硬座"); //外部状态 ticket.info(); //首次创建对象 ticket = TicketShareFactory.getTicketInfo("成都","西宁"); ticket.setSeat("硬卧"); //外部状态 ticket.info(); //外部状态改变了,但是内部状态共享,依然可以使用缓存 }}
复制代码


现在我们可以看到,将状态分离之后,外部状态的修改并不影响内部状态,也就是对象依然可以被缓存。


享元模式适用场景


  • 当系统中多处需要用到一些公共信息时,可以把这些信息封装到一个对象实现享元模式,避免重复创建对象带来系统的开销。

  • 享元模式主要用于系统中存在大量相似对象,且需要缓冲池的场景,一般情况下享元模式用于底层开发较多,以便提升系统性能。


享元模式优点减少对象的创建,降低了系统中对象的数量,故而可以降低系统的使用内存,提高效率。


享元模式缺点


  • 提高了系统的复杂度,需要注意分离出外部状态和内部状态。

  • 享元模式默认是线程不安全的,所以如果是并发场景需要考虑线程安全性问题。

发布于: 4 小时前阅读数: 8
用户头像

梁歪歪 ♚

关注

还未添加个人签名 2021.07.22 加入

还未添加个人简介

评论

发布
暂无评论
FlyweightPattern-享元模式_设计模式_梁歪歪 ♚_InfoQ写作社区