Flyweight模式

模式定义

 

Flyweight模式尤其适用于需要创建大量的颗粒对象,而这些对象在某些属性上又极为相似或着能够抽取出相同的属性的情况下,能大幅提高性能减少内存使用。

使用范围

 

  • 需要创建大量的颗粒对象(细微的,但是纹理清晰的)
  • 一个对象的外在属性能够抽取出来可供共享的

举例说明

 

假设需要在一个面板上绘出大量随机的圆圈图像,这些圆圈颜色是随机的,在面板上出现的位置也是随机的。通常的做法是在需要画出圆圈的时候new出一个Circle的实例,在画出来。假设需要画1000个圆圈,就需要创建1000个实例。

package zigzag.designpattern.flyweight;

import java.awt.*;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

class Painter extends JFrame{
   private static final Color colors[] = { Color.red, Color.blue,
                                           Color.yellow, Color.orange,
                                           Color.black,  Color.white };
   private static final int WIDTH = 600,
                            HEIGHT = 400,
                            NUMBER_OF_CIRCLES = 1000;

   public Painter() {
      Container contentPane = getContentPane();
      
      JButton button = new JButton("Draw Circle");
      final JPanel  panel  = new JPanel();

      contentPane.add(panel,  BorderLayout.CENTER);
      contentPane.add(button, BorderLayout.SOUTH);
      setSize(WIDTH ,HEIGHT);
      setTitle("Flightweight demo by ZigzagSoft.net");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setVisible(true);

      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent event) {
            Graphics g = panel.getGraphics();

            for(int i=0; i < NUMBER_OF_CIRCLES; ++i) {
               Circle circle = new Circle(getRandomColor());
               circle.draw(g, getRandomX(), getRandomY(), getRandomR());//1000 object created.
            }
         }
      });
   }

   private int getRandomX() {
      return (int)(Math.random()*WIDTH );
   }
   private int getRandomY() {
      return (int)(Math.random()*HEIGHT);
   }
   private int getRandomR() {
      return (int)(Math.random()*(HEIGHT/10));
   }
   private Color getRandomColor() {
      return colors[(int)(Math.random()*colors.length)];
   }
   public static void main(String[] args) {
      Painter painter = new Painter();
   }   
}

class Circle {
	   private Color color;

	   public Circle(Color color) {
	      this.color = color;
	   }
	   public void draw(Graphics g, int x, int y, int r) {
	      g.setColor(color);
	      g.drawOval(x, y, r, r);
	   }
	}

注意到在按钮事件中,我们循环创建Circle实例,消耗了太多的内存。

            for(int i=0; i < NUMBER_OF_CIRCLES; ++i) {
               Circle circle = new Circle(getRandomColor());
               circle.draw(g, getRandomX(), getRandomY(), getRandomR());//1000 object created.
            }

 

一个改进的做法是使用Flyweight。我们可以把创建过后的新的Circle存放在一个内存中,下次再次创建的时候,我们无须重新创建,而是每次都从内存中查找是否曾经创建过相同颜色的圆圈,有,则直接取出,否则才创建并保存至内存供下次使用。此处,Circle是一个细微的对象,颜色是它的一个纹理,可供大量共享。因此我们可以使用Singleton模式来维护这个内存的变化。一个改进的模式引入了新类CircleFactory

class CircleFactory {
   private static final HashMap circleByColor = new HashMap();

   public static Circle getCircle(Color color) {
      Circle circle = (Circle)circleByColor.get(color);

      if(circle == null) {
         circle = new Circle(color);
         circleByColor.put(color, circle);
         System.out.println("Creating " + color + " circle");
      }
      return circle;
   }
}

同时,创建Circle的方法也需要调整:

for(int i=0; i < NUMBER_OF_CIRCLES; ++i) {
                Circle circle = CircleFactory.getCircle(getRandomColor());
                circle.draw(g, getRandomX(), getRandomY(),getRandomR());
               //Since we have 6 different colors, we have 6 objects created.
            }

下载示例

 

<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[使用外部状态] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值