享元模式浅析

面向对象的思想确实很好地解决了抽象性的问题,以至于在面向对象的眼中,万事万物一切皆对象。不可避免的是,采用面向对象的编程方式,可能会增加一些资源和性能上的开销。不过,在大多数情况下,这种影响还不是太大,所以,它带来的空间和性能上的损耗相对于它的优点而言,基本上不用考虑。但是,在某些特殊情况下,大量细粒度对象的创建、销毁以及存储所造成的资源和性能上的损耗,可能会在系统运行时形成瓶颈。那么我们该如何去避免产生大量的细粒度对象,同时又不影响系统使用面向对象的方式进行操作呢?享元设计模式提供了一个比较好的解决方案。 

享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。图1.1为享元模式的结构图。




 
 

                                          图1.1享元模式结构图

享元模式(Flyweight):运用共享的技术有效地支持大量细粒度的对象。

抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外部状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。

 具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。

 复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称作不可共享的享元对象。这个角色一般很少使用。

 享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。

 客户端(Client)角色:本角色还需要自行存储所有享元对象的外部状态。

内部状态与外部状态:在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,反之随着环境改变而改变的,不可共享的状态称之为外部状态。

公共交换电话网的使用方式就是生活中常见的享元模式的例子。公共交换电话网中的一些资源,例如拨号音发生器、振铃发生器和拨号接收器,都是必须由所有用户共享的,不可能为每一个人都配备一套这样的资源,否则公共交换电话网的资源开销也太大了。当一个用户拿起听筒打电话时,他根本不需要知道到底使用了多少资源,对用户而言所有的事情就是有拨号音,拨打号码,拨通电话就行了。所以,就有很有人会共用一套资源,非常节省,这就是享元模式的基本思想。

假如我们要开发一个类似MS Word的字处理软件,下面分析一下将如何来实现。对于这样一个字处理软件,它需要处理的对象既有单个字符,又有由字符组成的段落以及整篇文档,根据面向对象的设计思想,不管是字符、段落还是文档都应该作为单个的对象去看待。我们暂不考虑段落和文档对象,只考虑单个的字符,于是可以很容易的得到下面的结构图:

Java代码:

//抽象的字符类

public abstract class Charactor{

    //属性

    protected char letter;

    protected int fontsize;

    //显示方法

    public abstract void display();

}

//具体的字符类A

public class CharactorA extends Charactor{

    //构造函数

    public CharactorA(){

        this.letter = 'A';

        this.fontsize = 12;

    }

    //显示方法

    public void display(){

    try{

        System.out.println(this.letter);

    }catch(Exception err){

    }

    }

}

//具体的字符类B

public class CharactorB extends Charactor{

    //构造函数

    public CharactorB(){

        this.letter = 'B';

        this.fontsize = 12;

    }

    //显示方法

    public void display(){

    try{

        System.out.println(this.letter);

    }catch(Exception err){

    }

    }

}

 

我们的这段代码完全符合面向对象的思想,但是却为此搭上了太多的性能损耗,代价很昂贵。

一篇文档的字符数量很可能达到成千上万,甚至更多,那么在内存中就会同时存在大量的Charactor对象,这时候的内存开销可想而知。

我们对内存中的对象稍加分析就能发现,虽然内存中Character实例很多,但是里面有很多实例差不多是相同的,比如CharactorA类的实例就有可能出现过很多次,这些不同的CharactorA的实例之间只有部分状态不同而已。那么,我们是不是可以只创建一份CharactorA的实例,然后让整个系统共享这个实例呢?直接使用显然是行不通的。比如一份文档中使用了很多的字符A,虽然它们的属性letter相同,都是'A',但是它们的fontsize却不相同的,即字符大小并不相同。显然,对于实例中的相同状态是可以共享的,不同的状态就不能共享了。  

为了解决这个问题,我们可以变换一下思路:首先将不可共享的状态从类里面剔除出去,即去掉fontsize这个属性,这时候我们再写一下代码:

Java代码:

//抽象的字符类

public abstract class Charactor{

    //属性

    protected char letter;

    //显示方法

    public abstract void display();

}

//具体的字符类A

public class CharactorA extends Charactor{

    //构造函数

    public CharactorA(){

        this.letter = 'A';

    }

    //显示方法

    public void display(){

    try{

        System.out.println(this.letter);

    }catch(Exception err){

    }

    }

}

//具体的字符类B

public class CharactorB extends Charactor{

    //构造函数

    public CharactorB(){

        this.letter = 'B';

    }

    //显示方法

    public void display(){

    try{

        System.out.println(this.letter);

    }catch(Exception err){

    }

    }

}

