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日

相关文章

  • .NET(C#)连接各类数据库代码-集锦

    . 前言 本文将为大家介绍如何使用.NET (C#)代码连接各种不同类型的数据库。无论是关系型数据库,还是NoSQL数据库,本文都将为您提供一条清晰明了的攻略。本文例举了连接MySQL和MongoDB两个数据库的示例,以供读者参考和学习。 . 连接MySQL数据库 连接MySQL数据库可以使用MySQL官方提供的Connector/NET组件,该组件提供了与…

    C 2023年5月22日
    00
  • C语言 实现N阶乘的程序代码

    对于实现N阶乘的程序代码,我们可以采用循环的方式进行求解,以下是详细的攻略: 1. 首先明确需求 我们需要实现一个能够求解N阶乘的程序代码,N可以是任意非负整数。 2. 编写程序代码 由于需要使用循环来进行计算,因此我们可以使用for循环来实现。以下是程序的代码: #include <stdio.h> int main() { int n, fa…

    C 2023年5月23日
    00
  • 威联通301W路由器怎么样? 威联通301W拆机测评

    威联通301W路由器测评攻略 介绍 威联通301W是一款智能路由器,拥有多种配置选项和广泛的应用功能。本篇文章将从拆机、性能、易用性等多个方面对该路由器进行测评,为大家提供详细介绍。 拆机 首先,我们需要打开威联通301W路由器的外壳,了解内部构造。拆机步骤如下: 用螺丝刀将底部五颗螺丝去除 待底部外壳拆除后,用螺丝刀将顶部四颗螺丝去除 拆下顶部外壳,就可以…

    C 2023年5月23日
    00
  • 一篇文章带你入门C语言:函数

    一篇文章带你入门C语言: 函数 函数的定义 函数是 C 语言中组织代码的一种主要方式。在 C 中,函数是由一系列语句组成的代码块,这些语句被命名并可以通过一个函数名来调用。 返回类型 函数名(参数列表) { // 函数体 } 返回类型:函数执行后返回的数据类型,例如 int、float 等。 函数名:函数的名称,可以根据函数的功能进行命名。 参数列表:函数执…

    C 2023年5月23日
    00
  • C语言switch语句详解

    C语言switch语句详解 简介 在C语言中,switch语句是一种多分支的选择结构,可以用来比对多个值,根据不同的值来执行对应的代码块。 语法 switch语句的基本语法如下: switch(expression){ case constant-expression1: statement(s); break; case constant-expressi…

    C 2023年5月24日
    00
  • C#向线程中传递多个参数的解决方法(两种)

    “C#向线程中传递多个参数的解决方法(两种)”是一个较为常见的问题,下面我将详细讲解其中的两种解决方案。 方案一:使用元组(Tuple) 在 C# 中,我们可以使用元组来将多个参数打包成一个参数,然后将该元组作为参数传递给线程。代码如下: private void StartThreadWithTuple() { // 创建包含多个参数的元组 var tup…

    C 2023年5月22日
    00
  • 超详细的cmake入门教程

    超详细的cmake入门教程 CMake 是一个开源的跨平台构建工具,可以自动化生成编译脚本,支持多种编译器和操作系统。本文将为大家介绍基本的 CMake 用法,以及如何在项目中使用 CMake 进行构建。 基本概念 在使用 CMake 之前,我们需要了解几个基本概念: 项目:即我们要构建的一个完整而独立的工程,由多个文件组成; 源文件:即工程中的源代码文件,…

    C 2023年5月23日
    00
  • JS循环遍历JSON数据的方法

    以下是详细的讲解”JS循环遍历JSON数据的方法”的完整攻略: 1. 解析JSON数据 在JS中,可以通过JSON.parse将字符串转换为JSON对象,也可以直接将JSON数据引入JS文件中,得到一个JSON对象。解析成功后,我们即可对JSON数据进行操作。 1.1 解析字符串为JSON对象 const jsonData = ‘{"name&qu…

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