JAVA设计模式:蝇量模式

声明:转载请说明来源: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)角色 :本角 色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有 一个符合要求的享元对象。如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个 合适的享元对象。

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

时间: 2024-12-28 00:54:42

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

设计模式之蝇量模式

蝇量模式:让某个类的一个实例能够用来提供多个"虚拟"实例,运用共享技术有效地支持大量细粒度的对象 特点: 减少运行时对象实例的个数 将许多"虚拟"对象的状态一同管理 运用共享技术有效地支持大量细粒度的对象 区分对象的共享变量(内部状态)和不可共享变量(外部状态,将此类变量从类从剔除,由外部传入) 用途: 当一个类需要创建很多个实例,而这些实例可以被同一个方法控制 缺点: 单个逻辑实例将无法拥有独立不同的行为 举例: 你需要建立很多树对象,每个树对象有三个属性:位置坐

蝇量模式——HeadFirst设计模式学习笔记

蝇量模式:让某个类的一个实例能够用来提供多个"虚拟"实例,运用共享技术有效地支持大量细粒度的对象 特点: 减少运行时对象实例的个数 将许多"虚拟"对象的状态一同管理 运用共享技术有效地支持大量细粒度的对象 区分对象的共享变量(内部状态)和不可共享变量(外部状态,将此类变量从类从剔除,由外部传入) 用途: 当一个类需要创建很多个实例,而这些实例可以被同一个方法控制 缺点: 单个逻辑实例将无法拥有独立不同的行为 举例: 你需要建立很多树对象,每个树对象有三个属性:位置坐

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

蝇量 蝇量模式:如果让某个类的一个实例能用来提供许多“虚拟实例”,就使用蝇量模式. 在一个设计房子的平台中,周围要加上一些树,树有一个坐标XY坐标位置,而且可以根据树的年龄动态将自己绘制出来.如果我们创建许多树之后,会有许多树的实例对象.使用一个树实例和一个客户对象来维护“所有”树的状态,这就是蝇量模式. 设计类图: 实现代码: ①创建一个存储树状态的类 1 public class TreeState 2 { 3 public int X { get; set; } 4 public int

蝇量模式(Flyweight Pattern)

蝇量模式:让某个类的一个实例能用来提供许多“虚拟实例”. 在有大量对象时,有可能造成内存溢出,把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重复创建.(JAVA中的String,如果没有则创建一个字符串保存在字符串常量池里,否则直接返回) 类图: public interface Planet { public abstract void display(int x, int y); } public class Tree implements Planet {

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

原文见:http://www.jb51.net/article/62068.htm 这篇文章主要介绍了Java设计模式之工厂模式(Factory模式)介绍,本文讲解了为何使用工厂模式.工厂方法.抽象工厂.Java工厂模式举例等内容,需要的朋友可以参考下 工厂模式定义:提供创建对象的接口. 为何使用工厂模式 工厂模式是我们最常用的模式了,著名的Jive论坛,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见. 为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经

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

java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代理 动态代理 1.3.代理模式关系图(以租房子为例) 2.静态代理 2.1.角色分析: 抽象角色:一般会使用接口或者抽象类来解决 真实角色:被代理的角色 代理客户:代理真实角色.代理真实角色后,我们一般会做一些附属的操作 客户:访问代理对象的人 2.2.例1(租房子演示) 2.2.1.抽象角色实现(

JAVA设计模式之代理模式

学编程吧JAVA设计模式之代理模式发布了,欢迎通过xuebiancheng8.com来访问 一.概述 给某一个对象提供一个代理,并由代理对象来完成对原对象的访问.代理模式是一种对象结构型模式. 二.适用场景 当无法直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口. 三.UML类图 四.参与者 1.接口类:Subject 它声明了真实访问者和代理访问者的共同接口,客户端通常需要针对接口角色进行编程. 2.代理类

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

1 工厂模式简介 工厂模式的定义:简单地说,用来实例化对象,代替new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式可以动态决定将哪一个类实例化,不用先知道每次要实例化哪一个类. 工厂模式可以分一下三种形态: 简单工厂 (Simple Factory)模式:又称静态工厂模式(StaticFactory). 工厂方法 (Factroy Method)模式:又称多态性工厂模式(Polymorphic Factory). 抽象工厂 (Abstract Factroy)模式:又称工具箱模式

浅析JAVA设计模式之工厂模式(二)

1 工厂方法模式简介 工厂方法 (Factroy Method) 模式:又称多态性工厂模式(Polymorphic Factory),在这种模式中,核心工厂不再是一个具体的类,而是一个抽象工厂,提供具体工厂实现的接口,具体创建产品交由子工厂去做,抽象工厂不涉及任何产品被实例化的细节.而不同等级的产品,就对应一个不同等级的工厂,如下图. 图1 1.1工厂方法模式(多态性工厂模式): 工厂方法模式有三个角色: 1. 抽象产品接口 2. 具体产品类 3. 抽象工厂接口 4.具体工厂类. 1.2工厂方法