经过这次重构,类里面剩余的状态就可以共享了,下面我们要做的工作就是要控制Charactor类的创建过程。如果已经存在了“A”字符这样的实例,就不需要再创建,直接返回实例;如果没有,则创建一个新的实例,这跟单例模式的做法有点类似了。在单例模式中是由类自身维护一个唯一的实例,享元模式则引入一个单独的工厂类CharactorFactory来完成这项工作:

Java代码:

public class CharactorFactory{

    private Hashtable<String,Charactor> charactors = new Hashtable<String,Charactor>();

    //构造函数

    public CharactorFactory(){

        charactors.put("A", new CharactorA());

        charactors.put("B", new CharactorB());

    }

    //获得指定字符实例

    public Charactor getCharactor(String key){

        Charactor charactor = (Charactor)charactors.get(key);

        if (charactor == null){

            if(key.equals("A")){

                charactor = new CharactorA();

            }else if(key.equals("B")){

                charactor = new CharactorB();

            }

            charactors.put(key, charactor);

        }

        return charactor;

    }

}

经过本次重构,已经可以使用同一个实例来存储可共享的状态,下面还需要做的工作就是要处理被剔除出去的那些不可共享的状态。缺少了这些不可共享的状态,Charactor对象就无法正常工作。

那么如何解决对象中不可共享状态的问题呢?

我们把这些不可共享的状态仍然保留在Charactor对象中,把不同的状态通过参数化的方式,由客户程序注入。以下代码是我们最终实现的一个版本:

Java代码:

//抽象的字符类

public abstract class Charactor{

    //属性

    protected char letter;

    protected int fontsize;

    //显示方法

public abstract void display();

//设置字体大小

public abstract void setFontSize(int fontsize);

}

//具体的字符类A

public class CharactorA extends Charactor{

    //构造函数

    public CharactorA(){

        this.letter = 'A';

        this.fontsize = 12;

    }

    //显示方法

    public void display(){

    try{

        System.out.println(this.letter);

    }catch(Exception err){

    }

}

//设置字体大小

public void setFontSize(int fontsize){

    this.fontsize = fontsize;

}

}

//具体的字符类B

public class CharactorB extends Charactor{

    //构造函数

    public CharactorB(){

        this.letter = 'B';

        this.fontsize = 12;

    }

    //显示方法

    public void display(){

    try{

        System.out.println(this.letter);

    }catch(Exception err){

    }

}

//设置字体大小

public void setFontSize(int fontsize){

    this.fontsize = fontsize;

}

}

//客户程序

public class ClinetTest{

public static void main(String[] args){

Charactor a = new CharactorA();

Charactor b = new CharactorB();

//设置字符A的大小

a.setFontSize(12);

        //显示字符B

        a.display();

//设置字符B的大小

b.setFontSize(14);

        //显示字符B

b.display();

    }

}

 

本文通过给出享元模式的典型应用例子,来介绍了享元模式的具体应用,但享元模式在一般的开发中并不常用,而是常常应用于系统底层的开发,以便解决系统的性能问题。

适用性

Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。当以下情况都成立时使用Flyweight模式。

1)一个应用程序使用了大量的对象。

2)完全由于使用大量的对象,造成很大的存储开销。

3)对象的大多数状态都可变为外部状态。

4)如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。

5)应用程序不依赖对象标识。

优缺点

1)享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。

2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

 

享元模式的分析以及实际应用.docx 享元模式的分析以及实际应用 1.前言 4 1.1 课题的研究背景 4 1.2 课题主要研究目标 4 2.相关技术简介 4 2.1Java简介 4 3. 享元模式介绍 5 3.1举例分析 5 3.2基本介绍 5 3.3享元模式的具体角色 5 4. 享元模式之借书案例代码设计 6 4.1定义抽象享元类 6 4.2定义具体享元类 6 4.3创建享元工厂 6 4.4创建学生客户端 7 4.5效果截图 8 5. .享元模式之实际案例应用 8 5.1数据库连接池介绍 8 5.2定义抽象享元类 8 5.3定义具体享元类 9 5.3创建享元工厂 11 5.4连接池配置类 12 5.5多线程访问数据库使用连接池和不使用连接池对比: 13 5.6此案例总结 13 6. 享元设计模式学习总结: 13 立即下载

相关推荐

Vivado项目开发新思路:用VSCode写代码,用Vivado管理IP核与综合(Windows/Mac环境通用指南)

本文介绍了如何结合Vivado和VSCode的优势,打造高效的FPGA设计工作流。通过配置VSCode的Verilog插件和Vivado环境集成,开发者可以在VSCode中编写代码,同时利用Vivado管理IP核与综合,显著提升开发效率和团队协作体验。

