文章目录

  • 前言
  • 正文
    • 一、定义
    • 二、情景假设
    • 三、情景分析
    • 四、模式结构及分析
      • (一) 享元模式的两种情形
      • (二) 模式分析
        • (1) 模式特点
        • (2) 模式缺点
    • 五、使用情景
    • 六、延申及拓展
  • 总结

前言

文章内容主要参考了刘伟主编的《设计模式(第2版)》,同时也结合了自己的一些思考和理解,希望能帮到大家。


本篇讲解享元模式。享元模式,这个模式就是将重复使用的对象去设置成“元”,让大家去共“享”。就不必去浪费过多系统资源,导致性能下降。

正文

一、定义

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。

一个软件系统在运行时所创建的相同或相似对象数量太多,将导致运行代价过高,带来系统资源浪费、性能下降等问题,这时候可以利用享元模式,构建享元池,在池子中存放共享的对象,需要的时候可以通过工厂获取,甚至于取出对象时设置各自不同的外部状态,这样对外就会有不同的表现。

二、情景假设

很多网络设备都是支持共享的,如交换机、集线器等,多台终端计算机可以连接同一台网络设备,并通过该网络设备进行数据转发,如图所示,现用享元模式模拟共享网络设备的设计原理。

三、情景分析

关于上面情景的类图(具体分析在下面)

首先定义我们的网络设备比如交换机、集线器,当然我们还要顶一个抽象设备类

//抽象构件类
public abstract class NetworkDevice
{public String getType();public void use();
}

具体已有的两个设备实现类

//Switch交换机
public class Switch extends NetworkDevice{private String type;public Switch(String type){this.type = type;}public String getType(){return this.type;}public void use(){System.out.println("Linked by switch, type is" + this.type);}
}//Hub集线器
public class Hub extends NetworkDevice{private String type;public Hub(String type){this.type = type;}public String getType(){return this.type;}public void use(){System.out.println("Linked by switch, type is" + this.type);}
}

定义一个工厂类,专门用来存储享元类,以及提供使用享元类的接口,其实就是相当于一个容器,作为共享池供大家使用。

//享元工厂类DeviceFactory
public class DeviceFactory {private ArrayList devices = new ArrayList();private int totalTerminal = 0;public DeviceFactory(){NetworkDevice nd1 = new Switch("Cisco-WS-C2950-24");devices.add(nd1);NetworkDevice nd2 = new Hub("TP-LINK-HF8M");devices.add(nd2);}public NetworkDevice getNetworkDevice(String type){if(type.equalsIgnoreCase("cisco")){totalTerminal++;return (NetworkDevice)devices.get(0);} else if(type.equalsIgnoreCase("tp")){totalTerminal++;return (NetworkDevice)devices.get(1);}else{return null;}}public int getTotalDevice(){return devices.size();}  public int getTotalTerminal(){return totalTerminal;}
}

接下来是客户端的代码:

public class Client
{public static void main(String args[]){NetworkDevice nd1,nd2,nd3,nd4,nd5;DeviceFactory df = new DeviceFactory();nd1 = df.getNetworkDevice("cisco");nd1.use();nd2 = df.getNetworkDevice("cisco");nd2.use();nd3 = df.getNetworkDevice("cisco");nd3.use();nd4 = df.getNetworkDevice("tp");nd4.use();nd5 = df.getNetworkDevice("tp");nd5.use();System.out.println("Total Device:" + df.getTotalDevice());System.out.println("Total Terminal:" + df.getTotalTerminal());}
}

可以看到我在客户端中定义五个设备,但其实总共也只使用了2个对象,因为在享元池中只存储了连个设备,并开放对外使用。

四、模式结构及分析

(一) 享元模式的两种情形

享元模式的设计一个关键的地方是先要区分内部状态外部状态

内部状态:存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享(例如:字符的内容)
外部状态:随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的(例如:字符的颜色和大小)

所以我们我们存在享元池的也仅仅是封装具有相同内部状态的的对象,这样才能共享,同时我们可以接口去能够注入外部状态,便可以获得一系列相似的对象

  • 无外部状态情况:
    上面的例子其实就是无状态的,我们从享元池中获取对象直接使用,不会去注入其他外部状态。

  • 有外部状态情况:
    在这里,我把模式情况进行了部分修改。
    虽然网络设备可以共享,但是分配给每一个终端计算机的端口(Port)是不同的,因此多台计算机虽然可以共享同一个网络设备,但必须使用不同的端口。我们可以将端口从网络设备中抽取出来作为外部状态,需要时再进行设置。

    在use方法中我们加入了一个参数Port,单独将端口给封装成一个类进行外部状态的注入
    下面是具体实现:

