Flyweight模式

Flyweight享元设计模式是一种结构型设计模式,它主要解决的问题是:由于(同类)对象的数量太大,采用面向对象时给系统带来了难以承受的内存开销。比如有这样一个场景:一个停车场中有1000辆汽车,我们所定义的汽车对象占用内存0.3M,那么要实例化1000辆就是300M。由此可见,在这种情况下采用一般的面向对象方式出现了大量细粒度的对象会很快充斥在系统中,从而带来很高的运行是代价(这里指的代价是内存开销的代价)。

       GoF《设计模式》中说道:运用共享技术有效的支持大量细粒度的对象。

 

案例分析

      定义一个场景:有一个汽车类型,客户程序要实例化20000个,实例化后查看一下内存分配情况。

 

 

#include "stdafx.h"
#include "windows.h"
#include "iostream.h"
#include <string>
#include <list>
#include <map>
#pragma warning (disable: 4786)

/*
class Car
{
    private:
    std::string Body;
    std::string Wheel;
    std::string Engine;
    std::string Brand;
    std::string Color;  

    public:
    Car(std::string body,std::string wheel,std::string engine,std::string brand,std::string color)
    {
        Body = body;
       Wheel = wheel;
       Engine = engine;
       Brand = brand;
       Color = color;
    }

};

int main(int argc, char* argv[])
{
    unsigned long MemCount = 0;
    MEMORYSTATUS  MemInfo;  
    MemInfo.dwLength = sizeof(MEMORYSTATUS);  
    GlobalMemoryStatus(&MemInfo);

    cout << "内存分配情况如下:" << endl;
    MemCount = MemInfo.dwAvailVirtual;
    cout << "实例化前:" << MemInfo.dwAvailVirtual << endl;

    std::list<Car> m_list;


    for(int i = 0;i < 20000;i++) 
    {
        Car * car = new Car("4.2M","Wheel","NeedForSpeed","BMW","Black");
       //m_list.push_back(*car);
    }

    GlobalMemoryStatus(&MemInfo);
    cout << "实例化后:" << MemInfo.dwAvailVirtual << endl;
    cout << "实例化后:" << MemCount - MemInfo.dwAvailVirtual << endl;

    return 0;
}
*/

/*
内存分配情况如下:
实例化前:2140868608
实例化后:2125139968
实例化后:15728640
*/


class CarBody

     private:
     std::string Body; 
     std::string Wheel; 
     std::string Engine;
     std::string Color;  
 
     public:
     CarBody(){};
     CarBody(std::string body,std::string wheel,std::string engine,std::string color)
    {
         Body = body; 
         Wheel = wheel;
         Engine = engine;
         Color = color; 
    }
};

class FlyWeightCar
{
     public:
     std::string Brand;
     CarBody * carbody;

 

     public:
     FlyWeightCar(){};
};


class FlyWeightFactory
{

 private:
     static FlyWeightCar * Car;
     static std::map<std::string,FlyWeightCar> table;

 

 public:
      static FlyWeightCar * CreateInit(std::string body,std::string wheel,std::string engine,std::string brand,std::string color)
     {
         FlyWeightCar * car = Car;
         if(brand.compare("BMW") == 0)
         {
             //car = &(FlyWeightCar)table[brand];
             car = Car;
         }
         else
         {
             car->Brand = brand;
             car->carbody = new CarBody(body,wheel,engine,color);
             std::pair<std::string,FlyWeightCar> item (brand, *car);
             table.insert(item);
          }
        return car;
    }
};

 

FlyWeightCar * FlyWeightFactory::Car = new FlyWeightCar();

std::map<std::string,FlyWeightCar> FlyWeightFactory::table;

 

int main(int argc, char* argv[])
{
     unsigned long MemCount = 0;
     MEMORYSTATUS  MemInfo;  
     MemInfo.dwLength = sizeof(MEMORYSTATUS);  
     GlobalMemoryStatus(&MemInfo);

     cout << "内存分配情况如下:" << endl;
     MemCount = MemInfo.dwAvailVirtual;
     cout << "实例化前:" << MemInfo.dwAvailPhys  << endl;

     std::list<FlyWeightCar> m_list;


     for(int i = 0;i < 10000;i++) 
     {
         FlyWeightCar * car = FlyWeightFactory::CreateInit("4.2M","Wheel","NeedForSpeed","Benz","Black");
         //m_list.push_back(*car);
     }
 
     for(i = 0;i < 10000;i++) 
    {
         FlyWeightCar * car = FlyWeightFactory::CreateInit("4.2M","Wheel","NeedForSpeed","BMW","Black");
         //m_list.push_back(*car);
    }
   
    GlobalMemoryStatus(&MemInfo);
    cout << "实例化后:" << MemInfo.dwAvailPhys  << endl;

    cout << "占用内存:" << MemCount - MemInfo.dwAvailVirtual << endl;
    return 0;
}