weixin_42545503的博客 336

设计模式--享元模式

当客户需要的网站结构相似度很高,而且都不是访问量很高的网站。那么,就可以通过多个网站共用一套代码的方法,快速创建网站,通过不同ip地址区分,可以让维护和扩展更加容易。

qq_43238335的博客 409

信号与系统——卷积、相关运算与FFT关系以及工程应用(三)

卷积运算、相关运算

qq_35028634的博客 6612

一篇文章掌握享元模式

一.案例引入 1.案例描述 2.传统解决方案 3.传统解决方案评价 需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费 解决思路 整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源浪费 即享元模式 二.享元模式 1.基本介绍 2.享元模式原理图 原理图解释 内部状态和外部状态的实例 内部状态和外部状态的定义 3.享元模式解决上述问题的UML类图

Android小白 295

DC/DC开关电源EMC仿真红宝书

本红宝书系统性介绍开关电源EMC仿真方法,包括仿真原理、仿真思路、仿真工具以及仿真效率提升与仿测一致性的实战经验。从理论到仿真,从仿真到应用,真正帮你实现一文掌握开关电源EMC仿真!全文约14000字,适读人群:开关电源设计、电磁兼容EMC相关从业者,电磁相关专业研究生及CST仿真爱好者等。

电磁学社 2498

从FPGA工程师视角看CXP-12:如何用Xilinx GTX/GTH收发器搞定一条50Gbps的工业相机数据流?

本文从FPGA工程师视角详细解析了如何利用Xilinx GTX/GTH收发器实现50Gbps的CXP-12工业相机数据流传输。通过协议分析、硬件设计要点和FPGA逻辑实现,帮助开发者高效构建高带宽工业视觉系统,特别适合4K/8K高分辨率图像处理需求。

weixin_42533622的博客 281

Java类型浅析、自动拆箱、享元模式

在Java中,万物皆对象,但是仍可以分为基本类型和非基本类型。 因为对于int 这些数据对象,并不需要多大的空间,为了提高效率,就将这些int、double、float、bool等基本类型直接按值存放在栈中。而非基本类型的实际值则是存放在堆中。 下面我们结合上面提到的数据类型再讨论一下==和equals的问题。 我们知道Java中都是以引用方式来操作实际对象的,而引用则是存在于栈中。而对于基本类型...

JustKian的博客 227

深度解析机器人关节位置环控制:从理论到实践

本文系统阐述了机器人关节位置环控制的核心原理与实现方法。首先介绍了关节控制的基础框架,阐明位置环在闭环控制中的关键作用。随后深入解析PID控制算法原理,包括比例、积分、微分项的功能与参数整定原则。在软件实现部分,提供了模块化设计思路和Python伪代码示例,强调实时性保障和异常处理机制。文章还包含典型面试问题解答和应用案例,展示如何通过参数优化将定位精度提升20倍。最后指出未来发展趋势将聚焦算法自适应性和系统稳健性。全文兼顾理论深度与实践指导,为开发者提供从基础到进阶的完整技术路线图。

郑伟强dev的专栏 50

设计模式浅析

设计模式是程序的灵魂,不仅仅让程序逻辑性更强,在架构设计上也是不可或缺的,好处多多

heyYouU的博客 232

点云配准SIFT+SI+RANSAC+ICP

Spin Image(SI)是一种用于点云数据描述的特征描述子,广泛应用于三维点云配准、识别和重建等领域。它能够捕捉点云中的局部几何信息,具有旋转不变性和部分平移不变性等优点,是一种非常强大的特征描述子。Spin Image是一种强大的点云特征描述子,具有旋转不变性、局部几何信息捕捉等优点,适用于点云配准、目标识别和三维重建等多个领域。通过合理选择局部区域、建立参考框架和计算描述子,可以充分利用Spin Image描述点云的特征,从而实现各种三维场景分析和处理任务。

点云兔子的博客 558

【立创EDA-PCB设计基础】1.嘉立创EDA专业版软件安装+PCB基本概念+原理图绘制

本文详解PCB基本概念以及实现MCU最小系统原理图的绘制(原理图包括MCU芯片GD32F103C8T6、外部晶振、输出端口、USB输入口、5v转3v3稳压输出、复位按键、唤醒按键、LED)及嘉立创EDA专业版软件安装。为本专栏【立创EDA-PCB设计基础】后面章节实现PCB绘制做准备。

嵌入式阿齐Archie 3万+

图解 | 不得错过的Binder浅析(一)

