定义
享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在减少创建对象的数量,从而减少内存消耗和提高性能。这个模式通过共享大量细粒度对象来实现。这种模式特别适用于需要大量对象的场景,比如在游戏开发中处理大量相似的对象时。
核心概念
-
享元(Flyweight) :享元对象是可以共享的对象,它们在系统中是重复使用的。享元模式将享元对象和具体的状态(外部状态)分离,以减少对象的创建和存储。
-
外部状态(External State) :享元对象不包含的、可以变化的状态,通常是对象的上下文信息。外部状态由客户端提供,而享元对象则只维护内部状态。
-
内部状态(Internal State) :享元对象自身的状态,这些状态在对象间是共享的。
结构
-
享元接口(Flyweight Interface) :定义了享元对象的公共接口,用于通过外部状态来处理具体的业务逻辑。
-
具体享元(Concrete Flyweight) :实现了享元接口,定义了内部状态的具体实现。每个具体享元对象都共享相同的内部状态。
-
享元工厂(Flyweight Factory) :管理享元对象的创建和共享。享元工厂确保每个享元对象只有一个实例,并为客户端提供共享的享元对象。
-
客户端(Client) :使用享元工厂获取享元对象,并将外部状态传递给享元对象,以实现具体业务逻辑。
代码示例
假设在开发一个图形编辑器,其中有很多相同类型的图形(如树木),每棵树木的外观是相似的,但它们的位置和大小可能不同。在享元模式中,我们可以将所有相同的树木外观(如颜色、形状)共享为一个享元对象,而将每棵树木的位置和大小作为外部状态传递给享元对象。
#include <iostream>
#include <unordered_map>
// 享元接口
class TreeType
{
public:
virtual ~TreeType() = default;
virtual void draw(int x, int y) const = 0;
};
// 具体享元
class ConcreteTreeType : public TreeType
{
private:
std::string name;
public:
ConcreteTreeType(const std::string& name) : name(name) {}
void draw(int x, int y) const override
{
std::cout << "Drawing tree type: " << name << " at (" << x << ", " << y << ")" << std::endl;
}
// 当前对象的地址
void printAddress() const
{
std::cout << "TreeType address: " << this << std::endl;
}
};
// 享元工厂
class TreeFactory
{
private:
std::unordered_map<std::string, std::shared_ptr<TreeType>> treeTypes;
public:
std::shared_ptr<TreeType> getTreeType(const std::string& name)
{
auto it = treeTypes.find(name);
if (it != treeTypes.end())
return it->second;
auto treeType = std::make_shared<ConcreteTreeType>(name);
treeTypes[name] = treeType;
return treeType;
}
};
// 客户端代码
class Tree
{
private:
std::shared_ptr<TreeType> treeType;
int x, y;
public:
Tree(const std::shared_ptr<TreeType>& treeType, int x, int y)
: treeType(treeType), x(x), y(y) {}
void draw() const
{
treeType->draw(x, y);
}
// 打印对象地址
void printTreeTypeAddress() const
{
std::dynamic_pointer_cast<ConcreteTreeType>(treeType)->printAddress();
}
};
int main()
{
TreeFactory treeFactory;
// 创建不同树木类型
auto oak = treeFactory.getTreeType("Oak");
auto pine = treeFactory.getTreeType("Pine");
// 打印树木类型的地址,验证共享
std::cout << "Address of Oak: ";
std::dynamic_pointer_cast<ConcreteTreeType>(oak)->printAddress();
std::cout << "Address of Pine: ";
std::dynamic_pointer_cast<ConcreteTreeType>(pine)->printAddress();
// 创建树木对象,并指定位置
std::vector<Tree> forest;
forest.emplace_back(oak, 10, 20);
forest.emplace_back(pine, 30, 40);
forest.emplace_back(oak, 50, 60);
forest.emplace_back(pine, 70, 80);
// 打印所有树木类型的地址,验证共享
for (const auto& tree : forest) {
tree.printTreeTypeAddress();
tree.draw();
}
system("pause");
return 0;
}
-
TreeType: 享元接口,定义了
draw方法来绘制树木。 -
ConcreteTreeType: 具体享元,包含具体的树木类型名称,并实现了
draw方法。 -
TreeFactory: 享元工厂,管理具体享元对象的创建和共享。它通过
getTreeType方法返回享元对象,并且确保相同名称的享元对象是共享的。在getTreeType方法中,TreeFactory首先检查哈希表中是否已经存在请求的享元对象(即是否已经创建过相同名称的树木类型)。如果存在,它直接返回已存在的对象;如果不存在,则创建新的对象,并将其存储到哈希表中,以供未来的请求复用。 -
Tree: 客户端对象,持有对享元对象的引用,并且包含具体的外部状态(树木的位置)。它通过
draw方法来绘制树木。 -
main: 客户端代码,使用
TreeFactory创建树木类型,然后创建不同位置的树木对象,并绘制它们。