什么是flyweight(元享)模式

享元模式解析
本文深入浅出地介绍了享元模式的原理与应用,通过歌手发行专辑的例子,详细讲解了如何利用享元模式避免大量重复对象的创建,提高系统效率。
部署运行你感兴趣的模型镜像

  对于设计模式我一直都在悟,说实在话,设计模式并不能让你的代码越来越简单,反而会让其复杂化,理解设计模式不难,吸收设计模式是痛苦的过程。 
  设计模式是实践的产物,有需求才有改进,是一种聪明的偷懒方法,gof的设计模式比较抽象化,看看就知道是博士创作出来的东西,高度抽象形成理论,而理论是比较难于理解的,今天我斗胆也来谈谈对设计模式的一点感悟,说实话,现阶段我对设计模式的理解和吸收还在一个比较初级的阶段,所以我开篇说了“悟”,所以有什么说的不对,写的不好的地方还需要各位朋友的支持和改正。 
  废话少说了,今天接触到了flyweight(元享模式),于是我先不急看手边的理论,想先查查资料,看看有什么比较通俗易懂,一针见血的好文,先让我有个直观的感受,回头再结合理论做深入的研究,这是我习惯的学习过程,效果也是不错的,至少印象深刻。但是发现关于此模式的介绍并不多,而且内容差不多,虽然这样,但是看完了一些还是有了认识,学习是要有动力的,现在面临着系统的重构工作,我想这就是我的动力,学以致用,用过了才有资格说自己会了,用的好不好,对不对暂且不说,至少这个过程是有益的,是会促使人思考的,是追求良好设计思想,优秀程序结构的表现。设计模式是要思考的,我往往一个模式要想半天,并不是我想不通,看不懂,而是想它背后的许多,总是有许多为什么,呵呵。 
  什么是flyweight(元享)模式,为什么要叫flyweight? 
  flyweight如果用中文来说是“次最轻量级的拳击选手”的意思,一开始我就很纳闷,为什么取这样一个名字,然后再看看中文对于这种模式的解释,叫“元享”模式,又是一头雾水,虽然说名字只是个称谓,一个记号,但是我相信在给这种模式命名的时候,前辈们应该是考虑过要和这种模式的本质有所关联的,是要符合这种模式本身特点和气质的。看到后来,似乎对英文名和中文名有了那么点感觉,又或者说如果不叫这个名字还能叫什么。 
  什么是flyweight(元享)模式呢?其实就是一种为了避免在我们的系统中产生大量重复对象的方法,也是一种追求解耦,实现封装抵抗需求变化的过程。 
  因为名字之所以有轻量级这层含义,我个人理解是针对系统中这样的对象而言,它们容易被重复产生,因为它们自身比较单纯,本身的侵入性比较小,有一层“轻”的概念在里面,而中文名叫“元享”,分两层来理解,第一,元是什么?我把元认为就是这样的对象,我们为什么会重复创建这些对象,因为它们是本元,万物不离其本元,我给你元,至于你怎么变化,怎么给它添砖加瓦,怎么组合排列那是你的事情,我只负责给你我的这个元,打个比方,我们每个人都有多重角色,你是男人,也是父亲,还是儿子,又是丈夫,区分这些角色的原则是要看你在什么样的环境下,你和谁在一起,比如我是个歌手,我可以出一张JAZZ专辑,也可以出一张ROCK的,或者RAP的,只要你的实力够档次,所以我们会有各式各样的对象就出来,无非是你这个人加上点什么就成了什么,这里就会产生一个问题,如果你把我new在了办公室里我就成了工作时的我,把我new在家里,我就是家里的我了,如果你同时把我new在办公室和家里,就会有两个我,虽然这好像说不通,但是我们本身是不能分身的,在程序里我们完全有能力做到这点,这样不断的new下去会有大量的我出现在各个地方,发挥各种作用,这样多爽啊,呵呵! 
  假如我能分身,那我不希望和这些特有的环境和物质相绑定来完成我的角色,为什么,因为联系的太过紧密,那是什么意思,其实就是高耦合。我需要一个地方来派发单纯的我,然后把我派到各个地方担任各个角色,(这不是工厂模式吗?)没错,元享模式就是应用了工厂模式,我就待在这个工厂里哪也不去,除非有命令,那我就出去。这里需要注意的是我的元神依然在工厂里,你忘记了吧,我会分身的,所以你在工厂外面的只是我的分身。 
  继续往下吧,哎,解释个名字解释半天,看来我终究不是博士,抽象能力太差。。。 
  文字的力量不及代码来的明显,让我举个例子,然后来看看代码。 
  什么例子呢,我自己也不想想了,就用歌手出CD专辑这个例子吧!这样一句话落在我们程序员脑子里就很不一样了,你从这句话中体会到了什么?歌手,CD,或许还有歌,专辑名称等等。。。(万物皆对象) 
  很好,看来你想象能力还真是不错呢,CD这个东西我们就来把他抽象一把,看代码: 
  
 