//端口类Port
public class Port(){private String port;public Port(String port){this.prot = port;}public void setPort(String port){this.port = port;}public String getPort(){return this.port;}
}//抽象构件定义中添加外部状态的注入
//抽象构件类
public abstract class NetworkDevice
{public String getType();public void use(Port port);//use方法添加外部状态注入
}//use方法中外部状态注入
//Switch交换机
public class Switch extends NetworkDevice{private String type;public Switch(String type){this.type = type;}public String getType(){return this.type;}public void use(Port port){//添加外部状态的注入System.out.println("Linked by switch, type is" + this.type + ", port is" + port.getPort());}
}//use方法中外部状态注入
//Hub集线器
public class Hub extends NetworkDevice{private String type;public Hub(String type){this.type = type;}public String getType(){return this.type;}public void use(Port port){//添加外部状态的注入System.out.println("Linked by switch, type is" + this.type + ", port is" + port.getPort());}
}//享元工厂类没变化
//享元工厂类DeviceFactory
public class DeviceFactory {private ArrayList devices = new ArrayList();private int totalTerminal = 0;public DeviceFactory(){NetworkDevice nd1 = new Switch("Cisco-WS-C2950-24");devices.add(nd1);NetworkDevice nd2 = new Hub("TP-LINK-HF8M");devices.add(nd2);}public NetworkDevice getNetworkDevice(String type){if(type.equalsIgnoreCase("cisco")){totalTerminal++;return (NetworkDevice)devices.get(0);} else if(type.equalsIgnoreCase("tp")){totalTerminal++;return (NetworkDevice)devices.get(1);}else{return null;}}public int getTotalDevice(){return devices.size();}  public int getTotalTerminal(){return totalTerminal;}
}//接下来是客户端的代码:
//在use的时候加入指定的端口号。
public class Client
{public static void main(String args[]){NetworkDevice nd1,nd2,nd3,nd4,nd5;DeviceFactory df = new DeviceFactory();nd1 = df.getNetworkDevice("cisco");nd1.use(new Port("1001"));nd2 = df.getNetworkDevice("cisco");nd2.use(new Port("1002"));nd3 = df.getNetworkDevice("cisco");nd3.use(new Port("1003"));nd4 = df.getNetworkDevice("tp");nd4.use(new Port("1004"));nd5 = df.getNetworkDevice("tp");nd5.use(new Port("1005"));System.out.println("Total Device:" + df.getTotalDevice());System.out.println("Total Terminal:" + df.getTotalTerminal());}
}

这就是存在外部状态的情况,把外部状态当单独封装,使用时进行注入。

(二) 模式分析

模式类图:

  • Component: 抽象构件类
  • ConcreteComponent: 具体构件类
  • Decorator: 抽象装饰类
  • ConcreteDecorator: 具体装饰类

(1) 模式特点

  1. 可以减少内存中对象的数量,使得相同或者相似的对象在内存中只保存一份,从而可以节约系统资源,提高系统性能
  2. 外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享

(2) 模式缺点

  1. 使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化
  2. 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长

五、使用情景

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

六、延申及拓展

  • 单纯享元模式和复合享元模式

    • 这里有个没讲到的是,我们在享元池中存储的都是享元的抽象类,实际上存储的内容可能是情况而定,实际中,我们的享元类可以分为可共享的享元类,这些类我们便可以放在享元池中,而还有一部分可能是非共享享元类,创建这类对象我们无法从共享池中获取,只能单独的创建。
    • 单纯享元模式:就是所有的具体享元类都是可共享的。
    • 复合享元模式:将纯享元使用组合模式加以组合,可以形成复合享元对象,这样的复合享元对象本身不能共享,但是他们可以分解成单纯享元对象,而后者则可以共享。这样复合享元类中所包含的每个单纯享元类都具有相同的外部状态,而这些单纯享元的内部状态往往可以不同。
  • 享元模式和其他模式的联用

    • 享元模式的享元工厂类(就是享元池)中通常有一个静态的工厂方法用于返回享元对象,使用简单工厂模式来生成享元对象。
    • 在一个系统中,通常只有唯一一个享元工厂,因此享元工厂类可以使用单例模式进行设计。
    • 如果只有一个具体构件类没有抽象构建类,那么可以将抽象装饰类作为具体构建类子类。
    • 享元模式可以结合组合模式形成复合享元模式,统一对享元对象设置外部状态。

总结

本篇文章主要介绍设计模式的享元模式,以及它的两种情况。

设计模式(十二)—— 享元模式(定义、案例分析、特点、缺点)相关推荐

  1. GoF设计模式(十二) - 享元模式

