享元模式(Flyweight Pattern),如果一个对象实例一经创建就不可变,那么反复创建相同的实例就没有必要,直接向调用方返回一个共享的实例就行,这样即节省内存,又可以减少创建对象的过程,提高运行速度。

享元模式的组成如下:

Flyweight(抽象享元角色) 是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入
Concrete Flyweight(具体享元角色) 实现抽象享元角色中所规定的接口
Unsharable Flyweight(非享元角色) 是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中
Flyweight Factory(享元工厂角色) 负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象

享元模式在Java标准库中有很多应用。我们知道,包装类型如ByteInteger都是不变类,因此,反复创建同一个值相同的包装类型是没有必要的。以Integer为例,如果我们通过Integer.valueOf()这个静态工厂方法创建Integer实例,当传入的int范围在-128~+127之间时,会直接返回缓存的Integer实例:

  1. public class FlyweightPattern {
  2. public static void main(String[] args) {
  3. Integer n1 = Integer.valueOf(100);
  4. Integer n2 = Integer.valueOf(100);
  5. System.out.println(n1 == n2); // true
  6. }
  7. }

享元模式的优点

  1. 相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力

享元模式的缺点

  1. 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性
  2. 读取享元模式的外部状态会使得运行时间稍微变长

享元模式应用场景

主要应用于缓存,即客户端如果重复请求某些对象,不必每次查询数据库或者读取文件,而是直接返回内存中缓存的数据。

享元模式使用示例

享元模式在五子棋游戏中的应用。

分析:五子棋同围棋一样,包含多个“黑”或“白”颜色的棋子,所以用享元模式比较好。

本实例中:

  • 棋子(ChessPieces)类是抽象享元角色,它包含了一个落子的 downPieces() 方法;
  • 白子(WhitePieces)和黑子(BlackPieces)类是具体享元角色,它实现了落子方法;
  • WeiqiFactory 是享元工厂角色,它通过 ArrayList 来管理棋子,并且提供了获取白子或者黑子的getChessPieces(inttype) 方法;
  1. public class FlyweightPattern {
  2. // 抽象享元角色:棋子
  3. public interface ChessPieces {
  4. void downPieces(int x, int y);
  5. }
  6. //具体享元角色:白子
  7. static class WhitePieces implements ChessPieces {
  8. @Override
  9. public void downPieces(int x, int y) {
  10. System.out.println("执白棋...x: " + x + " ,y: " + y);
  11. }
  12. }
  13. //具体享元角色:黑子
  14. static class BlackPieces implements ChessPieces {
  15. @Override
  16. public void downPieces(int x, int y) {
  17. System.out.println("执黑棋...x: " + x + " ,y: " + y);
  18. }
  19. }
  20. //享元工厂角色
  21. static class WeiqiFactory {
  22. private ArrayList<ChessPieces> pieces;
  23. public static final int WHITE = 1;
  24. public static final int BLACK = 2;
  25. public WeiqiFactory() {
  26. pieces = new ArrayList<>();
  27. ChessPieces w = new WhitePieces();
  28. pieces.add(w);
  29. ChessPieces b = new BlackPieces();
  30. pieces.add(b);
  31. }
  32. public ChessPieces getChessPieces(int type) {
  33. if (type == WHITE) {
  34. return pieces.get(0);
  35. }
  36. return pieces.get(1);
  37. }
  38. }
  39. public static void main(String[] args) {
  40. System.out.println("白棋先...");
  41. WeiqiFactory factory = new WeiqiFactory();
  42. ChessPieces pieces = factory.getChessPieces(WeiqiFactory.WHITE);
  43. pieces.downPieces(1, 1);
  44. pieces = factory.getChessPieces(WeiqiFactory.BLACK);
  45. pieces.downPieces(2, 2);
  46. // 复用之前的棋子,只是落子位置不一样了
  47. pieces = factory.getChessPieces(WeiqiFactory.WHITE);
  48. pieces.downPieces(3, 3);
  49. pieces = factory.getChessPieces(WeiqiFactory.BLACK);
  50. pieces.downPieces(4, 4);
  51. }
  52. }

程序运行结果:

:::success 白棋先…
执白棋…x: 1 ,y: 1 ::: :::success 执黑棋…x: 2 ,y: 2 ::: :::success 执白棋…x: 3 ,y: 3 ::: :::success 执黑棋…x: 4 ,y: 4 :::