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

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语言实现简单的贪吃蛇游戏的示例代码

    下面是详细的讲解“C语言实现简单的贪吃蛇游戏的示例代码”的攻略。 1. 前置知识 在开始编写贪吃蛇游戏代码之前,我们需要了解一些基本的C语言知识,包括:基本数据类型、条件语句、循环语句、函数、数组等等。如果对这些基础知识掌握不够熟练,建议先学习一下。 2. 游戏规则设计 在编写代码之前,我们需要明确游戏的规则和基本操作,例如: 蛇的移动方式:蛇可以向上、下、…

    C 2023年5月24日
    00
  • C/S和B/S两种架构区别与优缺点分析

    C/S和B/S两种架构区别与优缺点分析 C/S架构 C/S架构即客户端/服务器架构,是一种常见的软件架构模式。C/S架构中,客户端负责与用户交互,服务器负责执行核心业务逻辑。C/S架构需要在客户端和服务器端分别安装软件,因此需要专业的IT技能来安装和维护。 优点 可以在本地运行一些比较复杂或计算密集的程序,提高了程序的执行效率。 远程协作能力较强,多个用户可…

    C 2023年5月22日
    00
  • C++实现职工工资管理系统

    C++实现职工工资管理系统攻略 1. 系统需求分析 在开发职工工资管理系统前,我们需要先进行需求分析: 功能需求:该系统主要功能为实现职工的基本信息管理、工资发放和查询功能。 技术需求:采用C++语言实现,要求具备良好的代码结构和可扩展性。 2. 总体设计 系统总体设计包括以下几个部分: 实现一个职工类,用于存储每个职工的基本信息和工资信息。 设计一个管理类…

    C 2023年5月23日
    00
  • 学习C++编程的必备软件

    下面我将为您详细讲解“学习C++编程的必备软件”的完整攻略。 学习C++编程的必备软件 1. C++编译器 C++编译器是你学习编程时必备的工具之一。编译器负责将写好的C++程序翻译成机器可以理解的语言,让计算机可以运行它。以下是几个常用的C++编译器: Visual Studio:Visual Studio是一个非常强大的开发环境,附带了C++编译器和许多…

    C 2023年5月23日
    00
  • C语言 二叉查找树性质详解及实例代码

    C语言二叉查找树性质详解及实例代码 什么是二叉查找树? 二叉查找树,也称二叉搜索树,它是一种基于对比的动态数据结构。它的定义如下: 每个节点都包含一个键值,且键值唯一; 每个节点的左子树只包含小于当前节点的节点; 每个节点的右子树只包含大于当前节点的节点; 左右子树都是二叉搜索树; 二叉查找树的性质 二叉查找树的性质体现在它的增、删、查等操作中,具体有以下几…

    C 2023年5月24日
    00
  • C 程序 检查字母是元音还是辅音

    下面是关于“C 程序 检查字母是元音还是辅音”的完整使用攻略。该程序的主要思路是通过判断用户输入的字符是否为元音字母,来确定其为元音还是辅音。下面我们来逐步介绍该程序的使用步骤。 步骤一:复制代码 首先,在开始之前,需要复制如下的 C 语言代码: #include <stdio.h> #include <ctype.h> int ma…

    C 2023年5月9日
    00
  • 详解C语言实现猜数字游戏

    详解C语言实现猜数字游戏攻略 1. 猜数字游戏概述 对于猜数字游戏,通常来说,玩家会有一定的次数来猜测一个数字,如果猜对了,则游戏胜利;否则,游戏失败。在实现这个游戏的时候,我们需要完成以下几个步骤: 生成一个随机数字 让玩家进行猜测 判断猜测是否正确 根据判断结果输出信息 循环执行步骤2到4,直到达到游戏次数上限或者玩家获胜 在下面的部分中,我们将详细讲解…

    C 2023年5月22日
    00
  • C语言模拟实现atoi函数的实例详解

    C语言模拟实现atoi函数的实例详解 在C语言中,atoi函数能将字符串转化为整型数。本文将详细讲解C语言中模拟实现atoi函数的过程以及示例。 需求分析 想要实现atoi函数,我们需要明确要求的功能。即,将字符串转化为整型数。 实现思路 以下是实现atoi函数的思路: 首先考虑如何将字符转化为数字。C语言中,字符变量按照ASCII码表存储,因此可以通过in…

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