• 设计模式之美学习-结构型-享元模式(二十五)


    享元模式的应用场景

    当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,我们就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用。这样可以减少内存中对象的数量,起到节省内存的目的。

    例子一

    开发一个棋牌游戏(比如象棋)。一个游戏厅中有成千上万个“房间”,每个房间对应一个棋局。棋局要保存每个棋子的数据,比如:棋子类型(将、相、士、炮等)、棋子颜色(红方、黑方)、棋子在棋局中的位置。利用这些数据,我们就能显示一个完整的棋盘给玩家。具体的代码如下所示。其中,ChessPiece 类表示棋子,ChessBoard 类表示一个棋局,里面保存了象棋中 30 个棋子的信息。

    普通实现

    public class ChessPiece {//棋子
      private int id;
      private String text;
      private Color color;
      private int positionX;
      private int positionY;
    
      public ChessPiece(int id, String text, Color color, int positionX, int positionY) {
        this.id = id;
        this.text = text;
        this.color = color;
        this.positionX = positionX;
        this.positionY = positionX;
      }
    
      public static enum Color {
        RED, BLACK
      }
    
      // ...省略其他属性和getter/setter方法...
    }
    
    public class ChessBoard {//棋局
      private Map<Integer, ChessPiece> chessPieces = new HashMap<>();
    
      public ChessBoard() {
        init();
      }
    
      private void init() {
        chessPieces.put(1, new ChessPiece(1, "車", ChessPiece.Color.BLACK, 0, 0));
        chessPieces.put(2, new ChessPiece(2,"馬", ChessPiece.Color.BLACK, 0, 1));
        //...省略摆放其他棋子的代码...
      }
    
      public void move(int chessPieceId, int toPositionX, int toPositionY) {
        //...省略...
      }
    }

    如果有千万房间就会创建千万个棋局, 棋子数=棋局数据*棋子数量

    我们可以发现每个棋局棋子都一样 只是棋子的坐标不一样。我们可以把棋子的的坐标抽出来 让棋子作为一个享元类

    // 享元类
    public class ChessPieceUnit {
      private int id;
      private String text;
      private Color color;
    
      public ChessPieceUnit(int id, String text, Color color) {
        this.id = id;
        this.text = text;
        this.color = color;
      }
    
      public static enum Color {
        RED, BLACK
      }
    
      // ...省略其他属性和getter方法...
    }
    
    public class ChessPieceUnitFactory {
      private static final Map<Integer, ChessPieceUnit> pieces = new HashMap<>();
    
      static {
        pieces.put(1, new ChessPieceUnit(1, "車", ChessPieceUnit.Color.BLACK));
        pieces.put(2, new ChessPieceUnit(2,"馬", ChessPieceUnit.Color.BLACK));
        //...省略摆放其他棋子的代码...
      }
    
      public static ChessPieceUnit getChessPiece(int chessPieceId) {
        return pieces.get(chessPieceId);
      }
    }
    
    public class ChessPiece {
      private ChessPieceUnit chessPieceUnit;
      private int positionX;
      private int positionY;
    
      public ChessPiece(ChessPieceUnit unit, int positionX, int positionY) {
        this.chessPieceUnit = unit;
        this.positionX = positionX;
        this.positionY = positionY;
      }
      // 省略getter、setter方法
    }
    
    public class ChessBoard {
      private Map<Integer, ChessPiece> chessPieces = new HashMap<>();
    
      public ChessBoard() {
        init();
      }
    
      private void init() {
        chessPieces.put(1, new ChessPiece(
                ChessPieceUnitFactory.getChessPiece(1), 0,0));
        chessPieces.put(1, new ChessPiece(
                ChessPieceUnitFactory.getChessPiece(2), 1,0));
        //...省略摆放其他棋子的代码...
      }
    
      public void move(int chessPieceId, int toPositionX, int toPositionY) {
        //...省略...
      }
    }

    这样我们的棋子只会有一份 从而实现了节约内存

    源码中的应用

    Integer

            Integer i1 = 56;
            Integer i2 = 56;
            Integer i3 = 129;
            Integer i4 = 129;
            System.out.println(i1 == i2);//true
            System.out.println(i3 == i4);//false

    自动装箱后的代码

         Integer i1 = Integer.valueOf(56);
            Integer i2 = Integer.valueOf(56);
            Integer i3 = Integer.valueOf(129);
            Integer i4 = Integer.valueOf((129);
            System.out.println(i1 == i2);//true
            System.out.println(i3 == i4);//false

    按照我们的理解 可能2个都是false 因为是不同的对象

    public static Integer valueOf(int i) {
            //low -128 higt=127
            if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
    }
       private static class IntegerCache {
            static final int low = -128;
            static final int high;
            static final Integer cache[];
    
            static {
                // high value may be configured by property
                int h = 127;
                String integerCacheHighPropValue =
                    sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
                if (integerCacheHighPropValue != null) {
                    try {
                        int i = parseInt(integerCacheHighPropValue);
                        i = Math.max(i, 127);
                        // Maximum array size is Integer.MAX_VALUE
                        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                    } catch( NumberFormatException nfe) {
                        // If the property cannot be parsed into an int, ignore it.
                    }
                }
                high = h;
    
                cache = new Integer[(high - low) + 1];
                int j = low;
                for(int k = 0; k < cache.length; k++)
                    cache[k] = new Integer(j++);
    
                // range [-128, 127] must be interned (JLS7 5.1.7)
                assert IntegerCache.high >= 127;
            }
    
            private IntegerCache() {}
        }

    可以看到提前将-128~127的数值缓存了起来用于共享,为什么缓存-128~127 因为数值太多不可能全部缓存,只能缓存大多数情况出现的数值 当然我们也可以动态改变

    //方法一:-Djava.lang.Integer.IntegerCache.high=255

    //方法二:-XX:AutoBoxCacheMax=255

    String

            String s1 = "小争哥";
            String s2 = "小争哥";
            String s3 = new String("小争哥");
    
            System.out.println(s1 == s2);//true
            System.out.println(s1 == s3);//false

    1.new 会先在常量池找有没有,如果没有会在常量池看是否存在 如果不存在则创建,并在堆空间开辟的空间引用常量池,s1会直接引用常量池

           String s3 = new String("小争哥");
            //可能会认为会从常量池拿 因为上面是new 并不会放入常量池
            String s1 = "小争哥";
            System.out.println(s1 == s3);//false
  • 相关阅读:
    Java面向对象编程 -1.3
    Java面向对象编程 -1.2
    Java面向对象编程 -1
    Java基础 -5.3
    Java基础 -5.2
    oracle 新建用户
    js密码的匹配正则
    oracle导入和导出和授权
    oracle存储过程语法
    java.lang.NumberFormatException: For input string: "26.0"
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12573104.html
Copyright © 2020-2023  润新知