本文已参与「新人创作礼」活动,一起开启掘金创作之路。
模式意图
在实现某一功能时,要获取若干相似对象,但不同对象会占用太多内存,将获取的对象复用,减少内存消耗。
模式结构
- 享元工厂:负责创建和管理享元角色,当需要具体享元对象时,先获取享元工厂,再检查享元工厂中是否存在该对象,若存在再从享元工厂中去获取对象。
- 抽象享元角色:定义享元类的公共方法
- 具体享元角色:每一个享元类提供一个唯一的对象存储在享元工厂中
- 非享元角色:不存在享元工厂中的类,但可以继承抽象享元角色,需要时直接创建
俄罗斯方块案例
每种形状的图形都可以看作一个类,由于这些图形是重复出现的,如果每次生成一个图形都创建一个对象,会非常浪费空间,可以考虑用享元模式。
编辑
享元工厂:BoxFacory
抽象享元类:AbstractBox
具体享元类:IBox,LBox,OBox
编辑
抽象享元类的功能是定义规范和提高代码复用,子类具体享元类是有相似特点的类,
享元工厂类将具体享元类装到集合中,提供向外获取具体享元类的方法。
package mode; import javax.swing.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class Client{ public static void main(String[] args) { AbstractBox box1 = BoxFactory.getInstance().getShape("I"); box1.display("蓝色"); AbstractBox box2 = BoxFactory.getInstance().getShape("L"); box2.display("绿色"); AbstractBox box3 = BoxFactory.getInstance().getShape("I"); System.out.println(box1==box3);//true } } //抽象享元角色 abstract class AbstractBox{ //获取图形,定义规范 public abstract String getShape(); //显示图形及颜色,提高代码复用 public void display(String color){ System.out.println("方块形状:"+getShape()+", 颜色"+color); } } //享元工厂类,将该类设计成单例 class BoxFactory{ private HashMap<String,AbstractBox> map; private static BoxFactory boxFactory = new BoxFactory(); private BoxFactory(){ map = new HashMap<>(); map.put("I",new IBox()); map.put("L",new IBox()); map.put("O",new IBox()); } //根据名称获取图形对象 public AbstractBox getShape(String name){ return map.get(name); } public static BoxFactory getInstance(){ return boxFactory; } } //具体享元 class IBox extends AbstractBox{ @Override public String getShape() { return "I"; } } class LBox extends AbstractBox{ @Override public String getShape() { return "L"; } } class OBox extends AbstractBox{ @Override public String getShape() { return "O"; } }
优点
- 减少内存中相似或相同对象的数量,节约系统资源,提高系统性能
- 享元模式中的外部状态相对独立,不影响内部状态,内部状态就是具体享元对象的属性,内部状态转为外部状态就是将属性转为方法的参数,因为只有一个享元对象,相似的享元对象之间之间是有差异的,所以不能写在属性中,要靠传入形参来使用,比如下例中的颜色参数。
缺点
为了将对象可以共享,需要将享元对象的部分状态(属性)外部化,分离内部状态和外部状态(转化为形参),使程序逻辑复杂。
使用场景
- 一个系统中有大量相同或者相似的对象,造成内存的大量耗费
- 对象的大部分状态可以外部化,可以将这些外部状态传入对象
- 在使用享元模式时需要维护一个存储享元对象的享元池,会需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式
JDK源码解析
public static void main(String[] args) { Integer i1 = 127; Integer i2 = 127; System.out.println(i1 == i2);//true Integer i3 = 128; Integer i4 = 128; System.out.println(i3 == i4);//false }
Integer类默认先创建并缓存-128到127之间的Integer对象,当赋值的数在-128到127之间时,直接从缓存中返回,否则创建一个新的Integer对象