享元模式(Flyweight Pattern),如果一个对象实例一经创建就不可变,那么反复创建相同的实例就没有必要,直接向调用方返回一个共享的实例就行,这样即节省内存,又可以减少创建对象的过程,提高运行速度。
享元模式的组成如下:
| Flyweight(抽象享元角色) | 是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入 |
|---|---|
| Concrete Flyweight(具体享元角色) | 实现抽象享元角色中所规定的接口 |
| Unsharable Flyweight(非享元角色) | 是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中 |
| Flyweight Factory(享元工厂角色) | 负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象 |
享元模式在Java标准库中有很多应用。我们知道,包装类型如Byte、Integer都是不变类,因此,反复创建同一个值相同的包装类型是没有必要的。以Integer为例,如果我们通过Integer.valueOf()这个静态工厂方法创建Integer实例,当传入的int范围在-128~+127之间时,会直接返回缓存的Integer实例:
public class FlyweightPattern {public static void main(String[] args) {Integer n1 = Integer.valueOf(100);Integer n2 = Integer.valueOf(100);System.out.println(n1 == n2); // true}}
享元模式的优点
- 相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力
享元模式的缺点
- 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性
- 读取享元模式的外部状态会使得运行时间稍微变长
享元模式应用场景
主要应用于缓存,即客户端如果重复请求某些对象,不必每次查询数据库或者读取文件,而是直接返回内存中缓存的数据。
享元模式使用示例
享元模式在五子棋游戏中的应用。
分析:五子棋同围棋一样,包含多个“黑”或“白”颜色的棋子,所以用享元模式比较好。
本实例中:
- 棋子(ChessPieces)类是抽象享元角色,它包含了一个落子的 downPieces() 方法;
- 白子(WhitePieces)和黑子(BlackPieces)类是具体享元角色,它实现了落子方法;
- WeiqiFactory 是享元工厂角色,它通过 ArrayList 来管理棋子,并且提供了获取白子或者黑子的getChessPieces(inttype) 方法;
public class FlyweightPattern {// 抽象享元角色:棋子public interface ChessPieces {void downPieces(int x, int y);}//具体享元角色:白子static class WhitePieces implements ChessPieces {@Overridepublic void downPieces(int x, int y) {System.out.println("执白棋...x: " + x + " ,y: " + y);}}//具体享元角色:黑子static class BlackPieces implements ChessPieces {@Overridepublic void downPieces(int x, int y) {System.out.println("执黑棋...x: " + x + " ,y: " + y);}}//享元工厂角色static class WeiqiFactory {private ArrayList<ChessPieces> pieces;public static final int WHITE = 1;public static final int BLACK = 2;public WeiqiFactory() {pieces = new ArrayList<>();ChessPieces w = new WhitePieces();pieces.add(w);ChessPieces b = new BlackPieces();pieces.add(b);}public ChessPieces getChessPieces(int type) {if (type == WHITE) {return pieces.get(0);}return pieces.get(1);}}public static void main(String[] args) {System.out.println("白棋先...");WeiqiFactory factory = new WeiqiFactory();ChessPieces pieces = factory.getChessPieces(WeiqiFactory.WHITE);pieces.downPieces(1, 1);pieces = factory.getChessPieces(WeiqiFactory.BLACK);pieces.downPieces(2, 2);// 复用之前的棋子,只是落子位置不一样了pieces = factory.getChessPieces(WeiqiFactory.WHITE);pieces.downPieces(3, 3);pieces = factory.getChessPieces(WeiqiFactory.BLACK);pieces.downPieces(4, 4);}}
程序运行结果:
:::success
白棋先…
执白棋…x: 1 ,y: 1
:::
:::success
执黑棋…x: 2 ,y: 2
:::
:::success
执白棋…x: 3 ,y: 3
:::
:::success
执黑棋…x: 4 ,y: 4
:::
