享元模式

享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享大量细粒度对象来减少内存使用。它在需要创建大量相似对象的场景中尤其有用,通过共享对象来降低内存开销,从而提高性能。

核心思想

享元模式将对象的状态分为两部分:

  1. 内部状态(Intrinsic State):不随外部环境改变,可以共享的状态。它通常是对象的一部分,用来定义该对象的属性。例如,在象棋游戏中,棋子的颜色就是内部状态,它在不同的棋子对象中是相同的,可以共享。
  2. 外部状态(Extrinsic State):随外部环境变化的状态,不能被共享。它通常是对象的具体表现属性,例如象棋棋子的位置就是外部状态,不同的棋子有不同的位置,不能共享。

结构组成

享元模式通常由以下几个部分组成:

  1. Flyweight(享元接口):定义对象的公共接口,外部状态的操作通常由该接口来完成。
  2. ConcreteFlyweight(具体享元类):实现享元接口,并且存储可以共享的内部状态。
  3. UnsharedConcreteFlyweight(非共享具体享元类):并非所有对象都需要被共享,非共享的对象可以作为享元模式的扩展部分。
  4. FlyweightFactory(享元工厂类):管理享元对象的创建和复用。它确保每个享元对象是唯一的,并管理共享对象的生命周期。

使用场景

享元模式适用于以下场景:

  1. 有大量相似对象时:如果一个应用程序需要创建大量相似的对象,并且内存消耗成为瓶颈,享元模式可以通过共享对象来减少内存开销。
  2. 对象的多数状态是可以共享的:当对象的状态可以分为内部状态和外部状态,并且内部状态是可以共享的,享元模式能有效降低内存消耗。

优点

  • 减少对象创建,降低内存消耗:通过共享对象,显著减少了内存的占用,提升了系统性能。
  • 提高性能:由于减少了对象的数量,系统运行速度也可能因此得到提升。

缺点

  • 增加系统复杂度:为了实现对象的共享,需要对对象的内部状态和外部状态进行区分,这增加了代码的复杂性。
  • 不适用于所有场景:如果对象的内部状态和外部状态划分不清晰,或者对象数量并不多,享元模式的使用可能得不偿失。

示例

在象棋程序中,享元模式可以用于管理棋子对象:

  • 内部状态:棋子的颜色(黑色或白色)是不变的,可共享的。
  • 外部状态:棋子的位置(坐标)是可变的,不同的棋子有不同的位置,因此不能共享。

通过享元工厂类,创建和管理这些棋子对象,保证相同颜色的棋子是共享的。

享元接口(ChessPiece):

  • 在代码中,ChessPiece 是享元接口,定义了棋子的核心操作——放置棋子(place(int x, int y))。这个接口的作用是为不同的棋子定义一个统一的行为,即放置棋子的位置,而位置是棋子的外部状态,不会在不同对象之间共享。

  • // 享元接口,定义了操作方法
    public interface ChessPiece {
        void place(int x, int y); // 位置是外部状态
    }
    

具体享元类(ConcreteChessPiece):

  • ConcreteChessPiece 是享元模式中的具体享元类,它实现了 ChessPiece 接口。这个类的实例表示象棋中的一个棋子。

  • 内部状态(Intrinsic State): 棋子的颜色 (color) 是内部状态,这部分状态是不可变的,并且在多个相同颜色的棋子对象之间共享。

  • 外部状态(Extrinsic State): 棋子的位置(x, y 坐标)是外部状态,不同棋子的具体位置是通过 place 方法传入的,而不是存储在对象内部,这样多个棋子可以共享相同的颜色对象,但位置各自独立。

    // 具体享元类,实现了享元接口,包含共享的内部状态
    class ConcreteChessPiece implements ChessPiece {
        private final String color; // 颜色是内部状态,不变且可共享
    
        public ConcreteChessPiece(String color) {
            this.color = color;
        }
    
        @Override
        public void place(int x, int y) {
            System.out.println("Placing " + color + " chess piece at (" + x + ", " + y + ")");
        }
    }
    

享元工厂类(ChessPieceFactory):

  • 工厂类 ChessPieceFactory 负责管理和创建享元对象。在享元模式中,工厂类的职责是确保共享对象的合理使用。它通过 getChessPiece(String color) 方法来获取某种颜色的棋子。

  • 如果请求的棋子颜色已经存在(即缓存中已经有该颜色的棋子对象),那么直接返回这个共享对象;如果不存在,则创建一个新的棋子对象并将其缓存起来。

    // 享元工厂类,负责管理和创建享元对象
    class ChessPieceFactory {
        private final Map<String, ChessPiece> pieceMap = new HashMap<>();
    
        public ChessPiece getChessPiece(String color) {
            ChessPiece piece = pieceMap.get(color);
    
            // 如果不存在该颜色的棋子,则创建并放入缓存
            if (piece == null) {
                piece = new ConcreteChessPiece(color);
                pieceMap.put(color, piece);
            }
            return piece;
        }
    }
    
    

客户端(TestFlyweightPattern):

  • 客户端代码展示了如何使用享元模式来创建和使用棋子对象。在 main 方法中,使用 ChessPieceFactory 创建了多个棋子对象,但由于使用了享元模式,相同颜色的棋子实际上是共享同一个对象。

  • 通过 System.out.println(white1 == white2);System.out.println(black1 == black2); 可以验证同一颜色的棋子确实是同一个对象。

    public class TestFlyweightPattern {
        public static void main(String[] args) {
            ChessPieceFactory factory = new ChessPieceFactory();
    
            // 创建并共享白色棋子
            ChessPiece white1 = factory.getChessPiece("White");
            white1.place(1, 1);
    
            ChessPiece white2 = factory.getChessPiece("White");
            white2.place(2, 2);
    
            // 创建并共享黑色棋子
            ChessPiece black1 = factory.getChessPiece("Black");
            black1.place(3, 3);
    
            ChessPiece black2 = factory.getChessPiece("Black");
            black2.place(4, 4);
    
            // 验证共享的对象
            System.out.println(white1 == white2); // true,表明共享了白色棋子对象
            System.out.println(black1 == black2); // true,表明共享了黑色棋子对象
        }
    }
    

总结

享元模式是一种优化内存使用的模式,通过共享对象来减少重复对象的创建,从而提升系统性能。它适用于需要创建大量相似对象的场景,如图形编辑器、文字处理器、游戏开发等。在设计过程中,需要权衡对象共享带来的好处和系统复杂性之间的关系。

posted @ 2024-09-17 15:09  疾风不问归途  阅读(79)  评论(0)    收藏  举报