美文网首页
iOS设计模式之享元模式

iOS设计模式之享元模式

作者: 点滴86 | 来源:发表于2024-06-04 23:49 被阅读0次

享元模式(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个享元对象供所有棋局共享使用即可,大大节省了内存。

相关文章

网友评论

      本文标题:iOS设计模式之享元模式

      本文链接:https://www.haomeiwen.com/subject/yyquqjtx.html