结构型设计模式之享元模式_积跬步,至千里。的技术博客_51CTO博客
source link: https://blog.51cto.com/u_13494008/5592357
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
结构型设计模式之享元模式
推荐 原创享元模式(Flyweight Pattern)又称为轻量级模式,是对象池的一种实现。属于结构型模式。
类似于线程池,线程池可以避免不停的创建和销毁多个对象,消耗性能。享元模式提供了减少对象数量从而改善应用所需的对象结构的方式。
享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。主要用于减少创建对象的数量,以减少内存占用和提高性能。其本质是缓存共享对象,降低内存消耗。
运用共享技术有效地支持大量细粒度的对象,将多个对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。
在系统中增加类和对象的个数,当对象数量太多时,将导致运行代价过高,带来性能下降等问题。
当有大量对象时,有可能会造成内存溢出,把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
当系统中多处需要同一组信息时,可以把这些信息封装到一个对象中,然后对该对象进行缓存,这样,一个对象就可以提供给多处需要使用的地方,避免大量同一对象的多次创建,消耗大量内存空间。
享元模式其实就是工厂模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法生成对象的,只不过享元模式中为工厂方法增加了缓存这一功能。
1、系统有大量相似对象。
2、需要缓冲池的场景。
例如
1、JAVA中的String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
2、数据库的数据池。
1.减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率。
2.减少内存之外的其他资源占用。
1.关注内、外部状态、关注线程安全问题。
2.提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
1.抽象享元角色(Flyweight)
享元对象抽象基类或者接口,同时定义出对象的外部状态和内部状态的接口或实现。
2.具体享元角色(ConcreteFlyweight)
实现抽象角色定义的业务。该角色的内部状态处理应该与环境无关,不能出现会有一个操作改变内部状态,同时修改了外部状态。
3.享元工厂(FlyweightFactory)
负责管理享元对象池和创建享元对象。
享元模式结构
内部状态和外部状态
享元模式把一个对象的状态分成内部状态和外部状态,内部状态是不变的,外部状态是变化的。然后通过共享不变的部分,达到减少对象数量并节约内存的目的。
享元模式的定义有2个要求:细粒度和共享对象。因为要求细粒度对象,所以不可避免地会使对象数量多且性质相近,此时就将这些对象的信息分为两个部分:内部状态和外部状态。
内部状态指与外部状态
内部状态指对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变;
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
例如
连接池中的连接对象,保存在连接对象中的用户名、密码、连接等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而每个连接要回收利用时,需要给它标记为可用状态,这些为外部状态。
享元模式的基本使用
创建抽象享元角色
public interface ITicket {
/**
* 查询票信息
*/
void query();
}
创建具体享元角色
public class TrainTicket implements ITicket {
/**
* 出发地
*/
private String from;
/**
* 目的地
*/
private String to;
/**
* 票种类
*/
private String type;
/**
* 票价
*/
private int price;
public TrainTicket(String from, String to, String type) {
this.from = from;
this.to = to;
this.type = type;
}
public void query() {
this.price = new Random().nextInt(100);
int num = new Random().nextInt(10);
System.out.println(String.format("从%s到%s,%s,票价:%s元,剩余车票:%s", this.from, this.to, this.type, this.price, num));
}
}
创建享元工厂
将相同查询票的信息对象进行缓存,复用该对象进行查询,减少对象的创建,降低内存的压力。
public class TicketFactory {
private static Map<String, ITicket> ticketPool = new ConcurrentHashMap<String, ITicket>();
public static ITicket queryTicket(String from, String to, String type) {
String key = "出发站:"+from + " 目的站:" + to + " 坐席类型:" + type;
if (TicketFactory.ticketPool.containsKey(key)) {
System.out.println("使用缓存查询:" + key);
return TicketFactory.ticketPool.get(key);
}
System.out.println("第一次查询,创建对象: " + key);
ITicket ticket = new TrainTicket(from, to, type);
TicketFactory.ticketPool.put(key, ticket);
return ticket;
}
}
客户端调用
public static void main(String[] args) {
ITicket ticket = TicketFactory.queryTicket("A", "B", "特等座");
ticket.query();
ticket = TicketFactory.queryTicket("A", "B","一等座");
ticket.query();
ticket = TicketFactory.queryTicket("A", "B","特等座");
ticket.query();
}
第一次查询,创建对象: 出发站:A 目的站:B 坐席类型:特等座
从A--->B,特等座,票价:38元,剩余车票:8
第一次查询,创建对象: 出发站:A 目的站:B 坐席类型:一等座
从A--->B,一等座,票价:0元,剩余车票:4
使用缓存查询:出发站:A 目的站:B 坐席类型:特等座
从A--->B,特等座,票价:62元,剩余车票:6
容器式单例模式适用于需要大量创建单例对象的场景。享元模式的实现恰好类似于容器式单例模式。但是有一点区别是享元模式的重点在于结构上。
享元模式实现数据库连接池
经常使用的数据库连接池也使用了享元模式,有效提高了其运行的性能。
在使用数据库连接池时,由于经常使用Connection对象,其主要性能消耗在建立连接和关闭连接的时候,为了提高Connection在调用时的性能,可以将Connection对象在调用前创建好缓存起来,用的时候从缓存中取值,用完再放回去,达到资源重复利用的目的。
创建数据库连接池
@Data
public class ConnectionPool {
private Vector<Connection> pool;
private String url = "jdbc:mysql://localhost:3306/demo";
private String username = "root";
private String password = "123456";
private String driverClassName = "com.mysql.jdbc.Driver";
private int poolSize = 100;
/**
* 初始化一定数量的连接
*/
public ConnectionPool() {
pool = new Vector<Connection>(poolSize);
try {
Class.forName(driverClassName);
for (int i = 0; i < poolSize; i++) {
Connection conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接
* @return
*/
public synchronized Connection getConnection() {
if (pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
}
return null;
}
/**
* 释放归还连接
* @param conn
*/
public synchronized void release(Connection conn) {
pool.add(conn);
}
}
使用数据库连接池
public static void main(String[] args) {
ConnectionPool connectionPool = new ConnectionPool();
Connection conn = connectionPool.getConnection();
System.out.println("获取一个连接:" + conn + "连接池中剩余:" + connectionPool.getPool().size());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
connectionPool.release(conn);
System.out.println("归还连接池,连接池总数: " + connectionPool.getPool().size());
}
}
获取一个连接:com.mysql.jdbc.JDBC4Connection@7fad8c79连接池中剩余:99
归还连接池,连接池总数: 100
Recommend
-
55
前言 享元模式是非常常用的一种结构性设计模式。 特别是在面试的时候。当我们把这一节内容掌握,我相信不管是工作中还是面试中这一块内容绝对是一大亮点。 什么是享元模式 所谓“享元”,顾名思义就是被...
-
1
积跬步至千里:QUIC 协议在蚂蚁集团落地之综述 作者:孔令涛 自 2015 年以来,QUIC 协议开始在 IETF 进行标准化并被国内外各大厂商相继落地。鉴于 QUIC 具备“0RTT 建联”、“支持连接迁移”等诸多优势,并将成为下一代互联网协议:...
-
5
积跬步方致千里:苹果新CarPlay给国内同行恶补了一课紫金山科技·2022-06-10 12:51新一代CarPlay虽然谈不上颠覆,但足够令竞争对手们望其项背了...
-
6
结构型设计模式之代理模式 原创 结构型设计模式之代理模式 代理模式(Proxy Pattern)属于结构型模式。
-
2
本文通过优化买票的重复流程来说明享元模式,为了加深对该模式的理解,会以String和基本数据类型的包装类对该模式的设计进一步说明。 读者可以拉取完整代码到本地进行学习,实现代码均测试通过后上传到
-
6
结构性设计模式之门面模式 推荐 原创 结构性设计模式之门面模式 门面模式(Facade Pattern)又叫外观模...
-
3
结构型设计模式之桥接模式 精选 原创 结构型设计模式之桥接模式
-
3
行为型设计模式之模板方法模式模板方法模式 模板方法模式(Template Method Pattern)属于行为型设计模式。 它是指定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子...
-
5
创建型设计模式之建造者模式 精选 原创 丨Jack_Chen丨 2022-08-30 21:19:44
-
5
访问者模式 访问者模式属于行为型模式。它是一种将数据结构与数据操作分离的设计模式。是指封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK