声明:转载请说明来源:http://www.cnblogs.com/pony1223/p/7554686.html

一、引出蝇量模式

现在假设有一个项目,这个项目是为公园设计一个景观的部署,那么这个时候就会有一个问题出现,那么就是避免不了的会有一个树的类,树会很多,那么这个时候如果按照传统的方案来合计,我们会这样的设计:

然后,我们会建立很多树的对象,属性的含义分别为:x坐标,y坐标,年轮,显示的样式(比如:小树苗,参天大树等)代码如下:

package study.designmode.flyweight;

public class Tree {
private int xCoord, yCoord, age; public Tree(int xCoord, int yCoord, int age) {
this.xCoord = xCoord;
this.yCoord = yCoord;
this.age = age;
} public void display() {
// System.out.print("x");
}
}

然后现在公园里面,现在需要很多树,那么这个时候,我们模拟如下:

package study.designmode.flyweight;

public class TreesTest {

    private int length = 1000000;
private Tree[] treelst = new Tree[length]; public TreesTest() {
for (int i = 0; i < length; i++) {
treelst[i] = new Tree((int) (Math.random() * length),
(int) (Math.random() * length),
(int) (Math.random() * length) % 5);
}
} public void display() {
for (int i = 0, len = treelst.length; i < len; i++) {
treelst[i].display();
}
} }

现在就new 了一万颗树出来了,那么我测试下带来的内存消耗:

package study.designmode.flyweight;

public class MainTest {

    public static void main(String[] args) {
showMemInfo();
TreesTest mTreesTest;
mTreesTest = new TreesTest(); showMemInfo();
mTreesTest.display();
showMemInfo();
} public static void showMemInfo() {
// 最大内存:
long max = Runtime.getRuntime().maxMemory();
// 分配内存:
long total = Runtime.getRuntime().totalMemory();
// 已分配内存中的剩余空间 :
long free = Runtime.getRuntime().freeMemory();
// 已占用的内存:
long used = total - free; System.out.println("最大内存 = " + max);
System.out.println("已分配内存 = " + total);
System.out.println("已分配内存中的剩余空间 = " + free);
System.out.println("已用内存 = " + used);
System.out.println("时间 = " + System.currentTimeMillis());
System.out.println(""); } }

结果如下:

已分配内存 = 5177344
已分配内存中的剩余空间 = 4917312
已用内存 = 260032
时间 = 1505830341189

最大内存 = 66650112
已分配内存 = 45998080
已分配内存中的剩余空间 = 17842296
已用内存 = 28155784
时间 = 1505830341646

最大内存 = 66650112
已分配内存 = 45998080
已分配内存中的剩余空间 = 17842296
已用内存 = 28155784
时间 = 1505830341652

这样就带来一个比较严重的问题,对内存的消耗,即现在是1百万颗树,就new出来了这么多对象,如果树在多一点,或者说其他比如草,更多,带来的内存消耗就更大;那么如何来解决内存消耗的问题呢?

要想减小内存,必然要减少对象的出现,那就需要分析这些对象是否存在一些变与不变的东西,我们可以发现这些对象都很小,但是有一个共性就数量很大,那么针对对象小如苍蝇一样虽然小,但是量大,还是比较恐怖的,那么就引出了蝇量模式。

二、解决办法

首先,我们分析上述树这个类中,我们发现x坐标,y坐标,age 都是会变化的,而display 是随着x y age进行变化的,那就是说x y age 我们可以看成是一个外部状态是没有办法共享的,但是display 是可以共享,只是随外部状态变化而已,那这个display我们可以当做内部状态来进行处理;这样就可以分为两个对象,一个是持有display的蝇量对象,一个是控制 x y age的外部状态管理的管理对象。

蝇量模式:通过共享的方式高效的支持大量细粒度的对象。

代码实现如下:

1.蝇量对象:

package study.designmode.flyweight.ms;

public class TreeFlyWeight {

    public TreeFlyWeight() {

    }

    public void display(int xCoord, int yCoord, int age) {
// System.out.print("x");
} }

2.管理对象

package study.designmode.flyweight.ms;

public class TreeManager {

