享元模式详解

享元模式(Flyweight Design Pattern),顾名思义,就是被共享的单元。享元模式的意图是复用对象,节省内存的消耗。前提是享元对象是不可变对象。

享元模式把对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享复用不变的部分,达到减少对象数量并节约内存的目的。

优缺点

优点

  1. 减少对象频繁重复创建,降低内存消耗 ,提高效率。

缺点

  1. 提高了系统的复杂度,需要分离出外部状态和内部状态。

享元模式的构成

内部状态类:不变的部分,抽象出来作为享元的基类。
享元工厂类:所有内部状态类的集合,通过工厂类获取。
外部状态类:实际使用的类,可以自定义自己的属性和方法。

通过一个象棋的例子来说明享元模式。
象棋游戏中,有非常多的棋局,如果每个棋局都创建自己的棋子对象,则有大量重复的对象被创建出来。每个棋局中,用到的棋子都是一样的,只是坐标不一样,这些基本的棋子可以作为享元中的内部状态类。

把这些棋子对象使用享元工厂抽象出来复用。

每个棋局不一样的只是具体棋子的坐标,还有所属的棋局,这些每个棋局中具体的棋子就是外部状态类。

我们拿其中的帅和兵列举,其它是同理。

内部状态类

# 要被共享的类就是这些基本的棋子类
class Chess:
    pass


class Jiang(Chess):
    def __init__(self):
        self.text = "将"

    def __str__(self):
        return "这是一个将"


class Soldier(Chess):
    def __init__(self):
        self.text = "兵"

    def __str__(self):
        return "这是一个兵"

我们创建了基本的棋子类,以及基本的将和兵类,它们就是要被共享和复用的内部状态类。

享元工厂类

class FlyWeightFactory:

    def __init__(self):
        self.chess_map = {
            "jiang": Jiang(),
            "soldier": Soldier()
        }

    def get_chess(self, chess_name):
        assert chess_name in self.chess_map, "工厂中不存在该名字的类"
        return self.chess_map[chess_name]

通过一个享元工厂来缓存所有的基本棋子对象,工厂类获取到的就是享元。
这样就从每个棋局维护三十个多个基本棋子对象,变成了全局维护三十多个基本棋子对象,节省了大量内存。
每当有新的基本棋子类时,只需要在chess_map中新增该类实例化的映射即可。

外部状态类

class ChessPiece:
    def __init__(self, chessboard_id, chess: Chess, color):
        self.x = 0
        self.y = 0
        self.chessboard_id = chessboard_id
        self.color = color
        self.chess = chess

    def get_chess_info(self):
        info = f"chessboard_id:{self.chessboard_id};{self.chess} with {self.color} in {self.x}/{self.y}"
        return info

具体的棋子类,需要通过chessboard_id关联到具体的棋盘,并且有自己的坐标 x 和 y 以及颜色信息等,并且使用了基本棋子类的信息。

具体使用

# 享元工厂
fac = FlyWeightFactory()

# 创建一个id为1的棋盘中具体应用的红色小兵
chessboard_id = 1
red_soldier = ChessPiece(chessboard_id, fac.get_chess("soldier"), "red")
print(red_soldier.get_chess_info())

# 结果
chessboard_id:1;这是一个兵 with red in 0/0

总结

享元模式的实现非常简单,主要是通过工厂模式,在工厂类中,通过一个 map 或者 list 来缓存已经创建过的享元对象,来达到复用,减少内存消耗的目的。

享元模式与单例、缓存、线程池这些非常相似,都是为了共享,它们之间的区别主要是:

  • 应用单例模式是为了保证对象全局唯一。
  • 应用享元模式是为了实现对象复用,节省内存。
  • 缓存是为了提高访问效率,而非复用。
  • 池化技术中的“复用”理解为“重复使用”,主要是为了节省时间。

根据不同的应用场景,使用不同的共享方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tlqwanttolearnit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值