首页 > 其他分享 >享元模式(FlyWeight Pattern)

享元模式(FlyWeight Pattern)

时间:2024-02-01 14:44:25浏览次数:25  
标签:享元 对象 Pattern color 棋子 FlyWeight 共享 public

享元模式(FlyWeight Pattern)

  概要

  记忆关键字:细粒度、共享

  定义:运用共享技术有效地支持大量细粒度的对象

  类型:结构型

  分析:共享对象,将对象的一部分状态(内部状态)设计成可共享的,以减少对象的数量,达到节省内存的目的。

  UML类图如下:


一、 涉及的角色
1.  抽象享元类 Flyweight
通常是接口或抽象类,它声明了具体享元类的公共方法。通过这些方法可以向外界提供享元对象的内部状态和设置外部状态。

2. 具体享元类 ConcreteFlyweight
它实现了抽象享元类所声明的方法,其实例称为享元对象,为内部状态提供存储空间。

3. 非共享具体享元类 UnSharedConcreteFlyWeight
并不是所有抽象享元类的子类都需要被共享,不需要被共享的外部状态可设计为非共享具体享元类,它以参数的形式注入到具体享元的相关方法中,可以直接实例化。

4. 享元工厂类 FlyWeightFactory
用于创建和管理享元对象
它针对抽象享元类编程,将各种具体享元对象存储在一个享元池中。当用户请求一个具体享元对象时,享元工厂会检査系统中是否存在符合要求的享元对象,如果存在则提供给客户端,如果不存在,就创建一个新的享元对象。

 

二、举例

   一个游戏厅中有成千上万个“房间”,每个房间对应一个棋局。棋局要保存每个棋子的数据,比如:棋子类型(将、相、士、炮等)、棋子颜色(红方、黑方)、棋子在棋局中的位置。利用这些数据,我们就能显示一个完整的棋盘给玩家。

ChessPiece类表示棋子
ChessBoard类表示一个棋局,里面保存了象棋中30个棋子的信息

为记录每个房间当前的棋局情况,要给每个房间都创建一个ChessBoard棋局对象。因为游戏大厅中有成千上万房间,保存这么多棋局对象就会消耗大量内存。如何节省内存呢?

就得用上享元模式啦。在内存中有大量相似对象。这些相似对象的id、text、color都一样,仅棋子在棋局中的位置:positionX、positionY不同。将棋子的id、text、color属性拆出来,设计成独立类,并且作为享元供多个棋盘复用。棋盘只需记录每个棋子的位置信息
利用工厂类缓存ChessPieceUnit信息(也就是id、text、color)。通过工厂类获取到的ChessPieceUnit就是享元。所有的ChessBoard对象共享这30个ChessPieceUnit对象(因为象棋中只有30个棋子)。在使用享元模式之前,记录1万个棋局,我们要创建30万(30*1万)个棋子的ChessPieceUnit对象。利用享元模式,我们只需要创建30个享元对象供所有棋局共享使用即可,大大节省了内存。

主要通过工厂模式,在工厂类中,通过Map缓存已创建过的享元对象,达到复用。