    private int length = 1000000;
int[] xArray = new int[length], yArray = new int[length],
AgeArray = new int[length]; private TreeFlyWeight mTreeFlyWeight; public TreeManager() { mTreeFlyWeight = new TreeFlyWeight();
for (int i = 0; i < length; i++) { xArray[i] = (int) (Math.random() * length);
yArray[i] = (int) (Math.random() * length);
AgeArray[i] = (int) (Math.random() * length) % 5; } } public void displayTrees() { for (int i = 0; i < length; i++) {
mTreeFlyWeight.display(xArray[i], yArray[i], AgeArray[i]);
}
} }

3.测试:

package study.designmode.flyweight.ms;

public class MainTest {

    public static void main(String[] args) {

        showMemInfo();

        TreeManager mTreeManager;
mTreeManager = new TreeManager(); showMemInfo();
mTreeManager.displayTrees();
showMemInfo(); } public static void showMemInfo() {
// 已分配内存中的剩余空间 :
long free = Runtime.getRuntime().freeMemory();
// 分配内存:
long total = Runtime.getRuntime().totalMemory();
// 最大内存:
long max = Runtime.getRuntime().maxMemory();
// 已占用的内存: long used = total - free; System.out.println("最大内存 = " + max);
System.out.println("已分配内存 = " + total);
System.out.println("已分配内存中的剩余空间 = " + free);
System.out.println("已用内存 = " + used);
System.out.println("时间 = " + System.currentTimeMillis());
System.out.println("");
} }

结果如下:

最大内存 = 66650112
已分配内存 = 5177344
已分配内存中的剩余空间 = 4917312
已用内存 = 260032
时间 = 1505831079965

最大内存 = 66650112
已分配内存 = 14696448
已分配内存中的剩余空间 = 2527960
已用内存 = 12168488
时间 = 1505831080252

最大内存 = 66650112
已分配内存 = 14696448
已分配内存中的剩余空间 = 2527960
已用内存 = 12168488
时间 = 1505831080261

可以看出上面还是有点差异的,其实如果在更多对象的情况下,效果会更加明显,只是需要更多的内存来演示,否则容易内存溢出。

上面是蝇量模式的一个变异演示,为什么这么说呢?可以看下类图:

可见我们上面是直接一个蝇量对象,加管理对象完成的,如果要显示类上述样式,即要抽象出一个类和工厂来,为什么抽象,说明下:

1.我们采用蝇量模式的目的,就是为了解决对象小但数量多的问题,那么要解决,就要抽出内部状态和外部状态;那么任何一个都是这样来玩;比如上面的树,那么如果比如现在加入了草这个对象,也是一样的进行抽象;只是我在抽取蝇量对象的时候,发现树和草会有共性的出现,于是就抽化到了父类中,于是就出现了上面的抽象类,然后继承实现各个自己的蝇量。

2.既然抽象出了公共类,那么我们知道需要产生蝇量对象,如果对象种类比较多,这个时候我们可以采用工厂模式来做,并用一个集合来存放,已经有的蝇量对象就不在创建,如果没有就创建并放入到集合中使用;然后还有一个对象就是管理对象了client

代码如下:

package study.designmode.flyweight.fly;

public abstract class Plant {

    public Plant() {

    }

    public abstract void display(int xCoord, int yCoord, int age);

}

(1)树

package study.designmode.flyweight.fly;

public class Tree extends Plant {

    @Override
public void display(int xCoord, int yCoord, int age) {
// TODO Auto-generated method stub
// System.out.print("Tree x");
} }

(2)草

package study.designmode.flyweight.fly;

public class Grass extends Plant {

    @Override
public void display(int xCoord, int yCoord, int age) {
// TODO Auto-generated method stub
// System.out.print("Grass x");
} }

(3)工厂   先判断后获取

package study.designmode.flyweight.fly;

import java.util.HashMap;

public class PlantFactory {

    private HashMap<Integer, Plant> plantMap = new HashMap<Integer, Plant>();

    public PlantFactory() {

    }

    public Plant getPlant(int type) {

        if (!plantMap.containsKey(type)) {

            switch (type) {
case 0:
plantMap.put(0, new Tree());
break;
case 1:
plantMap.put(1, new Grass());
break;
}
} return plantMap.get(type);
}
}