/*
内存分配情况如下:
实例化前:328978432
实例化后:324227072
占用内存:7340032
*/

 

    从数字上不难看出内存分配的容量节省了不少,而且随着数量的增加,差距会更大,当数量教少时,普通方式的内存分配更小一些,所以,在使用时,我们一定要对所实例化对象的个数进行评估,否则的话会适得其反。

      

Flyweight模式的几个要点:

       1、面向对象很好的解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。

2Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象的状态处理。

3、对象的数量太大从而导致对象内存开销加大(这个数量要经过评估,而不能凭空臆断)

<think>我们正在讨论设计模式中的Flyweight模式(享元模式)。根据用户的问题,他之前询问了字面量的定义,但现在转向了Flyweight模式。用户要求定义Flyweight模式,并提供了多个引用。我们需要基于这些引用和已知的设计模式知识来回答。 参考引用: [^1]: C++中的Flyweight设计模式详解。Flyweight模式的实现首先定义了一个Flyweight接口,这个接口通常包含一个或多个纯虚函数,用于执行对象外部状态依赖的操作。 [^2]: 我们先编写一个Flyweight类的定义,它代表一个可共享对象。如下:public abstract class Shape { ... } [^3]: 抽象享元角色(Flyweight):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法。 [^4]: 蝇量工厂(FlyweightFactory):负责管理蝇量对象,形成一个对象池,提供取出对象的方法。 [^5]: 享元模式是一种结构型设计模式,它通过将对象的可共享部分与不可共享部分分离,从而减少重复对象的数量,达到节省内存的目的。 根据这些引用,我们可以总结出Flyweight模式的定义和关键组成部分。 享元模式Flyweight Pattern)是一种结构型设计模式,它通过共享技术来有效支持大量细粒度对象的复用。它通过共享已经存在的对象来减少创建对象的数量,从而减少内存占用和提高性能。 享元模式的核心在于区分内部状态(Intrinsic State)和外部状态(Extrinsic State): - 内部状态:对象中不会随环境改变而改变的部分,这部分状态可以被共享,存储在享元对象内部。 - 外部状态:对象中会随环境改变而改变的部分,这部分状态不能被共享,需要在调用时由外部传入。 享元模式的主要角色包括: 1. 抽象享元角色(Flyweight):定义了一个接口,通过这个接口可以接受并作用于外部状态。 2. 具体享元角色(ConcreteFlyweight):实现抽象享元接口,并为内部状态增加存储空间。该角色必须是可共享的,并且任何状态都应该是内部的。 3. 非共享具体享元角色(UnsharedConcreteFlyweight):并不是所有的享元对象都需要共享,非共享的具体享元角色通常作为组合中的叶节点。 4. 享元工厂角色(FlyweightFactory):负责创建和管理享元对象。它确保合理地共享享元对象,当用户请求一个享元对象时,工厂会检查是否已经存在一个符合要求的享元对象,如果存在则返回,否则创建一个新的。 使用享元模式的典型场景: - 一个应用程序使用大量的对象。 - 大量的对象造成很大的存储开销。 - 对象的大多数状态可以外部化(变为外部状态)。 - 使用享元模式后,由于共享对象,存储开销得以降低。 优点: - 减少内存中对象的数量,节省内存。 - 减少了创建对象的时间,提高性能。 缺点: - 增加了系统的复杂度,需要分离出内部状态和外部状态。 - 外部状态需要由客户端保存,并在使用享元对象时传入,这可能增加系统的逻辑复杂度。 示例代码(基于引用[2]的Shape类): 假设我们有一个图形类,颜色是内部状态,而位置和大小可以是外部状态。 抽象享元角色(Shape): ```java public abstract class Shape { protected String color; // 内部状态(颜色) public Shape(String color) { this.color = color; } public abstract void draw(int x, int y); // 外部状态(坐标) } ``` 具体享元角色(Circle): ```java public class Circle extends Shape { public Circle(String color) { super(color); } @Override public void draw(int x, int y) { System.out.println("Drawing a " + color + " circle at (" + x + "," + y + ")"); } } ``` 享元工厂(ShapeFactory): ```java import java.util.HashMap; import java.util.Map; public class ShapeFactory { private static final Map<String, Shape> circleMap = new HashMap<>(); public static Shape getCircle(String color) { Circle circle = (Circle) circleMap.get(color); if (circle == null) { circle = new Circle(color); circleMap.put(color, circle); System.out.println("Creating circle of color : " + color); } return circle; } } ``` 客户端使用: ```java public class Client { private static final String colors[] = { "Red", "Green", "Blue", "White", "Black" }; public static void main(String[] args) { for (int i = 0; i < 20; ++i) { Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor()); circle.draw(getRandomX(), getRandomY()); } } private static String getRandomColor() { return colors[(int) (Math.random() * colors.length)]; } private static int getRandomX() { return (int) (Math.random() * 100); } private static int getRandomY() { return (int) (Math.random() * 100); } } ``` 在这个例子中,颜色相同的圆会被共享,所以即使我们画出20个圆,实际上创建的圆对象只有颜色种类数(5个)。每个圆的坐标(外部状态)在调用draw方法时传入。 根据引用[5]:享元模式通过分离内部状态和外部状态,共享内部状态,从而达到减少对象数量的目的。 因此,我们给出Flyweight模式的定义: 享元模式Flyweight Pattern)是一种用于减少对象数量、节省内存的结构型设计模式,它通过共享对象来支持大量细粒度对象的复用。 最后,生成相关问题。 注意:用户提供的引用中,引用[1]是C++的,引用[2]是Java的,但设计模式是语言无关的,我们以Java为例进行说明。 </think>### Flyweight模式(享元模式)定义 **Flyweight模式** 是一种结构型设计模式,它通过共享对象来**最小化内存使用**并优化性能,特别适用于存在大量相似对象的场景。该模式的核心思想是分离对象的**内部状态(可共享)** 和**外部状态(不可共享)**,并通过工厂管理共享对象池[^1][^3][^5]。 --- ### 核心概念解析 #### 1. **状态分离原理** - **内部状态(Intrinsic State)** 对象中**固定不变**的部分(如颜色、类型),可被多个对象共享。例如在图形系统中,圆形对象的颜色属性可共享[^2]。 - **外部状态(Extrinsic State)** 对象中**动态变化**的部分(如位置、大小),需由客户端传入。例如绘制圆形时需单独指定坐标[^5]。 #### 2. **关键角色** | 角色 | 功能 | 示例引用 | |----------------------|----------------------------------------------------------------------|--------------------| | **抽象享元(Flyweight)** | 定义共享对象的接口,声明操作外部状态的方法 | `Shape` 抽象类 [^2] | | **具体享元(ConcreteFlyweight)** | 实现抽象接口,存储内部状态(如颜色) | `Circle` 类 [^2] | | **享元工厂(FlyweightFactory)** | 管理对象池:复用已有对象或创建新对象 | 对象池管理 [^4] | | **客户端(Client)** | 维护外部状态,调用享元对象 | 调用 `draw()` [^2] | #### 3. **工作流程** ```mermaid sequenceDiagram Client->>FlyweightFactory: 请求对象(参数:内部状态) FlyweightFactory-->>Client: 返回共享对象 Client->>Flyweight: 执行操作(传入外部状态) Flyweight-->>Client: 返回结果 ``` --- ### 应用示例 #### 场景:图形渲染系统 假设需绘制1000个圆形,颜色只有红、蓝、绿三种。 **传统实现** 创建1000个圆形对象 → 内存占用大。 **Flyweight模式实现** 1. **共享内部状态(颜色)** ```java public abstract class Shape { protected String color; // 内部状态(可共享) public abstract void draw(int x, int y); // 外部状态(坐标) } ``` 2. **具体享元类** ```java public class Circle extends Shape { public Circle(String color) { this.color = color; } @Override public void draw(int x, int y) { System.out.println("Draw " + color + " circle at ("+x+","+y+")"); } } ``` 3. **享元工厂** ```java public class ShapeFactory { private static final Map<String, Shape> pool = new HashMap<>(); public static Shape getCircle(String color) { if (!pool.containsKey(color)) { pool.put(color, new Circle(color)); // 创建新对象 } return pool.get(color); // 返回共享对象 } } ``` 4. **客户端调用** ```java // 仅存储3个共享对象,而非1000个实例 Shape redCircle = ShapeFactory.getCircle("red"); redCircle.draw(10, 20); // 传入外部状态(坐标) ``` --- ### 模式价值与适用场景 | 优势 | 适用场景 | |-------------------------------|----------------------------------| | ✅ 减少内存占用(对象复用) | 大量相似对象的系统(如游戏粒子) | | ✅ 提升性能(避免重复创建) | 文本编辑器中的字符渲染 | | ✅ 解耦内部/外部状态 | 图形用户界面组件库 | | 局限性 | |-------------------------------| | ⚠️ 增加系统复杂度(状态分离) | | ️ 线程安全问题(共享对象池) | > 🔍 **总结**:Flyweight模式通过分离**可共享的固有属性**和**需外部传入的动态属性**,实现对象复用,解决海量细粒度对象的内存消耗问题[^5]。 ```mermaid graph LR A[客户端] -->|请求对象| B[享元工厂] B -->|对象存在| C[返回共享对象] B -->|对象不存在| D[创建新对象存入池] C --> E[具体享元] D --> E E -->|操作| F[使用外部状态] ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值