设计模式(二十二)—享元模式

最后更新于: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提供的基本数据类型,提高程序的性能。 **使用场景:** 系统中存在大量的相似对象,细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是对象没有特定身份,需要缓冲池的场景
';