(4)管理对象

package study.designmode.flyweight.fly;

public class PlantManager {

    private int length = 10000000;
private int[] xArray = new int[length], yArray = new int[length],
AgeArray = new int[length], typeArray = new int[length]; private PlantFactory mPlantFactory;
public PlantManager() { mPlantFactory=new PlantFactory();
for (int i = 0; i < length; i++) { xArray[i] = (int) (Math.random() * length);
yArray[i] = (int) (Math.random() * length);
AgeArray[i] = (int) (Math.random() * length) % 5;
typeArray[i]= (int) (Math.random() * length) % 2;
}
} public void displayTrees() {
for (int i = 0; i < length; i++) {
mPlantFactory.getPlant(typeArray[i]).display(xArray[i], yArray[i], AgeArray[i]);
}
}
}

(5)测试:

package study.designmode.flyweight.fly;

import java.util.ArrayList;

public class MainTest {

    public static void main(String[] args) {

        showMemInfo();

        PlantManager mPlantManager;
mPlantManager = new PlantManager(); showMemInfo();
mPlantManager.displayTrees();
showMemInfo(); } public static void showMemInfo() {
// 已分配内存中的剩余空间 :
long free = Runtime.getRuntime().freeMemory();
// 分配内存:
long total = Runtime.getRuntime().totalMemory();
// 最大内存:
long max = Runtime.getRuntime().maxMemory();
// 已占用的内存: long used = total - free; System.out.println("最大内存 = " + max);
System.out.println("已分配内存 = " + total);
System.out.println("已分配内存中的剩余空间 = " + free);
System.out.println("已用内存 = " + used);
System.out.println("时间 = " + System.currentTimeMillis());
System.out.println("");
} }

结构图:

三、总结

Flyweight在拳击比赛中指最轻量级,即“蝇量级”或“雨量级”,这里选择使用“享元模式”的意译,是因为这样更能反映模式的用意。享元模式是对象的结构模式。享元模式以共享的方式高效地支持大量的细粒度对象。

Java中的String类型
  在JAVA语言中,String类型就是使用了享元模式。String对象是final类型,对象一旦创建就不可改变。在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝。String a="abc",其中"abc"就是一个字符串常量。

public class Test {
public static void main(String[] args) { String a = "abc";
String b = "abc";
System.out.println(a==b); }
}

  上面的例子中结果为:true ,这就说明a和b两个引用都指向了常量池中的同一个字符串常量"abc"。这样的设计避免了在创建N多相同对象时所产生的不必要的大量的资源消耗。

享元模式的结构
  享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最常见、最直观的就是内存的损耗。享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。
  一个内蕴状态是存储在享元对象内部的,并且是不会随环境的改变而有所不同。因此,一个享元可以具有内蕴状态并可以共享。
  一个外蕴状态是随环境的改变而改变的、不可以共享的。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态不可以影响享元对象的内蕴状态,它们是相互独立的。
  享元模式可以分成单纯享元模式和复合享元模式两种形式。
单纯享元模式  
  在单纯的享元模式中,所有的享元对象都是可以共享的。
  单纯享元模式所涉及到的角色如下:
  ●  抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。
  ●  具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
  ●  享元工厂(FlyweightFactory)角色 :本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。

复合享元模式
  在单纯享元模式中,所有的享元对象都是单纯享元对象,也就是说都是可以直接共享的。还有一种较为复杂的情况,将一些单纯享元使用合成模式加以复合,形成复合享元对象。这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。
  
  复合享元角色所涉及到的角色如下:
  ● 抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。
  ● 具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
  ● 复合享元(ConcreteCompositeFlyweight)角色 :复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称作不可共享的享元对象。
  ●  享元工厂(FlyweightFactory)角色 :本角 色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有 一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个 合适的享元对象。

本质:内部状态和外部状态,解决大量细颗粒对象问题.

