- 一、概念
- 二、结构
- 三、举例
- 四、特点
- ☯ 优点
- ☯ 缺点
- ☯ 适用
- 五、Integer 源码探索
一、概念结构型模式关注如何将现有类或对象组织一起形成更加强大的结构。
享元模式(FlyWeight Pattern):运用共享技术有效地支持大量细粒度对象的复用。
享元模式的内外部状态:
- 内部状态(Intrinsic State):存储在享元对象内部并且不会随着环境改变而改变的状态,内部状态可以共享。
- 外部状态(Extrinsic State):随环境变化而变化的、不可共享的状态。通常由客户端保存,并在享元对象被创建后,需要使用的时候再传入享元对象内部。
正因为区别了内外部状态,可以将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以共享的,当需要的时候就将对象从享元池中取出,实现对象的复用。
通过想去出的对象注入不同的外部状态可以得到一系列相似的对象,而它们在内存中只存在一份。
二、结构[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pwpUQzbl-1635853614033)(C:Users64454DesktopABC结构型模式FlyWeight结构.jpg)]
- Flyweight(抽象享元类):定义为接口或抽象类,声明了具体享元类的公共方法。
- ConCreteFlyweight(具体享元类):实现了抽象享元类,存储内部状态。通常结合单例模式提供唯一的享元对象。
- UnsharedConcreteFlyweight(非共享具体享元类):并非所有抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类。
- FlyweightFactory(享元工厂类):用于创建、管理享元对象,将各种类型的享元对象存储在享元池中,一般设计为“K-V”映射集合,可结合工厂模式设计。
Flyweight(抽象享元类)
public abstract class Flyweight { public abstract void operation(String extrinsicState); }
ConCreteFlyweight(具体享元类)
public class ConcreteFlyweight extends Flyweight { //内部状态 intrinsicState 作为成员变量,同一个享元对象的内部状态是一致的 private String intrinsicState; public ConcreateFlyweight(String intrinsicState) { this.intinsicState = intrinsicState; } //外部状态 extrinsicState 在使用时由外部设置,不保存在享元对象中,即使是同一个对象,在每一次调用时可以传入不同的外部状态 public void operation(String extrincState) { //实现业务方法 } }
UnsharedConcreteFlyweight(非共享具体享元类)
public class UnsharedConcreteFlyweight extends Flyweight { public void operation(String extrinsicState) { //实现业务方法 } }
FlyweightFactory(享元工厂类)
public class FlyweightFactory { //定义一个 HashMap 用于存储享元对象,实现享元池 private HashMap flyweights = new HashMap(); public Flyweight getFlyweight(String key) { //如果对象存在,则直接从享元池获取 if (flyweights.containsKey(key)) { return (Flyweight)flyweights.get(key); } //如果对象不存在,先创建一个新的对象添加到享元池中,然后返回 else { Flyweight fw = new ConcreteFlyweight(); flyweights.put(key,fw); return fw; } } }三、举例
软件公司需开发围棋软件,现需对黑白子进行定义。围棋棋子大小、形状一致,为节省存储空间、提供性能,使用享元模式设计棋子类:
围棋棋子类:充当抽象享元类
public abstract class IgoChessman { public abstract String getColor(); public void display(Coordinates coordinates) { System.out.println("棋子颜色:" + this.getColor() + "棋子位置" + coordinates.getX() + "," + coordinates.getY()); } }
黑色棋子类:充当具体享元类
public class BlackIgoChessman extends IgoChessman { @Override public String getColor() { return "黑色"; } }
白色棋子类:充当具体享元类
public class WhiteIgoChessman extends IgoChessman { @Override public String getColor() { return "白色"; } }
围棋棋子工厂类:充当享元工厂类,使用单例模式对其进行设计
public class IgoChessmanFactory { private static IgoChessmanFactory instance = new IgoChessmanFactory(); private static HashMap pool; //使用 HashMap 来存储享元对象,充当享元池 private IgoChessmanFactory() { pool = new HashMap(); IgoChessman black, white; black = new BlackIgoChessman(); pool.put("b",black); white = new WhiteIgoChessman(); pool.put("w",white); } //返回享元工厂类的唯一实例 public static IgoChessmanFactory getInstance(){ return instance; } //通过 Key 获取存储在 HashMap 中的享元对象 public static IgoChessman getIgoChessman(String color) { return (IgoChessman) pool.get(color); } }
棋子位置类:充当外部状态类
public class Coordinates { private int x; private int y; public Coordinates(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
测试代码:
IgoChessman black1, black2, black3, white1, white2; IgoChessmanFactory factory; //获取享元工厂对象 factory = IgoChessmanFactory.getInstance(); //通过享元工厂获取 3 颗黑子 black1 = factory.getIgoChessman("b"); black2 = factory.getIgoChessman("b"); black3 = factory.getIgoChessman("b"); //通过享元工厂获取 2 颗白子 white1 = factory.getIgoChessman("w"); white2 = factory.getIgoChessman("w"); //判断两颗棋子是否相同 System.out.println("判断两颗黑子是否相同:" + (black1 == black3)); System.out.println("判断两颗白子是否相同:" + (white1 == white2)); //显示棋子,同时设置棋子的坐标位置 black1.display(new Coordinates(1,2)); black2.display(new Coordinates(3,4)); black3.display(new Coordinates(1,3)); white1.display(new Coordinates(2,5)); white2.display(new Coordinates(2,4));
测试结果:
- 享元模式可以减少内存中对象的数量,使得相同/相似对象在内存中只保存一份。节约系统资源,提高性能。
- 外部状态相对独立,不会影响内部状态。
- 因需要分离内外部状态,使得系统逻辑复杂化。
- 为使对象共享,需将部分状态外部化。
- 一个系统有啊大量相同/相似对象,造成大量内存浪费
- 对象的大部分状态都可以外部化,可以传入对象
- 维护享元池需要消耗系统资源,应在需要多次重复使用想元对象时使用享元模式
享元模式的注意事项和细节:
- 享元模式:“享”:共享,“元”:对象
- 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
- 用唯一标识码判断,如果内存中有,则返回这个唯一标识码所标识的对象,用 HashMap/HashTable 存储
- 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
- 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们 使用享元模式需要注意的地方
- 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制(内部状态)
- 享元模式经典的应用场景是需要缓存池的场景,比如String常量池、数据库连接池
//如果 Integer.valueOf(x) x 在 -128 ~ 127 之间,就使用享元模式返回 Integer x = Integer.valueOf(127); //得到x的实例,类型Integer Integer y = new Integer(127); //得到y的实例,类型Integer Integer z = Integer.valueOf(127); //... Integer w = new Integer(127); //... System.out.println(x.equals(y)); //比较大小:true System.out.println(x == y); // false System.out.println(x == z); // true System.out.println(w == x); // false System.out.println(w == y); // false
//如果 Integer.valueOf(x) x 未在 -128 ~ 127 之间,则使用new创建对象 Integer x = Integer.valueOf(200); Integer y = Integer.valueOf(200); System.out.println(x == y); // false
valueOf源码:
本方法总是将 -128 ~ 127 的数值进行缓存,如果是其他值则不进行此 *** 作。
Cache缓存池(享元池):
按范围(-128 ~ 127,即low ~ high)存储缓存数值
数组大小:
小结:
- 在 valueOf 方法中,先判断数值是否在 Integer 的cache中:如果不在就创建新的Integer对象;否则,就直接从缓存池中返回
- valueOf 方法,部分应用享元模式
img-GHMdja3v-1635853614037)]
Cache缓存池(享元池):
按范围(-128 ~ 127,即low ~ high)存储缓存数值
[外链图片转存中…(img-B3lniWg6-1635853614039)]
数组大小:
[外链图片转存中…(img-I2hBl0qH-1635853614040)]
小结:
- 在 valueOf 方法中,先判断数值是否在 Integer 的cache中:如果不在就创建新的Integer对象;否则,就直接从缓存池中返回
- valueOf 方法,部分应用享元模式
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)