定义

享元模式是对象的结构模式,享元模式以共享的方式高效的支持大量的细粒度对象,主要用于减少创建对象的数量,以减少内存占用和提高性能

享元对象能做到共享的关键在于区分了内蕴状态和外蕴状态

内蕴状态是存储在享元对象内部的,并且是不会随环境的改变而有所不同,因此,一个享元对象可以具有内蕴状态并且可以共享

外蕴状态是随着环境的改变而改变的,并且是不可以共享的状态,享元对象的外蕴状态必须是由客户端保存,并且是在享元对象创建之后,在需要使用的时候再传到享元对象内部

外蕴状态不可以影响享元对象的内蕴状态,换句话说,它们是相互独立的

意图

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

主要解决问题

在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建

何时使用

系统中存在大量的对象,并且消耗了大量的内存,这些对象的状态大部分可以外部化,这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替

优缺点

优点:

大大减少对象的创建,降低系统的内存,使效率提高

缺点:

使得系统更加复杂,为了使对象可以共享,需要将一些状态外部化

结构



涉及的角色:

  • 抽象享元(Flyweight)角色:所有具体享元类的超类,为这些类规定需要实现的接口
  • 具体享元(ConcreteFlyweight)角色:实现抽象享元角色规定的接口,如果有内蕴状态的话,必须为内蕴状态提供存储空间,享元对象的内蕴状态必须与所处的环境无关,从而使得享元对象可以在系统中共存
  • 享元(FlyweightFactory)工厂角色:负责创建和管理享元角色,本角色必须保证享元对象可以被系统适当的共享,当客户端调用享元对象的时候,享元工厂会检查系统中是不是已经有了符合要求的享元对象,如果有了就提供这个对象,如果没有享元工厂就应当创建一个合适的享元对象
  • 客户端(Client)角色:本角色需要维护一个对所有享元对象的引用,需要自行存储所有享元对象的外蕴状态

对应源码如下:

public abstract class Flyweight {

    /** 一个示意性的方法,参数state是外蕴状态 */
public abstract void operation(String state);
}
public class ConcreteFlyweight extends Flyweight {

    private int intrinsicState;

    public ConcreteFlyweight(int intrinsicState) {
this.intrinsicState = intrinsicState;
} /**
* 外蕴状态作为参量传入
* 改变方法的行为
* 并不改变对象的内蕴状态
*/
@Override
public void operation(String state) { System.out.println("内蕴状态:" + intrinsicState + ",,外蕴状态:" + state);
}
}
public class FlyweightFactory {

    private Map<String, Flyweight> map = new HashMap();

    private Flyweight flyweight;

    public FlyweightFactory() {

    }

    //内蕴状态作为参量传入
public Flyweight factory(int state) {
if (map.containsKey(state)) {
return map.get(state);
} else {
Flyweight fly = new ConcreteFlyweight(state);
map.put(String.valueOf(state), fly);
return fly;
}
} public void checkFlyweight() {
Flyweight fly;
int i = 0;
Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry e = (Map.Entry) iterator.next();
System.out.println("Item " + (++i) + ":" + e.getKey());
}
}
}
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight = factory.factory(888);
flyweight.operation("第一次");
flyweight = factory.factory(999);
flyweight.operation("第二次");
flyweight = factory.factory(888);
flyweight.operation("第三次");
System.out.println();
factory.checkFlyweight();
}
}

奶茶摊位的例子

在这个奶茶摊位上,有一系列口味的奶茶,客人来了之后拿到奶茶付了钱就走了,奶茶有内蕴状态,也就是它的口味,没有外部环境影响,也就是没有外蕴状态

如果在系统中,对每一杯奶茶都创建一个对象的话,那么就会创建出很多细小的对象出来,如果按照奶茶的口味区分,每一种口味的奶茶都只创建一个对象,然后共享

下面使用代码实现:

抽象享元角色,也就是每一种奶茶口味需要实现的接口:

public abstract class Order {

    /** 将咖啡卖给客人 */
public abstract void sellingCoffee(); /** 返回奶茶的口味 */
public abstract String getFlavor();
}

具体享元角色,奶茶口味实现抽象享元角色提供的接口:

public class Flavor extends Order {

    private String flavor;

    public Flavor(String flavor) {
this.flavor = flavor;
} @Override
public void sellingCoffee() {
System.out.println("将奶茶卖给客人,奶茶口味为:" + this.flavor);
} @Override
public String getFlavor() {
return this.flavor;
}
}