    前言 享元模式(Flyweight Pattern)运用共享技术有效地支持大量细粒度的对象,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的 ...

  2. 设计模式(十)享元模式Flyweight(结构型)

    设计模式(十)享元模式Flyweight(结构型) 说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释 ...

  3. 设计模式十二之组合模式

    设计模式十二之组合模式 1. 模式的定义与特点 1.1 模式的定义 1.2 模式的特点 1.3 模式的使用场景 2. 模式的结构与实现 2.1 模式的结构 2.2 模式的实现 3. 模式在开源软件中的 ...

  4. 结构型设计模式(五) 之 享元模式是池技术的表现

    1 定义 享元模式(Flyweight Pattern)属于结构型设计模式之一,它主要是使用共享对象有效地避免了创建过多的性质相近的对象,从而降低内存的占用,提高程序的性能.它也是池技术的重要实现方式 ...

  5. C#设计模式之十一享元模式(Flyweight Pattern)【结构型】

    一.引言 今天我们要讲[结构型]设计模式的第六个模式,该模式是[享元模式],英文名称是:Flyweight Pattern.还是老套路,先从名字上来看看."享元"是不是可以这样理解 ...

  6. 【设计模式自习室】享元模式 Flyweight Pattern:减少对象数量

    前言 <设计模式自习室>系列,顾名思义,本系列文章带你温习常见的设计模式.主要内容有: 该模式的介绍,包括: 引子.意图(大白话解释) 类图.时序图(理论规范) 该模式的代码示例:熟悉该模 ...

  7. 【设计模式】11.享元模式

    概述 定义: ​ 运用共享技术来有效地支持大量细粒度对象的复用.它通过共享已经存在的对象来大幅度减少需要创建的对象数量.避免大量相似对象的开销,从而提高系统资源的利用率. 结构 享元(Flyweigh ...

  8. java23设计模式---class10、享元模式(FlyWeight)

    文章目录 一.基本介绍 1.定义 2.优点 3.缺点 4.角色 1)抽象享元类 2)具体享元类 3)享元工厂类 4)组合享元类 5.内部状态和外部状态 二.应用情景 1.线程池 2.String 3. ...

  9. 设计模式之享元模式、例子分析

    1. 定义 享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象 按我的理解来说,享元,就是共享单元,重复运用,这个模式的核心是复用代码,按不同需要,创建并共用一个实例(不同需要是共 ...

  10. Net设计模式实例之享元模式( Flyweight Pattern)

    一.享元模式简介(Brief Introduction) 享元模式(Flyweight Pattern),运用共享技术有效支持大量细粒度的对象. Use sharing to support larg ...

最新文章

  1. H5中的拖拽文件上传-----------------需修改,需测试
  2. DotNET企业架构应用实践-系统架构与性能-缓存技术与ORM中的缓存查询技术
  3. 基于Spring Boot的“课程设计”的设计与实现
  4. php mysql增删查改 主码不能修改_PHP 数据库练习
  5. 7-27 冒泡法排序 (20 分)
  6. 中文论文万能句型_收藏 | SCI 论文写作的万能句型(二)
  7. Android学习笔记---android平台中利用,SAX解析xml
  8. 消息通知系统模型设计
  9. [18/11/22] 将点分十进制的IP地址化成二进制输出
  10. sql和mysql同时配置_jfinal同时配置mssql和mysql混动数据源
  11. 初窥Linux 之 ext2/ext3文件系统
  12. 21天学通JAVA——学习笔记
  13. iPhone4S使用红雪最新iOS5平刷和降级教程
  14. 从PXE启动安装Linux
  15. ZCMU--1585: 面试
  16. 贾玲,这次你是不是可以真的快乐了?
  17. APIO2016滚粗记
  18. 简述人工智能发展的先决条件
  19. mac 麦克风等权限添加应用操作手册
  20. PyQt5实战:你是成熟的软件啦,该有启动画面和加载进度条了!

热门文章

  1. Unity入门案例(约翰的密室逃脱)
  2. java实时数据抽取_试用Web-HarvestJava开源Web数据抽取...
  3. 靠钓鱼7天涨粉200万,他说他只做了一件事 | 新抖专访
  4. int在c语言中能输出负数么,为什么使用模数时C ++输出负数?
  5. 同事离职后如何把原有的GIT变为自己的GIT
  6. PLUTO SDR入门系列之十:两款开源收录音机软件-“gqrx”和“CubicSDR”
  7. c语言数据结构考研试题,数据结构C语言版考研复习题.doc
  8. 三电技术之电池管理技术
  9. 实验五:系统检测维护工具Wsycheck使用
  10. Twitter雪花算法SnowFlake介绍