示例代码如下:

 1 // 抽象享元类:棋子单元
 2 public abstract class ChessPieceUnit {
 3     private int id;
 4     private String type;
 5     private ChessPieceColor color;
 6 
 7     public ChessPieceUnit(int id, String type, ChessPieceColor color) {
 8         this.id = id;
 9         this.type = type;
10         this.color = color;
11     }
12 
13     public abstract void display(int positionX, int positionY);
14 
15     // 省略其他方法和属性的实现
16 
17     public int getId() {
18         return id;
19     }
20 
21     public String getType() {
22         return type;
23     }
24 
25     public ChessPieceColor getColor() {
26         return color;
27     }
28 }
29 
30 // 具体享元类:具体棋子单元
31 public class ConcreteChessPieceUnit extends ChessPieceUnit {
32     public ConcreteChessPieceUnit(int id, String type, ChessPieceColor color) {
33         super(id, type, color);
34     }
35 
36     @Override
37     public void display(int positionX, int positionY) {
38         System.out.println("棋子:" + getColor() + " " + getType() + ",位置:" + positionX + ", " + positionY);
39     }
40 
41     // 省略特有的方法和属性的实现
42 }
43 
44 // 享元工厂类:棋子单元工厂
45 public class ChessPieceUnitFactory {
46     private static final Map<Integer, ChessPieceUnit> chessPieces = new HashMap<>();
47 
48     static {
49         chessPieces.put(1, new ConcreteChessPieceUnit(1, "将", ChessPieceColor.RED));
50         chessPieces.put(2, new ConcreteChessPieceUnit(2, "兵", ChessPieceColor.BLACK));
51         // 添加其他棋子
52     }
53 
54     public static ChessPieceUnit getChessPiece(int chessPieceId) {
55         return chessPieces.get(chessPieceId);
56     }
57 }
58 
59 // 客户端代码
60 public class Client {
61     public static void main(String[] args) {
62         ChessPieceUnit chessPiece = ChessPieceUnitFactory.getChessPiece(1);
63         chessPiece.display(0, 0);
64 
65         // 其他棋子的使用示例
66         ChessPieceUnit blackPawn = ChessPieceUnitFactory.getChessPiece(2);
67         blackPawn.display(1, 2);
68     }
69 }

    在象棋的例子中,如果有一些棋子具有特殊属性,不适合被共享,就可以设计 UnsharedConcreteFlyweight 类。例如,考虑到有些棋子可能有特殊的移动规则,这些规则不适用于所有相同类型和颜色的棋子,那么就可以将这些规则作为 UnsharedConcreteFlyweight 的一部分。

以下是象棋中的 UnsharedConcreteFlyweight 的示例:

 1 // 不共享的具体享元对象,包含不被共享的特殊属性
 2 public class UnsharedConcreteFlyweight implements ChessPiece {
 3     private String type;  // 棋子类型,内部状态
 4     private String color; // 棋子颜色,内部状态
 5     private String specialMove; // 特殊移动规则,不被共享的外部状态
 6 
 7     public UnsharedConcreteFlyweight(String type, String color, String specialMove) {
 8         this.type = type;
 9         this.color = color;
10         this.specialMove = specialMove;
11     }
12 
13     @Override
14     public void display(int x, int y) {
15         System.out.println("Type: " + type + ", Color: " + color +
16                 ", Position: (" + x + ", " + y + "), Special Move: " + specialMove);
17     }
18 }

在这个例子中,UnsharedConcreteFlyweight 类包含了额外的特殊移动规则,这些规则是不被共享的外部状态。在实际使用中,可以根据具体的业务需求决定是否需要 UnsharedConcreteFlyweight 类。如果所有棋子都具有相同的属性,可以避免使用不共享的具体享元对象。


三、优缺点分析
1.  优点
- 通过共享对象减少了内存中对象的数量,降低了内存的占用,提高了系统的性能。
- 享元模式的外部状态相对独立,不会影响其内部状态,从而使享元对象可以在不同的环境中被共享。

2. 缺点
- 需要分离出内部状态和外部状态,增加了系统的复杂性。
- 为了使对象可以共享,需要将部分状态外部化,而读取外部状态会使运行时间变长。

3.分析
- 享元模式这样理解:“享”就表示共享,“元”表示对象
- 享元模式的目的在于通过共享尽可能多的相似对象来减小内存占用或计算开销。
- 享元模式中,内部状态和外部状态是两个重要的概念,它们用于描述对象的状态,并在共享对象时进行区分
- 内部状态(Intrinsic State)是可以共享的,它独立于具体的享元对象,因此可以被多个对象共享。内部状态存储在享元对象内部,不会随着外部环境的改变而改变。
- 外部状态 (Extrinsic State)是不可共享的,它取决于具体的应用场景,并且随着外部环境的改变而改变。外部状态由客户端管理,而不是由享元对象管理。

四、使用场景
- 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
- 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
- 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

五、应用实例
1. String常量池
2. 数据库连接池

参考链接:https://zhuanlan.zhihu.com/p/521739369

标签:享元,对象,Pattern,color,棋子,FlyWeight,共享,public
From: https://www.cnblogs.com/hld123/p/18001188

相关文章

  • [Typescript] The type Registry pattern (declare module)
    OurprojectmighthaveafilestructurelikeOurprojectmighthaveafilestructurelikedata/book.ts//AmodelforBookrecordsmagazine.ts//AmodelforMagazinerecordslib/registry.ts//Ourtyperegistry,anda`fetchRecord`f......
  • django路由分发1中RegexURLResolver, RegexURLPattern和2中URLResolver, URLPattern
    django路由分发1中RegexURLResolver,RegexURLPattern和2中URLResolver,URLPatternfromdjango.confimportsettingsfromdjango.utils.module_loadingimportimport_stringfromdjango.urlsimportRegexURLResolver,RegexURLPatternfromcollectionsimportOrderedDict......
  • [Design Pattern] Intro: Three groups of patterns
    Creationalpatternsprovideobjectcreationmechanismsthatincreaseflexibilityandreuseofexistingcode.Factorymethod:Providesaninterfaceforcreatingobjectsinasuperclass,butallowsubclasstoalterthetypeofobjectsthatwillbecreated.......
  • 【Leetcode1949. 坚定的友谊】使用MySQL在无向图中寻找{"CompleteTripartite", {1, 1,
    目录题目地址思路代码MySQL代码逐行翻译为Pandas代码等效Cypher查询(未验证)题目地址https://leetcode.cn/problems/strong-friendship/思路就是在无向图中寻找这个pattern:(*Mathematica*)GraphData[{"CompleteTripartite",{1,1,3}}]SQL写还是比较麻烦。更加复杂的查询还是......
  • BZOJ1717 Milk Patterns 产奶的模式 (二分+后缀数组+height数组)
    发现这样起标题更能引流(ylg实锤了)题意给定一个长度为\(n\)的数组\(a\),求在\(a\)中出现了至少\(k\)次的最长子串的长度。解法考虑将一个子串拆成两个后缀,即\([l,r]=[l,n]-[r,n]\),发现一个长度为\(x\)的子串\(t\)在\(i,j\)两个位置出现过当且仅当后缀\(i,j\)有......
  • 享元模式
    把一些常用且不变的对象保存在一个队列里,每次优先从队列中取,没取到则新创建一个,再将它插入队列以供后续使用定义:提供了减少对象数量从而改善应用所需的对象结构的方式,运用共享技术有效的支持大量细粒度的对象类型:结构型适用场景:常常应用于系统底层的开发,以便解决系统的......
  • 【Leetcode1949. 坚定的友谊】使用MySQL在无向图中寻找{"CompleteTripartite", {1, 1,
    目录题目地址思路代码MySQL代码等效Cypher查询(未验证)题目地址https://leetcode.cn/problems/strong-friendship/思路就是在无向图中寻找这个pattern:(*Mathematica*)GraphData[{"CompleteTripartite",{1,1,3}}]SQL写还是比较麻烦。更加复杂的查询还是建议把数据迁......
  • Python实现软件设计模式8:桥接模式 Bridge Pattern
    动机将抽象部分与实现部分分离,使他们都可以独立地变化。用抽象关联取代传统的多层继承。将类之间的静态继承关系转换为动态的对象组合关系。上图展示的软件系统中存在两个独立地变化维度,分别是抽象类(Abstraction)和实现类(Implementor),它俩都是接口、都分别可以独立地扩展出多......
  • Python实现软件设计模式7:适配器模式 Adapter Pattern
    动机有两个不存在直接继承或关联关系的类A、B,A希望能利用到B类中某个已存在的、功能完善的方法,而不再去具体实现A的接口源码;适配器模式使接口不兼容的那些类可以一起工作。主要角色目标类Target抽象接口类适配者Adaptee适配器Adapter具体实现接口客户端C......
  • 程序员必知!享元模式的实战应用与案例分析
    享元模式是一种减少相似对象创建和销毁的设计模式,通过将对象状态分为不变和可变部分,实现内存节省和性能提升。例如,在线游戏中大量玩家角色可共享相同的不变属性,而每人特有的可变属性则单独存储,享元模式使用享元类存储不变属性,非享元类存储可变属性,并通过享元工厂管理对象的复用和......