Framework和Binder的内容挺深的,本文还是站在应用层开发者的角度来建立基本认知,能在遇到问题的时候有思路和方向即可。(本文将带着关键问题和核心流程展开,不会面面俱到) 大纲: 背景 为什么要多进程 为什么要Binder Binder简单架构 简单示例 源码分析 客户端与驱动交互 服务端与驱动交互 总结 细节补充 Binder为什么高效 Binder为什么不用shm 提问 参考资料 本文约4.0k字,阅读大约17分钟。 Android源码基于8.0。 背景 为什么要多进程

哈利迪 765

Open3D点云数据处理(二):读写点云,你真的会吗?

本文详细介绍了open3d加载点云、保存点云的方法,同时打印点云坐标、颜色、法向量等字段信息。

孙悟空 2604

浅析23种设计模式

设计模式 文章目录设计模式创建型模式单例模式原型模式构建者模式工厂方法模式抽象工厂模式结构型模型组合模式适配器模式桥接模式装饰模式代理模式享元模式外观模式行为型模式策略模式访问者模式状态模式责任链模式观察者模式模板方法模式备忘录模式命令模式迭代器模式调停者模式解释器模式 日期:2021年2月6日; 作者:木子六日; 细说一下23种设计模式; 按照四人帮的分法可以分为以下3类:创建型模式、结构型模式、行为型模式; 创建型模式 这类设计模式提供了我们创建对象的不同方法。 单例模式 有些对象我们只希望存在一个

Paul_1i的博客 264

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

浅析23种软件设计模式

<br />浅析23种软件设计模式<br /> 1、工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。 <br /><br /> 2、建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 <br /><

LV_Q的专栏 836

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Glide系列(一) — Glide 框架结构浅析

Glide 是一个图片加载库,跟它同类型的库还有 Picasso、Fresco、Universal-Image-Loader 等。 Glide 框架的优点如下: 1. 加载类型多样化:Glide 支持 Gif、WebP 等格式的图片。 2. 生命周期的绑定:图片请求与页面生命周期绑定,避免内存泄漏。 3. 高效的缓存策略: 1. 支持多种缓存策略 (Memory 和 Disk 图片缓存)。 2. 根据 ImageView 的大小来加载相应大小的图片尺寸。 3. 内存开销小。

Elson的博客 2万+

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

[ java ] java中的==浅析!

看下面代码: public static void main(final String[] args) { Integer a = new Integer(100); Integer b = 100; System.out.println(a == b); } 这段代码的输出是什么?相信很多人都会很容易的猜到:false,因为a、b两个对象的地址不同,

杨有梦的博客 546

java浅析设计模式

简单浅析java设计模式

qq_48645407的博客 618

浅析Typescript设计模式

设计模式就是软件开发过程中形成的套路,就如同你在玩lol中的“正方形打野”,“四一分推”,又或者篮球运动中的“二夹一”,“高位单打”一样,属于经验的总结。熟悉设计模式有什...

lentoo的博客 316

java设计模式浅析

java设计模式理解、设计模式、源码分析、场景分析

高格调的博客 257

浅析设计模式

1.设计思想 目的:易于扩充,易于维护 1.OOP 面向对象 类: 模拟现实 接口:隔离变化 抽象类:包容变与不变 继承,封装,多态 2.OO设计原则: a.找出变化,分开变化和不变的 b.面向编程 3.设计模式: a,策略模式、面向接口 b.工厂模式 ...

dervish0927的博客 246

浅析Java中的23种设计模式

前言 设计模式不论是在我们学习编程,还是在工作和面试过程中,都会涉及到的一个问题,所以了解和学习好设计模式,是我们每一位码农必须要具备的技能,对以后的发展和自己技能的提升都有好处. 什么是设计模式(Design Patterns)? 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。 为什么使用设计模式? ...

weixin_34082177的博客 71

浅析职责链模式(JavaScript示例)

职责链模式(Chain of Responsibility Pattern)为请求创建了一个接受者对象的链,使得多个接受者对象都有机会处理该请求,从而将请求的发送者与接受者之间解耦。请求沿着该职责链传递,直到遇到能处理它的对象为止,其过程类似于递归调用。在设计模式的定义中,职责链模式属于行为型模式。 这里简单做个扩展,设计模式根据使用目的的不同,被分为三大类:创建型模式、结构型模式以及行为型模式。创建型模式主要用于创建对象,是对对象实例化过程的一种抽象;结构型模式处理类和对象的组合,从而得到相对更复杂的类;

Key Board的博客 440
上一篇: 利用Strut2控制标签对SSH网站积分功能进行实现
Mr菜
博客等级 码龄15年 0粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值