享元模式(Flyweight Design Pattern)
所谓享元,就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。
当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用。这样可以减少内存中对象的数量,起到节省内存的目的。
不可变对象指的是,一旦通过构造函数初始化完成之后,它的状态(对象的成员变量或者属性)就不会再被修改了。所以,不可变对象不能暴露任何set等修改内部状态的方法。之所以要求享元是不可变对象,那是因为它会被多处代码共享使用,避免一处代码对享元进行了修改,影响到其他使用它的代码。
示例
一个棋牌游戏(比如象棋)。一个游戏厅中有成千上万个”房间“,每个房间对应一个棋局。棋局要保存每个棋子的数据,比如:棋子类型(将、相、士、炮等)、棋子颜色(红方、黑方)、棋子在棋局中的位置。利用这些数据,就能显示一个完整的棋盘给玩家。具体代码如下。DMChessPiece类表示棋子,DMChessBoard类表示一个棋局,里面保存了象棋中32个棋子的信息。
typedef enum : NSInteger {
DMChessColorRed = 0,
DMChessColorBlack = 1,
} DMChessColor;
@interface DMChessPiece : NSObject
- (instancetype)initWithChessPieceId:(NSString *)chessPieceId chessPieceText:(NSString *)chessPieceText chessPieceColor:(DMChessColor)chessColor positionX:(NSInteger)positionX positionY:(NSInteger)positionY;
@property (nonatomic, strong) NSString *mId;
@property (nonatomic, strong) NSString *mText;
@property (nonatomic, assign) DMChessColor mChessColor;
@property (nonatomic, assign) NSInteger mPositionX;
@property (nonatomic, assign) NSInteger mPositionY;
@end
@implementation DMChessPiece
- (instancetype)initWithChessPieceId:(NSString *)chessPieceId chessPieceText:(NSString *)chessPieceText chessPieceColor:(DMChessColor)chessColor positionX:(NSInteger)positionX positionY:(NSInteger)positionY {
if (self = [super init]) {
self.mId = chessPieceId;
self.mText = chessPieceText;
self.mChessColor = chessColor;
self.mPositionX = positionX;
self.mPositionY = positionY;
}
return self;
}
@end
@interface DMChessBoard : NSObject
- (void)moveChessPiece:(NSString *)chessPieceId toPositionX:(NSInteger)positionX toPositionY:(NSInteger)positionY;
@end
@interface DMChessBoard ()
@property (nonatomic, strong) NSMutableDictionary *chessPieces;
@end
@implementation DMChessBoard
- (instancetype)init {
if (self = [super init]) {
[self initData];
}
return self;
}
- (void)initData {
[self.chessPieces setObject:[[DMChessPiece alloc] initWithChessPieceId:@"0001" chessPieceText:@"車" chessPieceColor:DMChessColorRed positionX:0 positionY:0] forKey:@"0001"];
[self.chessPieces setObject:[[DMChessPiece alloc] initWithChessPieceId:@"0002" chessPieceText:@"馬" chessPieceColor:DMChessColorRed positionX:0 positionY:1] forKey:@"0002"];
// 省略摆放其它棋子的代码
}
- (void)moveChessPiece:(NSString *)chessPieceId toPositionX:(NSInteger)positionX toPositionY:(NSInteger)positionY {
DMChessPiece *chessPiece = [self.chessPieces objectForKey:chessPieceId];
chessPiece.mPositionX = positionX;
chessPiece.mPositionY = positionY;
}
#pragma mark - getter and setter
- (NSMutableDictionary *)chessPieces {
if (_chessPieces == nil) {
_chessPieces = [[NSMutableDictionary alloc] init];
}
return _chessPieces;
}
@end
为了记录每个房间当前的棋局情况,需要给每个房间都创建一个DMChessBoard 棋局对象。因为游戏大厅中有成千上万的房间(实际上,百万人同时在线的游戏大厅也有很多),那保存这么多棋局对象就会消耗大量的内存。有没有什么办法来节省内存呢?
这个时候,享元模式就可以派上用场了。像刚刚的实现方式,在内存中会有大量的相似对象。这些相似对象的mId、mText、mChessColor属性拆分出来,设计成独立的类,并且作为享元供多个棋盘复用。这样,棋盘只需要记录每个棋子的位置信息就可以了。具体代码实现如下:
typedef enum : NSInteger {
DMChessColorRed = 0,
DMChessColorBlack = 1,
} DMChessColor;
@interface DMChessPieceUnit : NSObject
- (instancetype)initWithChessPieceId:(NSString *)chessPieceId chessPieceText:(NSString *)chessPieceText chessPieceColor:(DMChessColor)chessPieceColor;
@property (nonatomic, strong, readonly) NSString *mId;
@property (nonatomic, strong, readonly) NSString *mText;
@property (nonatomic, assign, readonly) DMChessColor mChessColor;
@end
@interface DMChessPieceUnit ()
@property (nonatomic, strong, readwrite) NSString *mId;
@property (nonatomic, strong, readwrite) NSString *mText;
@property (nonatomic, assign, readwrite) DMChessColor mChessColor;
@end
@implementation DMChessPieceUnit
- (instancetype)initWithChessPieceId:(NSString *)chessPieceId chessPieceText:(NSString *)chessPieceText chessPieceColor:(DMChessColor)chessPieceColor {
if (self = [super init]) {
self.mId = chessPieceId;
self.mText = chessPieceText;
self.mChessColor = chessPieceColor;
}
return self;
}
@end
@interface DMChessPiece : NSObject
- (instancetype)initWithChessPieceUnit:(DMChessPieceUnit *)chessPieceUnit positionX:(NSInteger)positionX positionY:(NSInteger)positionY;
@property (nonatomic, strong) DMChessPieceUnit *mChessPieceUnit;
@property (nonatomic, assign) NSInteger mPositionX;
@property (nonatomic, assign) NSInteger mPositionY;
@end
@implementation DMChessPiece
- (instancetype)initWithChessPieceUnit:(DMChessPieceUnit *)chessPieceUnit positionX:(NSInteger)positionX positionY:(NSInteger)positionY {
if (self = [super init]) {
self.mChessPieceUnit = chessPieceUnit;
self.mPositionX = positionX;
self.mPositionY = positionY;
}
return self;
}
@end
@interface DMChessPieceFactory : NSObject
+ (DMChessPieceUnit *)getChessPiece:(NSString *)chessPieceId;
@end
static NSMutableDictionary *standardChessPieces = nil;
@implementation DMChessPieceFactory
+ (void)load {
standardChessPieces = [[NSMutableDictionary alloc] init];
[standardChessPieces setObject:[[DMChessPieceUnit alloc] initWithChessPieceId:@"0001" chessPieceText:@"車" chessPieceColor:DMChessColorRed]forKey:@"0001"];
[standardChessPieces setObject:[[DMChessPieceUnit alloc] initWithChessPieceId:@"0002" chessPieceText:@"馬" chessPieceColor:DMChessColorRed]forKey:@"0002"];
// 省略其它棋子的创建
}
+ (DMChessPieceUnit *)getChessPiece:(NSString *)chessPieceId {
return [standardChessPieces objectForKey:chessPieceId];
}
@end
@interface DMChessBoard : NSObject
- (void)moveChessPiece:(NSString *)chessPieceId toPositionX:(NSInteger)positionX toPositionY:(NSInteger)positionY;
@end
@interface DMChessBoard ()
@property (nonatomic, strong) NSMutableDictionary *chessPieces;
@end
@implementation DMChessBoard
- (instancetype)init {
if (self = [super init]) {
[self initData];
}
return self;
}
- (void)initData {
[self.chessPieces setObject:[[DMChessPiece alloc] initWithChessPieceUnit:[DMChessPieceFactory getChessPiece:@"0001"] positionX:0 positionY:0] forKey:@"0001"];
[self.chessPieces setObject:[[DMChessPiece alloc] initWithChessPieceUnit:[DMChessPieceFactory getChessPiece:@"0002"] positionX:0 positionY:1] forKey:@"0002"];
// 省略摆放其它棋子的代码
}
- (void)moveChessPiece:(NSString *)chessPieceId toPositionX:(NSInteger)positionX toPositionY:(NSInteger)positionY {
DMChessPiece *chessPiece = [self.chessPieces objectForKey:chessPieceId];
chessPiece.mPositionX = positionX;
chessPiece.mPositionY = positionY;
}
#pragma mark - getter and setter
- (NSMutableDictionary *)chessPieces {
if (_chessPieces == nil) {
_chessPieces = [[NSMutableDictionary alloc] init];
}
return _chessPieces;
}
@end
在上面的代码实现中,利用工厂类来缓存DMChessPieceUnit信息(也就是mId、mText、mChessColor)。通过工厂类获取到的DMChessPieceUnit 就是享元。所有的DMChessBoard 对象共享这32个DMChessPieceUnit对象(因为象棋中只有32个棋子)。在使用享元模式之前,记录1万个棋局,需要创建32万(32 * 1万)个棋子的DMChessPieceUnit对象。利用享元模式,只需要创建32个享元对象供所有棋局共享使用即可,大大节省了内存。
网友评论