C++设计模式之享元模式(Flyweight)

yizhihongxing

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技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C typedef

    当我们使用C语言开发时,我们可能会遇到一些复杂的数据类型,为了使代码更加简单易读并方便调用这些数据类型,我们可以使用C语言中的typedef关键字来定义自定义的数据类型别名。本文将详细介绍C语言中typedef的使用方法,包括定义基本类型别名和结构体别名等内容。 定义基本类型别名 我们可以使用typedef定义一些基本类型的别名,例如: typedef un…

    C 2023年5月10日
    00
  • Android实现城市选择三级联动

    Android实现城市选择三级联动攻略 在Android开发中,有时会需要实现城市选择功能,而且还需要支持三级联动,即选择省份后展示该省份下的市区列表,选择城市后展示该城市下的县区列表。这里提供一份Android实现城市选择三级联动的攻略。 一、搭建数据源 实现城市选择三级联动首先需要有一个完整的城市数据源,可以采用第三方库提供的数据,也可以自己搭建。以下是…

    C 2023年5月23日
    00
  • 复杂JSON字符串转换为Java嵌套对象的实现

    将复杂的 JSON 字符串转换为 Java 嵌套对象可以使用 Gson 库来实现。具体步骤如下: 步骤一:添加依赖 在项目的 pom.xml 文件中添加如下依赖: <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</…

    C 2023年5月23日
    00
  • PTC Mathcad Prime 9.0破解许可安装详细教程(附下载)

    PTC Mathcad Prime 9.0破解许可安装详细教程 PTC Mathcad Prime 9.0是一款强大的工程计算软件,但是它的价格让很多人望而却步。为了让更多人使用到这个优秀的软件,以下是我整理的详细的破解许可安装教程。 第一步:下载软件和破解文件 需要下载PTC Mathcad Prime 9.0安装文件和破解文件。可以到官网或其他可靠网站下…

    C 2023年5月22日
    00
  • C#中ToString数据类型格式大全(千分符)

    C#中的ToString()方法可以将一个对象转换成字符串类型。当我们使用ToString()方法时,可以携带一个参数。这个参数可以是标准格式字符串,也可以是自定义格式字符串。在这个参数中,我们可以设置Convert类中的几种数据类型格式。 以下是数据类型格式大全: c/C:货币(Currency)格式; d/D:十进制(Decimal)格式; e/E:科学…

    C 2023年5月24日
    00
  • C语言控制台应用程序GDI绘制正弦曲线

    让我来给您详细讲解如何用C语言控制台应用程序GDI绘制正弦曲线的完整攻略。 什么是GDI GDI是Windows操作系统中的图形设备接口,全称为Graphical Device Interface,它提供了一组API让开发者能够在屏幕上绘制各种图形和文本。在C++和C#等高级编程语言中,GDI API可以直接调用来绘制各种各样的图形,而对于C语言控制台应用程…

    C 2023年5月30日
    00
  • C++操作.json文件的超详细新手教程

    C++操作.json文件的超详细新手教程 什么是JSON文件? JSON全称JavaScript Object Notation,是一种轻量级的数据交换格式。它基于JavaScript语言的一个子集,采用完全独立于编程语言的文本格式标准来表示数据。JSON实现简单,易于读写,同时易于机器解析和生成,因此成为前后端数据交互的重要工具。 选择合适的JSON库 C…

    C 2023年5月23日
    00
  • C语言模拟实现密码输入的示例代码

    下面是关于“C语言模拟实现密码输入的示例代码”的完整攻略。 一、问题描述及解决思路 在C语言中,实现密码输入的方式一般是通过scanf或gets函数来实现。但这两种方式都有一个共同的问题,就是在输入密码时,密码会被明文显示在屏幕上,存在安全隐患。因此,为了提高系统的安全性,可以使用一些特殊的函数来模拟实现密码输入功能。 在C语言中,实现密码输入可以借助于Wi…

    C 2023年5月24日
    00
合作推广
合作推广
分享本页
返回顶部