C++ 设计模式之享元模式

【声明】本题目来源于卡码网(题目页面 (kamacoder.com)

【提示:如果不想看文字介绍,可以直接跳转到C++编码部分】


【简介】什么是享元模式 -- 可重复使用

享元模式是⼀种结构型设计模式 ,在享元模式中,对象被设计为可共享的 ,可以被多个上下⽂使⽤,⽽不必在每个上下⽂中都创建新的对象。

想要了解享元模式,就必须要区分什么是内部状态,什么是外部状态。

  • 内部状态 是指那些可以被多个对象共享的状态,它存储在享元对象内部,并且对于所有享元对象都是相同的,这部分状态通常是不变的
  • 外部状态 是享元对象依赖的、可能变化的部分。这部分状态不存储在享元对象内部,⽽是在使⽤享元对象时通过参数传递给对象。

举个例⼦,图书馆中有很多相同的书籍,但每本书都可以被多个⼈借阅,图书馆⾥的书就是内部状态,⼈就是外部状态。

再举个开发中的例⼦,假设我们在构建⼀个简单的图形编辑器,⽤户可以在画布上绘制不同类型的图形,⽽图形就是所有图形对象的内部状态(不变的),⽽图形的坐标位置就是图形对象的外部状态(变化的)。

如果图形编辑器中有成千上万的图形对象,每个图形对象都独⽴创建并存储其内部状态,那么系统的内存占⽤可能会很⼤,在这种情况下,享元模式共享相同类型的图形对象,每种类型的图形对象只需创建⼀个共享实例,然后通过设置不同的坐标位置个性化每个对象,通过共享相同的内部状态,降低了对象的创建和内存占⽤成本。


【基本结构】

享元模式包括以下⼏个重要⻆⾊:

  • 享元接⼝Flyweight: 所有具体享元类的共享接⼝,通常包含对外部状态的操作。
  • 具体享元类ConcreteFlyweight: 继承Flyweight 类或实现享元接⼝,包含内部状态。
  • 享元⼯⼚类FlyweightFactory: 创建并管理享元对象,当⽤户请求时,提供已创建的实例或者创建⼀个。
  • 客户端Client : 维护外部状态,在使⽤享元对象时,将外部状态传递给享元对象。

【简易实现】

享元模式的实现通常涉及以下步骤:(以Java代码作以说明)

1. 定义享元接⼝,接受外部状态作为参数并进⾏处理。

java 复制代码
// 步骤 1: 定义享元接⼝
interface Flyweight {
    // 操作外部状态
    void operation(String externalState);
}

**2. 实现具体享元类,**存储内部状态。

java 复制代码
// 步骤 2: 实现具体享元类
class ConcreteFlyweight implements Flyweight {
    private String intrinsicState; // 内部状态

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    @Override
    public void operation(String externalState) {
        System.out.println("Intrinsic State: " + intrinsicState + ", External State: "
        + externalState);
    }
}

3. 创建享元⼯⼚类,创建并管理Flyweight 对象,当⽤户请求⼀个Flyweight 时,享元⼯⼚会提供⼀个已经创建的实例或者创建⼀个。

java 复制代码
class FlyweightFactory {
    private Map<String, Flyweight> flyweights = new HashMap<>();

    public Flyweight getFlyweight(String key) {
        if (!flyweights.containsKey(key)) {
            flyweights.put(key, new ConcreteFlyweight(key));
        }
        return flyweights.get(key);
    }
}

4. 客户端使⽤享元模式

java 复制代码
public class Main {
    public static void main(String[] args) {
    FlyweightFactory factory = new FlyweightFactory();

    // 获取或创建享元对象,并传递外部状态
    Flyweight flyweight1 = factory.getFlyweight("A");
    flyweight1.operation("External State 1");

    Flyweight flyweight2 = factory.getFlyweight("B");
    flyweight2.operation("External State 2");

    Flyweight flyweight3 = factory.getFlyweight("A"); // 【重复使⽤!】已存在的享元对象
    flyweight3.operation("External State 3");
    }
}

【使用场景】

使⽤享元模式的关键在于包含⼤量相似对象,并且这些对象的内部状态可以共享。具体的应⽤场景包括⽂本编辑器,图形编辑器,游戏中的⻆⾊创建,这些对象的内部状态⽐较固定(外观,技能,形状),但是外部状态变化⽐较⼤时,可以使⽤。


【编码部分】

1. 题目描述

在一个图形编辑器中,用户可以绘制不同类型的图形,包括圆形(CIRCLE)、矩形(RECTANGLE)、三角形(TRIANGLE)等。现在,请你实现一个图形绘制程序,要求能够共享相同类型的图形对象,以减少内存占用;

2. 输入描述

输入包含多行,每行表示一个绘制命令。每个命令包括两部分: 图形类型(Circle、Rectangle 或 Triangle) 绘制的坐标位置(两个整数,分别表示 x 和 y);

3. 输出描述

对于每个绘制命令,输出相应图形被绘制的位置信息。如果图形是首次绘制,输出 "drawn at",否则输出 "shared at"。

4. C++ 编码部分

java 复制代码
/**
* @version Copyright (c) 2024 NCDC, Servo。 Unpublished - All rights reserved
* @file FlyWeightMode.hpp
* @brief 享元模式
* @autor 写代码的小恐龙er
* @date 2024/01/13
*/

#include <iostream>
#include <string>
#include <vector>
#include <map>

using namespace std;

// 形状枚举类
enum class ShapeType{
    CIRCLE,   // 圆形
    RECTANGLE,// 矩形
    TRIANGLE, // 三角形
};

// 前置声明

// 图形位置
class Position;
// 1 -- 享元接口类
class Shape;
// 2 -- 具体实现享元接口
class ConcreteShape;
// 3 -- 享元工厂
class ShapeFactory;

// 图形位置
class Position
{
// 成员数据
private:
    int _x;
    int _y;
    
// 成员函数
public:
    Position(){}
    Position(int x, int y){
        this->_x = x;
        this->_y = y;
    }
    
    // 成员函数获取接口
    int GetX(){
        return _x;
    }
    int GetY(){
        return _y;
    }
};

// 1 -- 享元接口类
class Shape
{
// 接口函数
public:
    virtual void DrawShape(Position *position) = 0;
};

// 2 -- 具体实现享元接口
class ConcreteShape : public Shape
{
// 成员数据
private:
    // 形状类型
    ShapeType _shapeType;
    // 首次绘制标志
    bool _firstTime = true;
// 成员函数
public:
    // 通过 形状类型来创建具体的对象
    ConcreteShape(ShapeType shapeType){
        this->_shapeType = shapeType;
    }
    // 设置是否首次绘制标志位
    void SetFirstTime(bool flag){
        this->_firstTime = flag;
    }
    // 重载基类的接口函数
    void DrawShape(Position *position){
        string outStr = "";
        
        switch(_shapeType)
        {
        case ShapeType::CIRCLE:
            outStr = "CIRCLE";
            break;
        case ShapeType::RECTANGLE:
            outStr = "RECTANGLE";
            break;
        case ShapeType::TRIANGLE:
            outStr = "TRIANGLE";
            break;
        }

        if(_firstTime) outStr += " drawn at (";
        else outStr += " shared at (";
        
        std::cout << outStr << (position->GetX()) << ", " << (position->GetY()) << ")" << endl;
    }
};

// 3 -- 享元工厂
class ShapeFactory
{
// 成员数据
private:
    // 包含可扩展的享元类
    std::map<ShapeType, Shape*> _shapesMap;
    
// 成员函数
public:
    Shape* GetShape(ShapeType type){
        std::map<ShapeType, Shape*>::iterator pos = _shapesMap.find(type);
        if(pos == _shapesMap.end())
        {
            // 如果在map中找不到具体的形状实现类 则新建
            _shapesMap.insert(make_pair(type, new ConcreteShape(type)));
        }
        
        // 返回 具体的形状实现类
        return _shapesMap[type];
    }
};

int main()
{
    // 形状类型 
    string type;
    // x y
    int x = 0;
    int y = 0;
    // 新建位置类
    Position *position = nullptr;
    
    // 新建享元工厂类
    ShapeFactory *shapeFactory = new ShapeFactory();
    
    while(std::cin >> type >> x >> y)
    {
        // 形状类型
        ShapeType shapeType;
        if(type == "CIRCLE")
        {
            shapeType = static_cast<ShapeType>(0);
        }
        else if(type == "RECTANGLE")
        {
            shapeType = static_cast<ShapeType>(1);
        }
        else if(type == "TRIANGLE")
        {
            shapeType = static_cast<ShapeType>(2);
        }
        
        // 位置类
        position = new Position(x, y);
        
        // 具体的享元实现类
        Shape *shape = shapeFactory->GetShape(shapeType);
        
        // 先进行绘制操作
        shape->DrawShape(position);
        
        // 通过 dynamic_cast 向下类型转换
        ConcreteShape *concreteShape = dynamic_cast<ConcreteShape *> (shape);
        if(concreteShape == nullptr) return 0;
        
        // 再将该类型的标志位置为false
        concreteShape->SetFirstTime(false);
    }
    
    
    // 析构
    if(position != nullptr){
        delete position;
        position = nullptr;
    }

    delete shapeFactory;
    shapeFactory = nullptr;
    
    return 0;
}


......

To be continued.

相关推荐
A懿轩A32 分钟前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
机器视觉知识推荐、就业指导37 分钟前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香37 分钟前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工2 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
大圣数据星球3 小时前
Fluss 写入数据湖实战
大数据·设计模式·flink
suweijie7683 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿4 小时前
List深拷贝后,数据还是被串改
java
思忖小下4 小时前
梳理你的思路(从OOP到架构设计)_设计模式Template Method模式
设计模式·模板方法模式·eit