C++设计模式之享元模式(Flyweight)攻略
概述
享元模式是一种结构型设计模式,它的主要目标是减少对象的数量,通过尽可能多的与其他相似对象共享来最小化内存占用和计算量。 在享元模式中,所有共享对象都以一个单一的实例存在于内存中,因此系统需要考虑识别这些对象以便正确地重用已经存在的实例,而不是创建新的对象。具体实现时,享元模式通过将需要重复使用的属性划分为内部状态(Intrinsic State)和外部状态(Extrinsic State)两类,其中内部状态是所有对象共享的,而外部状态是在对象被使用时动态变化的。
模式结构
- Flyweight(享元):定义一个接口,通过它可以接收并作用于外部状态;
- Concrete Flyweight(具体享元):实现Flyweight接口,并为内部状态增加存储空间;
- Unshared Concrete Flyweight(非共享具体享元):由于客户端需要,享元对象不能进行共享的子类;
- Flyweight Factory(享元工厂):用于创建和管理Flyweight对象,可以通过享元工厂实现对象池。
示例说明
下面我们通过两个示例来详细讲解享元模式的实现。
示例1
现在我们有一个图形绘制工具,其中包含多个形状对象(例如圆、矩形等),我们需要绘制这些对象。在此过程中,由于每个图形对象都包含许多共同的属性(如颜色、边框宽度等),因此我们可以使用享元模式来共享这些属性,达到优化性能的目的。
首先,我们定义一个抽象的Shape类作为所有图形对象的基类,并在其中定义共同的属性(如颜色和边框大小)。然后,通过实现Shape接口,我们可以创建ConcreteShape类(例如Circle和Rectangle),并为内部状态增加存储空间。
class Shape {
public:
virtual void draw(int x, int y) = 0;
protected:
string color_;
int borderWidth_;
};
class Circle : public Shape {
public:
Circle(string color, int borderWidth) {
this->color_ = color;
this->borderWidth_ = borderWidth;
}
void draw(int x, int y) override {
cout << "Draw a circle with " << this->color_ <<" color and "
<< this->borderWidth_ << " border width at (" << x << ", " << y << ")" << endl;
}
};
class Rectangle : public Shape {
public:
Rectangle(string color, int borderWidth) {
this->color_ = color;
this->borderWidth_ = borderWidth;
}
void draw(int x, int y) override {
cout << "Draw a rectangle with " << this->color_ << " color and "
<< this->borderWidth_ << " border width at (" << x << ", " << y << ")" << endl;
}
};
接下来我们再定义一个ShapeFactory类来实现享元工厂,以便创建和管理不同的Shape对象,其中,ShapeFactory的内部存储了一个SharePool对象,用于存放所有的共享Shape对象,并通过getShape方法对所有请求进行处理。如果请求对象已存在,直接返回共享对象;否则,创建新的对象并将其加入到SharePool中进行管理。
class SharePool {
public:
static shared_ptr<Shape> getShape(string color, int borderWidth) {
auto key = color + to_string(borderWidth);
auto it = shapeMap_.find(key);
if (it != shapeMap_.end())
return it->second;
auto shareShape = make_shared<Circle>(color, borderWidth);
shapeMap_[key] = shareShape;
return shareShape;
}
static void clear() {
shapeMap_.clear();
}
private:
static map<string, shared_ptr<Shape>> shapeMap_;
};
map<string, shared_ptr<Shape>> SharePool::shapeMap_;
class ShapeFactory {
public:
static shared_ptr<Shape> getShape(string color, int borderWidth) {
return SharePool::getShape(color, borderWidth);
}
};
最后,在客户端中,我们可以通过ShapeFactory来获取具体的圆形或矩形等图形对象,并将共同的属性作为参数传入。
int main() {
auto circle1 = ShapeFactory::getShape("red", 1);
circle1->draw(10, 20);
auto circle2 = ShapeFactory::getShape("red", 1);
circle2->draw(30, 40);
// circle2与circle1是同一个对象
auto rectangle = ShapeFactory::getShape("blue", 2);
rectangle->draw(50, 60);
return 0;
}
示例2
我们再以数据库连接为例,来进一步说明享元模式的使用场景。
假设我们正在编写一个数据库应用程序,其中有许多不同的数据库连接对象。在此过程中,由于每个连接都需要打开和关闭,并且提供的服务通常只需要少量的属性(如主机名、用户名和密码等),因此我们可以使用享元模式来共享这些属性,从而达到优化性能的目的。
首先,我们定义一个抽象的Database类作为所有数据库连接对象的基类,并在其中定义共同的属性和方法(如打开连接和关闭连接等)。然后,通过实现Database接口,我们可以创建多个ConcreteDatabase类(例如MySQL和Oracle),并为内部状态增加存储空间。
class Database {
public:
virtual void open() = 0;
virtual void close() = 0;
protected:
string host_;
string username_;
string password_;
};
class MySQL : public Database {
public:
MySQL(string host, string username, string password) {
this->host_ = host;
this->username_ = username;
this->password_ = password;
}
void open() override {
cout << "Open a MySQL database connection at " << this->host_
<< " with username " << this->username_ << endl;
}
void close() override {
cout << "Close the MySQL database connection at " << this->host_ << endl;
}
};
class Oracle : public Database {
public:
Oracle(string host, string username, string password) {
this->host_ = host;
this->username_ = username;
this->password_ = password;
}
void open() override {
cout << "Open an Oracle database connection at " << this->host_
<< " with username " << this->username_ << endl;
}
void close() override {
cout << "Close the Oracle database connection at " << this->host_ << endl;
}
};
接下来我们再定义一个DatabaseFactory类来实现享元工厂,以便创建和管理不同的Database对象,其中,DatabaseFactory的内部存储了一个SharePool对象,用于存放所有的共享Database对象,并通过getConnection方法对所有请求进行处理。如果请求对象已存在,直接返回共享对象;否则,创建新的对象并将其加入到SharePool中进行管理。
class SharePool {
public:
static shared_ptr<Database> getConnection(string host, string username, string password) {
auto key = host + username + password;
auto it = dbMap_.find(key);
if (it != dbMap_.end())
return it->second;
auto shareDb = make_shared<MySQL>(host, username, password);
dbMap_[key] = shareDb;
return shareDb;
}
static void clear() {
dbMap_.clear();
}
private:
static map<string, shared_ptr<Database>> dbMap_;
};
map<string, shared_ptr<Database>> SharePool::dbMap_;
class DatabaseFactory {
public:
static shared_ptr<Database> getConnection(string host, string username, string password) {
return SharePool::getConnection(host, username, password);
}
};
最后,在客户端中,我们可以通过DatabaseFactory来获取具体的MySQL或Oracle等数据库连接对象,并将共同的属性作为参数传入。
int main() {
auto conn1 = DatabaseFactory::getConnection("localhost", "root", "pwd");
conn1->open();
//do some work...
conn1->close();
auto conn2 = DatabaseFactory::getConnection("localhost", "root", "pwd");
conn2->open();
//do some work...
conn2->close();
// conn2与conn1是同一个对象
return 0;
}
总结
享元模式是一种优化性能的设计模式,它通过共享相似的对象来减少内存占用和计算量。在实现过程中,我们需要将需要重复使用的属性划分为内部状态和外部状态两类,并通过享元工厂来实现对象的创建和管理。享元模式在许多场景下都有很好的应用,例如图形绘制、数据库连接等。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++设计模式之享元模式(Flyweight) - Python技术站