摊主提供奶茶,也就是享元工厂,负责创建和管理享元角色:

public class FlavorFactory {

    private Order[] flavors = new Flavor[10];

    private int ordersMade = 0;
private int totalFlavors = 0; /** 根据所需要的口味提供奶茶 */
public Order getOrder(String flavor) {
if (ordersMade > 0) {
for (int i=0; i<ordersMade; i++) {
if (flavor.equals(flavors[i].getFlavor())) {
return flavors[i];
}
}
}
flavors[ordersMade] = new Flavor(flavor);
totalFlavors++;
return flavors[ordersMade++];
} /** 返回所有的奶茶口味 */
public int getTotalFlavors() {
return totalFlavors;
}
}

客户端:

public class Client {
private static Order[] flavors = new Flavor[10]; private static int ordersMade = 0; private static FlavorFactory factory; private static void taskOrder(String flavor) {
flavors[ordersMade++] = factory.getOrder(flavor);
} public static void main(String[] args) {
factory = new FlavorFactory(); taskOrder("原味");
taskOrder("香蕉");
taskOrder("原味");
taskOrder("草莓");
taskOrder("草莓");
for (int i=0; i<ordersMade; i++) {
flavors[i].sellingCoffee();
}
System.out.println("总共买奶茶人数:" + ordersMade);
System.out.println("总共卖出奶茶口味数:" + factory.getTotalFlavors());
}
}



可以看到,虽然客人买了5杯奶茶,但是奶茶的口味只有三种

奶茶店的例子

奶茶摊位没有提供桌椅给客人,也就是没有外部环境影响,现在奶茶店提供了桌椅,那么桌椅就是外蕴状态

抽象享元角色,也就是每一种奶茶口味需要实现的接口:

public abstract class Order {

    /** 将咖啡卖给客人 */
public abstract void sellingCoffee(Table table); /** 返回奶茶的口味 */
public abstract String getFlavor();
}

具体享元角色,奶茶口味实现抽象享元角色提供的接口:

public class Flavor extends Order {

    private String flavor;

    public Flavor(String flavor) {
this.flavor = flavor;
} @Override
public void sellingCoffee(Table table) {
System.out.println("将奶茶卖给客人,奶茶口味为:" + this.flavor + ",客人所坐桌子号码为:" + table.getNumber());
} @Override
public String getFlavor() {
return this.flavor;
}
}

奶茶店提供奶茶,也就是享元工厂,负责创建和管理享元角色:

public class FlavorFactory {

    private Order[] flavors = new Flavor[10];

    private int ordersMade = 0;
private int totalFlavors = 0; /** 根据所需要的口味提供奶茶 */
public Order getOrder(String flavor) {
if (ordersMade > 0) {
for (int i=0; i<ordersMade; i++) {
if (flavor.equals(flavors[i].getFlavor())) {
return flavors[i];
}
}
}
flavors[ordersMade] = new Flavor(flavor);
totalFlavors++;
return flavors[ordersMade++];
} /** 返回所有的奶茶口味 */
public int getTotalFlavors() {
return totalFlavors;
}
}

环境角色,桌椅:

public class Table {

    /** 桌子号码 */
private int number; public Table(int number) {
this.number = number;
} public int getNumber() {
return number;
} public void setNumber(int number) {
this.number = number;
}
}

客户端:

public class Client {
private static Order[] flavors = new Flavor[10]; private static int ordersMade = 0; private static FlavorFactory factory; private static void taskOrder(String flavor) {
flavors[ordersMade++] = factory.getOrder(flavor);
} public static void main(String[] args) {
factory = new FlavorFactory(); taskOrder("原味");
taskOrder("香蕉");
taskOrder("原味");
taskOrder("草莓");
taskOrder("草莓");
for (int i=0; i<ordersMade; i++) {
flavors[i].sellingCoffee(new Table(i+1));
}
System.out.println("总共买奶茶人数:" + ordersMade);
System.out.println("总共卖出奶茶口味数:" + factory.getTotalFlavors());
}
}

在什么情况下使用享元模式

  • 一个系统中有大量的对象
  • 这些对象消耗大量的内存
  • 这些对象的状态中大部分都可以外部化
  • 这些对象可以按照内蕴状态分为很多组,当把外蕴状态从对象中剔除时,每一个组都可以用一个对象代替
  • 软件系统不依赖这些对象的身份,即这些对象可以是不可分辨的

