设计模式(二十二)—享元模式
最后更新于:2022-04-01 16:26:38
**定义**:使用共享对象可有效的支持大量的细粒度的对象。
享元模式的定义为我们提出了两个要求:细粒度的对象和共享对象。
我们知道分配太多的对象到应用程序中将有损程序的性能,同时还容易造成内存溢出。享元模式就可以有效的避免。
细粒度对象由于是对象数量多且性质相近,我们将这些对象分为两个部分:内部状态和外部状态。
内部状态:
---|内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变。如id,address等。
外部状态:
---|外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态。如考试科目+考试地点等。
## 一般模式
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-06_5755340d34149.jpg)
Flyweight抽象享元角色
---|产品的抽象类,定义出对象的外部状态和内部状态的接口或实现
ConcreteFlyweight具体享元角色
---|实现抽象角色定义的义务。该角色要注意内部状态处理与环境无关,不能出现一个操作改变内部状态,同时修改外部状态。
unShareConcreteFlyweight不可共享的享元角色
---|不存在外部状态或者安全要求不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。
FlyweightFactory享元工厂
---|职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法。
~~~
public class FlyweightTest {
}
/**
* 抽象的享元角色。
* 定义出对象的外部状态和内部状态的接口和实现。
* @author admin
*/
abstract class Flyweight{
//内部状态
private String intrinsic;
//外部状态
protected String extrinsic;
//构造方法提供外部字符串
public Flyweight(String extrinsic) {
this.extrinsic = extrinsic;
}
//定义业务操作
public abstract void operate();
//内部状态的getter和
public String getIntrinsic() {
return intrinsic;
}
public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}
}
/**
* 具体的享元角色
* @author admin
*/
class ConcreteFlyweight1 extends Flyweight{
public ConcreteFlyweight1(String extrinsic) {
super(extrinsic);
}
@Override
public void operate() {
System.out.println("享元角色1...操作");
}
}
/**
* 具体的享元角色
* @author admin
*/
class ConcreteFlyweight2 extends Flyweight{
public ConcreteFlyweight2(String extrinsic) {
super(extrinsic);
}
@Override
public void operate() {
System.out.println("享元角色2...操作");
}
}
/**
* 享元工厂角色
* @author admin
*/
class FlyweightFactory{
//定义一个容器
private static HashMap<String, Flyweight> pool = new HashMap<String, Flyweight>();
//享元工厂
public static Flyweight getFlyweight(String extrinsic){
//需要返回的对象
Flyweight flyweight=null;
if(!pool.containsKey(extrinsic)){
//根据外部状态创建享元对象
flyweight = new ConcreteFlyweight1(extrinsic);
pool.put(extrinsic, flyweight);
}else{
flyweight = pool.get(extrinsic);
}
return flyweight;
}
}
~~~
## 一个例子
一个学生报考系统,该系统限时3天注册并报名。每天的访问量达上百万,人数极多。系统开发完,出现了OOM内存溢出
的状况。产生该问题的原因有两种:
1、内存溢出(无意识的代码缺陷,导致JVM不能获得连续的内存空间)
2、对象太多,把内存耗尽了。
享元模式来解决,来限制对象的创建。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-06_5755340d4c272.jpg)
~~~
public class FlyweightT {
public static void main(String[] args) {
//创建对象
for(int i=0;i<4;i++){
String subject = "科目"+i;
for(int j=0;j<30;j++){
String location = "地点"+j;
SignInfo signInfo = SignInfoFactory.getSignInfo(subject+location);
}
}
SignInfo s = SignInfoFactory.getSignInfo("科目3地点4");
}
}
/**
* 抽象的学生享元角色
* 提供学生的一些基本信息。
* @author admin
*/
abstract class SignInfo{
//学生id
private String id;
//学生考试地点
private String location;
//考试科目
private String subject;
//考试
private String postAddress;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getPostAddress() {
return postAddress;
}
public void setPostAddress(String postAddress) {
this.postAddress = postAddress;
}
}
/**
* 具体的享元角色,
* 设置外部状态。
* @author admin
*
*/
class SignInfo4Pool extends SignInfo{
private String key;
public SignInfo4Pool(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
/**
* 享元工厂类。
* 创建对象享元对象
* @author admin
*
*/
class SignInfoFactory{
private static HashMap<String, SignInfo> pool = new HashMap<String, SignInfo>();
public static SignInfo getSignInfo(String key){
SignInfo signInfo =null;
if(pool.containsKey(key)){
System.out.println("从对象池中获取...");
signInfo = pool.get(key);
}else{
System.out.println("创建新对象...");
signInfo = new SignInfo4Pool(key);
pool.put(key, signInfo);
}
return signInfo;
}
}
~~~
**享元模式的优缺点:**
大大减少了应用程序创建的对象,降低程序内存的占用,增强程序的性能。
使用享元模式时,外部状态尽量使用java提供的基本数据类型,提高程序的性能。
**使用场景:**
系统中存在大量的相似对象,细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是对象没有特定身份,需要缓冲池的场景