Java代码 
  1. public abstact class CD{  
  2.     private Artist artist;  
  3.      private CDOtherThings  special;  
  4.      public CD(Artist artist,CDOtherThings special){  
  5.         this.artist=artist;  
  6.         this.special = special;  
  7.      }  
  8.      //忽略了get和set方法  
  9.      public void doSomeSpecial(CDOtherThings special);  
  10. }   
  11. public class CDOtherThings {  
  12.     private String cd_name;  
  13.     private ArrayList<String> songsList;  
  14.     public CDOtherThings(String cdName,ArrayList<String> songsList){  
  15.         this.cd_name = cdName;  
  16. this.songsList = songsList;  
  17.     }  
  18.     public String getCd_name() {  
  19. return cd_name;  
  20.     }  
  21.     public void setCd_name(String cd_name) {  
  22. this.cd_name = cd_name;  
  23.     }  
  24.     public ArrayList<String> getSongsList() {  
  25. return songsList;  
  26.     }  
  27.     public void setSongsList(ArrayList<String> songsList) {  
  28. this.songsList = songsList;  
  29.     }  
  30.  }  
  31. //再来个歌手类  
  32. public class Artist {  
  33.     private String name;  
  34.     private String age;  
  35.     private String company;  
  36.     public Artist(String name){  
  37.         this.name=name;  
  38.     }  
  39.     public String getAge() {  
  40.         return age;  
  41.     }  
  42.     public void setAge(String age) {  
  43.         this.age = age;  
  44.     }  
  45.     public String getCompany() {  
  46.         return company;  
  47.     }  
  48.     public void setCompany(String company) {  
  49.         this.company = company;  
  50.     }  
  51.     public String getName() {  
  52.         return name;  
  53.     }  
  54.     public void setName(String name) {  
  55.         this.name = name;  
  56.     }  
  57.   }  

  我刚才说了,歌手是一直要待在工厂里提供给外面的是分身了,那么让我们来搞个工厂吧,注意,这可以此模式的精髓哦! 
 
Java代码 
  1.  public class ArtistFactory {  
  2.   
  3.      private Hashtable<String,Artist> artist_table;  
  4.   
  5.      public Artist getArtist(String name){  
  6.          Artist artist = null;  
  7.  if(artist_table==null){  
  8.      artist_table = new Hashtable<String,Artist>();  
  9.      artist = new Artist(name);  
  10.      artist_table.put(name,artist);  
  11.      return artist;  
  12.           }  
  13.           artist = artist_table.get(name);  
  14.   if(artist==null){  
  15.       artist = new Artist(name);  
  16.       artist_table.put(name,artist);  
  17.    }  
  18.    return artist;  
  19. }  
  20.  }  

  我在这个工厂里维护了一个数据结构,我们的元神就待在这里,命令来了我们要看一下这个数据结构中有没有我们想要的元神,如果有就给他,其实给的是副本了,如果没有我们就创建一个给它,但是我们还要把它放在元神共存的这个数据结构里,这里,这个key就很重要了,我们方便起见就拿歌手名称来作为key去找本人咯,呵呵。 
  接下来让我们继承一下这个CD类吧 
  
 
Java代码 
  1.     
  2. public class CDChild extends CD{      
  3.     public CDChild(Artist artist,CDOtherThings special){  
  4.         super(artist,special);      
  5.     }  
  6.     public void doSomeSpecial(CDOtherThings special){  
  7.         String cd_name = special.getCd_name();  
  8.         ArrayList<String> songsList = special.getSongsList();  
  9.         if(cd_name==null||"".equals(cd_name)){  
  10.             System.out.println("no cd name!");  
  11.        return;  
  12.     }  
  13.     if(songsList==null||songsList.size()==0){  
  14.         System.out.println(cd_name+"no songs!");  
  15.         return;  
  16.     }  
  17.     int size = songsList.size();  
  18.     for(int i=0;i<size;i++){  
  19.         String songName = songsList.get(i);  
  20.         System.out.println(songName);  
  21.     }  
  22.      }  
  23.   }  
  24.   //下面就是测试代码咯  
  25.   public class Test {  
  26.       
  27.       public static void main(String[]args){  
  28.           
  29.           ArrayList<String> songsList = new ArrayList<String>();  
  30.      songsList.add("aSong");  
  31.      songsList.add("bSong");  
  32.      CDOtherThings special = new CDOtherThings("CD_NAME_ONE",songsList);  
  33.      ArtistFactory factory = new ArtistFactory();  
  34.      Artist artist = factory.getArtist("LP");  
  35.      CD cd = new CDChild(artist,special);  
  36.         cd.doSomeSpecial(special);  
  37.      }  
  38.     }    
  39.     

  此时,“LP”这个乐队在这里就出了这样一张“CD_NAME_ONE”这张专辑,但是不管我用这种方式给“LP”去发几张不同的专辑,“LP”在工厂里有一个元神。这就避免了重复创建“LP”。 
  flyweight(享元)模式还有两个概念: 
  1,intrinsic 内部状态 
  2,extrinsic 外部状态 
  其实就是Artist和CDOtherThings的意思,我是这么理解的。 
  当然,此模式也可以不支持共享元神对象的,这里就不多做说明了,这里的不共享是指Artist对象,我可以待在那个数据结构中等待命令来调用我,也可以每次都new一下new一下,具体情况具体分析了。 
  写完了,也不知道例子卡不恰当,表述的清楚不清楚,希望能对一些朋友有帮助,同时也是对自己学习的一个总结,相当于再一次学习和升华。

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<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、付费专栏及课程。

余额充值