JAVA设计模式:蝇量模式的更多相关文章

  1. Head First设计模式——蝇量和解释器模式

    蝇量 蝇量模式:如果让某个类的一个实例能用来提供许多“虚拟实例”,就使用蝇量模式. 在一个设计房子的平台中,周围要加上一些树,树有一个坐标XY坐标位置,而且可以根据树的年龄动态将自己绘制出来.如果我们 ...

  2. 蝇量模式(Flyweight Pattern)

    蝇量模式:让某个类的一个实例能用来提供许多“虚拟实例”. 在有大量对象时,有可能造成内存溢出,把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重复创建.(JAVA中的S ...

  3. Java设计模式之工厂模式(Factory模式)介绍(转载)

    原文见:http://www.jb51.net/article/62068.htm 这篇文章主要介绍了Java设计模式之工厂模式(Factory模式)介绍,本文讲解了为何使用工厂模式.工厂方法.抽象工 ...

  4. java设计模式6——代理模式

    java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代 ...

  5. Java设计模式——装饰者模式

    JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...

  6. 浅析JAVA设计模式之工厂模式(一)

    1 工厂模式简单介绍 工厂模式的定义:简单地说,用来实例化对象,取代new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式能够动态决定将哪一个类实例化.不用先知道每次要实例化哪一个类. 工 ...

  7. JAVA设计模式--装饰器模式

    装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...

  8. 折腾Java设计模式之建造者模式

    博文原址:折腾Java设计模式之建造者模式 建造者模式 Separate the construction of a complex object from its representation, a ...

  9. 折腾Java设计模式之备忘录模式

    原文地址:折腾Java设计模式之备忘录模式 备忘录模式 Without violating encapsulation, capture and externalize an object's int ...

随机推荐

  1. asp.net core MVC 全局过滤器之ExceptionFilter异常过滤器(一)

    本系类将会讲解asp.net core MVC中的内置全局过滤器的使用,将分为以下章节 asp.net core MVC 过滤器之ExceptionFilter异常过滤器(一) asp.net cor ...

  2. Promise与异步

    不知道promise,大家现在用了吗?如果还不了解的话,今天就来对了-基础的了解起来- 正文从这开始- 接触过promise的的都知道它的应用场景和用途,Promise可以用来避免异步操作函数里的嵌套 ...

  3. C# 移动无标题栏窗体的几种方法

    第一种,手工移动. 该方法根据鼠标位置实现窗体的移动.网上有很多相关的例子,这里不再多讲. 第二种,调用系统API原理:是当鼠标左键按下时,让系统认为是在标题栏按下的.这里我们用到了winapi里的W ...

  4. 关于android appcompatv7 Menu items should specify a title的解决办法

    做安卓开发时,添加menu时 是AS报以下错误: 解决办法为修改如下: <menu xmlns:android="http://schemas.android.com/apk/res/ ...

  5. 常用的Linux发行版

    Linux发行版百花齐放 [内容摘要] 如今,众多的Linux发行版百花齐放,linux的阵营日益壮大,每一款发行版都拥有一大批用户,开发者自愿为相关项目投入精力.Linux发行版可谓是形形色色,它们 ...

  6. eclipse导入SVN上的Maven多模块项目

    eclipse导入SVN上的Maven多模块项目 博客分类: Eclipse&MyEclipse SVN Maven   一.SVN上Maven多模块项目结构 使用eclipse导入SVN上的 ...

  7. 【Weblogic】启动命令nohup解析

    nohup ./startWebLogic.sh >out.log 2>&1 & 解析 其中 0.1.2分别代表如下含义: 0 – stdin (standard inpu ...

  8. Spring Web MVC(三)之注解

    [toc] spring web mvc 基于注解的优化 我写的注解是按照spring web的部件分类写的,这样的话比较方便查看,大家感觉有用的话可以分享个别人,希望对对更多的人有帮助.毕竟零基础开 ...

  9. Spark Structured Streaming框架(2)之数据输入源详解

    Spark Structured Streaming目前的2.1.0版本只支持输入源:File.kafka和socket. 1. Socket Socket方式是最简单的数据输入源,如Quick ex ...

  10. Android Studio开发常见问题

    Compilation failed; see the compiler error output for details 错误描述 解决方法 原因:文件编码问题.进入项目根目录,在命令提示符下执行以 ...