享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享大量细粒度对象来减少内存使用。它在需要创建大量相似对象的场景中尤其有用,通过共享对象来降低内存开销,从而提高性能。
核心思想
享元模式将对象的状态分为两部分:
- 内部状态(Intrinsic State):不随外部环境改变,可以共享的状态。它通常是对象的一部分,用来定义该对象的属性。例如,在象棋游戏中,棋子的颜色就是内部状态,它在不同的棋子对象中是相同的,可以共享。
- 外部状态(Extrinsic State):随外部环境变化的状态,不能被共享。它通常是对象的具体表现属性,例如象棋棋子的位置就是外部状态,不同的棋子有不同的位置,不能共享。
结构组成
享元模式通常由以下几个部分组成:
- Flyweight(享元接口):定义对象的公共接口,外部状态的操作通常由该接口来完成。
- ConcreteFlyweight(具体享元类):实现享元接口,并且存储可以共享的内部状态。
- UnsharedConcreteFlyweight(非共享具体享元类):并非所有对象都需要被共享,非共享的对象可以作为享元模式的扩展部分。
- FlyweightFactory(享元工厂类):管理享元对象的创建和复用。它确保每个享元对象是唯一的,并管理共享对象的生命周期。
使用场景
享元模式适用于以下场景:
- 有大量相似对象时:如果一个应用程序需要创建大量相似的对象,并且内存消耗成为瓶颈,享元模式可以通过共享对象来减少内存开销。
- 对象的多数状态是可以共享的:当对象的状态可以分为内部状态和外部状态,并且内部状态是可以共享的,享元模式能有效降低内存消耗。
优点
- 减少对象创建,降低内存消耗:通过共享对象,显著减少了内存的占用,提升了系统性能。
- 提高性能:由于减少了对象的数量,系统运行速度也可能因此得到提升。
缺点
- 增加系统复杂度:为了实现对象的共享,需要对对象的内部状态和外部状态进行区分,这增加了代码的复杂性。
- 不适用于所有场景:如果对象的内部状态和外部状态划分不清晰,或者对象数量并不多,享元模式的使用可能得不偿失。
示例
在象棋程序中,享元模式可以用于管理棋子对象:
- 内部状态:棋子的颜色(黑色或白色)是不变的,可共享的。
- 外部状态:棋子的位置(坐标)是可变的,不同的棋子有不同的位置,因此不能共享。
通过享元工厂类,创建和管理这些棋子对象,保证相同颜色的棋子是共享的。
享元接口(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,表明共享了黑色棋子对象 } }
总结
享元模式是一种优化内存使用的模式,通过共享对象来减少重复对象的创建,从而提升系统性能。它适用于需要创建大量相似对象的场景,如图形编辑器、文字处理器、游戏开发等。在设计过程中,需要权衡对象共享带来的好处和系统复杂性之间的关系。
标签:享元,状态,对象,模式,color,棋子,共享 From: https://www.cnblogs.com/20lxj666/p/18388823