由奶茶店突发奇想开始了Java设计模式:享元模式的更多相关文章

  1. 【设计模式】Java设计模式 - 享元模式

    Java设计模式 - 享元模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自己 ...

  2. JAVA 设计模式 享元模式

    用途 享元模式 (Flyweight) 运用共享技术有效地支持大量细粒度的对象. 享元模式是一种结构型模式. 结构

  3. Java设计模式-享元模式(Flyweight)

    享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用. FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查 ...

  4. Java设计模式—享元模式

    享元模式:是池技术的重要实现方式. 定义如下: 使用共享对象可有效地支持大量的细粒度的对象. 个人理解:享元模式利用共享对象的技术,解决了Java中内存溢出的问题. 享元模式的定义为我们提出了两个要求 ...

  5. java设计模式---享元模式

    享元模式 顾名思义:共享元对象.如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用创建新的对象. 享元模式是为数不多的.只为提升系统性能而生的设计模式.它的主要作用就 ...

  6. java设计模式——享元模式

    一. 定义与类型 定义:提供了减少对象数量从而改善应用所需的对象结构的方式,运用共享技术有效地支持大量细粒度的对象 类型:结构性 二. 使用场景 (1)  常常应用于系统底层的开发,以便解决系统的性能 ...

  7. Java设计模式15:常用设计模式之享元模式(结构型模式)

    1. Java之享元模式(Flyweight Pattern) (1)概述:       享元模式是对象池的一种实现,英文名为"Flyweight",代表轻量级的意思.享元模式用来 ...

  8. Java设计模式学习记录-享元模式

    前言 享元模式也是一种结构型模式,这篇是介绍结构型模式的最后一篇了(因为代理模式很早之前就已经写过了).享元模式采用一个共享来避免大量拥有相同内容对象的开销.这种开销最常见.最直观的就是内存损耗. 享 ...

  9. Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景

    我做了个例子 ,需要可以下载源码:代理模式 1.前言: Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没 ...

随机推荐

  1. 2.go语言入门----变量类型、声明变量、数组、切片

    基本变量类型 介绍几种基本的变量类型:字符串.int.float.bool package main import ( "fmt" ) // 列举几种非常基本的数据类型 func ...

  2. Java Socket编程基础及深入讲解

    原文链接:https://www.cnblogs.com/yiwangzhibujian/p/7107785.html 原文写的特别好,非常详细,但是禁止转载,那我就不再复制粘贴了! socket实现 ...

  3. Vue学习笔记-chrome84版本浏览器跨域设置

    一  使用环境: windows 7 64位操作系统 二  chrome84版本浏览器跨域设置   报错问题:Indicate whether to send a cookie in a cross- ...

  4. 原生JS快速实现拖放

    原生 JS 快速实现拖放 拖放是很常见的一种交互效果,很多时候我们都会借助于第三方的控件来实现,其实用原生 js 实现起来也非常的方便.接下来我们就用原生 js 和 css 快速实现拖放效果 html ...

  5. 心脏滴血(CVE-2014-0160)检测与防御

    用Nmap检测 nmap -sV --script=ssl-heartbleed [your ip] -p 443 有心脏滴血漏洞的报告: ➜ ~ nmap -sV --script=ssl-hear ...

  6. 使用ASP.NET Blazor Server 写混合桌面程序的疯狂想法

    开发本地桌面程序,使用进程内浏览器+进程内BLAZOR服务器,然后任性写功能,自由分发,放飞自我,大家看怎么样? 求评估,求批评 https://github.com/congzhangzh/desk ...

  7. NodeJs 入门到放弃 — 常用模块及网络爬虫(二)

    码文不易啊,转载请带上本文链接呀,感谢感谢 https://www.cnblogs.com/echoyya/p/14473101.html 目录 码文不易啊,转载请带上本文链接呀,感谢感谢 https ...

  8. PAT-1018(Public Bike Management)最短路+额外条件+所有最短路中找出满足条件的路径+dijkstra算法

    Public Bike Management PAT-1018 使用一个vector来存储所有最短路的前驱结点,再通过使用dfs和一个额外的vector记录每一条路径 #include<iost ...

  9. SQL注入绕过waf的一万种姿势

      绕过waf分类: 白盒绕过: 针对代码审计,有的waf采用代码的方式,编写过滤函数,如下blacklist()函数所示: 1 ........ 2 3 $id=$_GET['id']; 4 5 $ ...

  10. 【odoo14】第十四章、CMS网站开发

    第十四章.CMS网站开发** Odoo有一个功能齐全的内容管理系统(CMS).通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了.在本 ...