概述
享元模式(FlyWeight):运用共享技术有效的支持大量细粒度对象的重用。
享元对象能做到共享的关键就是区分了内部状态和外部状态。
内部状态:可以共享,不会随环境变化而变化
外部状态:不可以共享,会随环境变化而变化
使用场景
- 系统中存在大量的相似对象
- 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份.
- 需要缓冲池的场景比如线程池,数据库连接池,这些都利用享元模式共享了部分属性,在池中操作。
- String类的设计也是享元模式
UML
享元模式分为单纯享元模式和复合享元模式
单纯享元模式
在单纯的享元模式中,所有的享元对象都是可以共享的.抽象享元模式UML如下
其中涉及到的角色有:
Flyweight
(抽象享元角色): 享元对象抽象基类或者接口,规定出所有具有享元角色需要实现的方法.ConcreteFlyweight
(具体享元角色) : 具体的享元对象,实现了Flyweight
接口FlyweightFactory
(享元工厂角色) : 享元工厂,负责管理享元对象池和创建享元对象.本角色必须保证享元对象可以被系统适当地共享.
复合享元模式
复合享元模式将一些单纯享元使用合成模式加以复合.形成复合享元对象,复合享元对象本身不能共享,但可以分解为单纯享元对象实现共享,复合享元对象的UML如下
其中涉及到的角色
Flyweight
(抽象享元角色) : 享元对象的抽象接口.ConcreteFlyweight
(具体享元角色) : 实现了Flyweight
接口的享元角色.ConcreteCompositeFlyweight
(复合享元角色) : 单纯享元对象的组合,不可共享的享元对象.FlyweightFactory
(享元工厂角色) : 创建和管理享元角色.
实例
单纯享元
如下以火车票售票系统为例模拟,当数以万计的人查询票价信息时,假设 出发地-终点地
,上铺,下铺,坐票
看做是享元对象进行缓存,减少服务器压力,如下是一个单纯享元模式示例
- 首先创建
Flyweight
package com.dyleaf.structure.FlyweightPattern.Simple;
/**
* 抽象享元类
*/
public interface Ticket {
public void showTicketInfo(String bunk); //外部状态传入
}
- 创建
ConcreteFlyweight
对象,这里是火车票
package com.dyleaf.structure.FlyweightPattern.Simple;
import Struct.FlyweightPattern.Simple.Ticket;
import java.util.Random;
/**
* 具体享元角色类
*/
public class TrainTicket implements Ticket {
public String from;
public String to;
public String bunk;
public int price;
//负责为内部状态提供存储空间
public TrainTicket(String from, String to) {
this.from = from;
this.to = to;
}
@Override
public void showTicketInfo(String bunk) {
price = new Random().nextInt(300);
System.out.println("购买 从" + from + " 到" + to + "的" + bunk + "火车票" + "," + "价格" + price);
}
}
- 创建
FlyweightFactory
享元工厂
package com.dyleaf.structure.FlyweightPattern.Simple;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 享元工厂
*/
public class TicketFactory {
public static Map<String, Ticket> stringTicketMap = new ConcurrentHashMap<>(); //内部状态
public static Ticket getTicket(String from, String to) {
String key = from + "-" + to;
if (stringTicketMap.containsKey(key)) {
System.out.println("使用缓存-->" + key);
return stringTicketMap.get(key);
} else {
System.out.println("创建对象-->" + key);
Ticket ticket = new TrainTicket(from, to);
stringTicketMap.put(key,ticket);
return new TrainTicket(from, to);
}
}
}
Client
调用代码如下
package com.dyleaf.structure.FlyweightPattern.Simple;
public class Test {
public static void main(String[] args) {
Ticket ticket1 = TicketFactory.getTicket("北京", "青岛");
ticket1.showTicketInfo("上铺");
Ticket ticket2 = TicketFactory.getTicket("北京", "青岛");
ticket2.showTicketInfo("下铺");
Ticket ticket3 = TicketFactory.getTicket("北京", "青岛");
ticket3.showTicketInfo("坐票");
}
}
输出
创建对象-->北京-青岛
购买 从北京 到青岛的上铺火车票,价格161
使用缓存-->北京-青岛
购买 从北京 到青岛的下铺火车票,价格259
使用缓存-->北京-青岛
购买 从北京 到青岛的坐票火车票,价格162
复杂享元
- 首先创建
Flyweight
package com.dyleaf.structure.FlyweightPattern.Complex;
/**
* 抽象享元类
*/
public interface Ticket {
public void showTicketInfo(String bunk); //外部状态传入
}
- 创建
ConcreteFlyweight
对象,这里是火车票
package com.dyleaf.structure.FlyweightPattern.Complex;
import Struct.FlyweightPattern.Simple.Ticket;
import java.util.Random;
/**
* 具体享元角色类
*/
public class TrainTicket implements Ticket {
public String from;
public String to;
public String bunk;
public int price;
//负责为内部状态提供存储空间
public TrainTicket(String from, String to) {
this.from = from;
this.to = to;
}
@Override
public void showTicketInfo(String bunk) {
price = new Random().nextInt(300);
System.out.println("购买 从" + from + " 到" + to + "的" + bunk + "火车票" + "," + "价格" + price);
}
}
- 创建
ConcreteCompositeFlyweight
对象,这里是指定位置的火车票
package com.dyleaf.structure.FlyweightPattern.Complex;
import java.util.HashMap;
import java.util.Map;
public class CompositeTrainTicket implements Ticket {
private Map<String, Ticket> map = new HashMap<>();
/**
* 增加一个新的单纯享元对象到聚集中
*/
public void add(String key, Ticket trainTicket) {
map.put(key, trainTicket);
}
/**
* 外蕴状态作为参数传入到方法中
*/
@Override
public void showTicketInfo(String bunk) {
Ticket ticket = null;
for (Object o:map.keySet()) {
ticket = map.get(o);
ticket.showTicketInfo(bunk);
}
}
}
- 创建
FlyweightFactory
享元工厂
package com.dyleaf.structure.FlyweightPattern.Complex;
import jdk.internal.util.xml.impl.Pair;
import java.security.KeyPair;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 享元工厂
*/
public class TicketFactory {
public static Map<String, Ticket> stringTicketMap = new ConcurrentHashMap<>(); //内部状态
public Ticket getTicket(List<Map.Entry<String,String>> fromToPair){
CompositeTrainTicket ticket = new CompositeTrainTicket();
for (Map.Entry p:fromToPair) {
ticket.add(p.getKey()+"-"+p.getValue(), this.getTicket((String) p.getKey(), (String) p.getValue()));
}
return ticket;
}
public static Ticket getTicket(String from, String to) {
String key = from + "-" + to;
if (stringTicketMap.containsKey(key)) {
System.out.println("使用缓存-->" + key);
return stringTicketMap.get(key);
} else {
System.out.println("创建对象-->" + key);
Ticket ticket = new TrainTicket(from, to);
stringTicketMap.put(key,ticket);
return new TrainTicket(from, to);
}
}
}
Client
调用代码如下
package com.dyleaf.structure.FlyweightPattern.Complex;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Test {
public static void main(String[] args) {
List<Map.Entry<String, String>> list = new ArrayList<>();
list.add(new AbstractMap.SimpleEntry<String, String>("北京", "青岛"));
list.add(new AbstractMap.SimpleEntry<String, String>("北京", "杭州"));
list.add(new AbstractMap.SimpleEntry<String, String>("北京", "福州"));
TicketFactory ticketFactory = new TicketFactory();
CompositeTrainTicket ticket1 = (CompositeTrainTicket) ticketFactory.getTicket(list);
CompositeTrainTicket ticket2 = (CompositeTrainTicket) ticketFactory.getTicket(list);
ticket1.showTicketInfo("下铺");
ticket2.showTicketInfo("上铺");
}
}
输出
创建对象-->北京-青岛
创建对象-->北京-杭州
创建对象-->北京-福州
使用缓存-->北京-青岛
使用缓存-->北京-杭州
使用缓存-->北京-福州
购买 从北京 到福州的下铺火车票,价格83
购买 从北京 到青岛的下铺火车票,价格249
购买 从北京 到杭州的下铺火车票,价格245
购买 从北京 到福州的上铺火车票,价格85
购买 从北京 到青岛的上铺火车票,价格36
购买 从北京 到杭州的上铺火车票,价格141
优缺点
优点:
1.极大的减少内存中对象的数量
2.相同或相似对象内存中只存在一份,极大的节约资源,提高系统性能
3.外部状态相对独立,不影响内部状态
缺点:
1.模式较复杂,使程序逻辑复杂化
2.为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。