减小内存的占用问题——享元模式和单例模式的对比分析

俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!总结的知识点如下:

  • 享元模式概念和实现例子
  • 使用了享元模式的Java API String类
  • java.lang.Integer 的 valueOf(int)方法源码分析
  • 使用享元模式的条件
  • 享元模式和单例模式的区别

  前面的策略模式的话题提起了:如何解决策略类膨胀的问题,说到 “有时候可以通过把依赖于环境Context类的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。”换言之,可以使用享元模式来减少对象的数量,享元模式它的英文名字叫Flyweigh模式,又有人翻译为羽量级模式,它是构造型模式之一,它通过与其他类似对象共享数据来减小内存占用,也正应了它的名字:享-分享。

  那么到底是什么意思呢?有什么用呢?下面看个例子:我们有一个文档,里面写了很多英文,大家知道英文字母有26个,大小写一起一共是52个:

  那么我保存这个文件的时候,所有的单词都占据了一份内存,每个字母都是一个对象,如果文档里的字母有重复的,怎么办?难道每次都要创建新的字母对象去保存么?答案是否定的,其实每个字母只需要创建一次,然后把他们保存起来,当再次使用的时候直接在已经创建好的字母里取就ok了,这就是享元模式的一个思想的体现。说到这儿,其实想起了Java的String类,这个类就是应用了享元模式。稍后再说,先看享元模式的类图和具体实现例子。

  抽象享元角色(接口或者抽象类):所有具体享元类的父类,规定一些需要实现的公共接口。

  具体享元角色:抽象享元角色的具体实现类,并实现了抽象享元角色规定的方法。

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

 1 public interface ICharacter {
 2     /**
 3      * 享元模式的抽象享元角色,所有具体享元类的父类,规定一些需要实现的公共接口。其实没有这个接口也可以的。
 4      *
 5      * 显式我自己的字母
 6      */
 7     void displayCharacter();
 8 }
 9
10 public class ChracterBuilder implements ICharacter {
11     private char aChar;
12
13     public ChracterBuilder(char c) {
14         this.aChar = c;
15     }
16     /**
17      * 具体的享元模式角色
18      */
19     @Override
20     public void displayCharacter() {
21         System.out.println(aChar);
22     }
23 }
24
25 public class FlyWeightFactory {
26     /**
27      * 注意:享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最常见、最直观的就是内存的损耗。
28      * 我们这里使用数组也行,或者 HashMap
29      */
30     private Map<Character, ICharacter> characterPool;
31
32     public FlyWeightFactory() {
33         this.characterPool = new HashMap<>();
34     }
35
36     public ICharacter getICharater(Character character) {
37         // 先去pool里判断
38         ICharacter iCharacter = this.characterPool.get(character);
39
40         if (iCharacter == null) {
41             // 如果池子里没有就new一个新的,并加到pool里
42             iCharacter = new ChracterBuilder(character);
43             this.characterPool.put(character, iCharacter);
44         }
45
46         // 否则直接从pool里取出
47         return iCharacter;
48     }
49 }

下面使用客户端调用,先看看普通的方法

public class MainFlyWeight {
    public static void main(String[] args) {
        //===================================
        // 不用享元模式,我们每次创建相同内容的字母的时候,都要new一个新的对象
        ICharacter iCharacter = new ChracterBuilder('a');
        ICharacter iCharacter1 = new ChracterBuilder('b');
        ICharacter iCharacter2 = new ChracterBuilder('b');
        ICharacter iCharacter3 = new ChracterBuilder('b');
        ICharacter iCharacter4 = new ChracterBuilder('b');

        iCharacter.displayCharacter();
        iCharacter1.displayCharacter();
        iCharacter2.displayCharacter();
        iCharacter3.displayCharacter();
        iCharacter4.displayCharacter();

        // 再通过实验判断
        if (iCharacter2 == iCharacter1) {
            System.out.print("true");
        } else {
            // 打印了 false,说明是两个不同的对象
            System.out.print("false");
        }
}

下面使用享元模式,必须指出的是,使用享元模式,那么客户端绝对不可以直接将具体享元类实例化,而必须通过一个工厂得到享元对象。

public class MainFlyWeight {
    public static void main(String[] args) {
        // 使用享元模式
        // 必须指出的是,客户端不可以直接将具体享元类实例化,而必须通过一个工厂
        FlyWeightFactory flyWeightFactory = new FlyWeightFactory();

        ICharacter iCharacter = flyWeightFactory.getICharater('a');
        ICharacter iCharacter1 = flyWeightFactory.getICharater('b');
        ICharacter iCharacter2 = flyWeightFactory.getICharater('b');

        iCharacter.displayCharacter();
        iCharacter1.displayCharacter();
        iCharacter2.displayCharacter();

        if (iCharacter1 == iCharacter2) {
            // 确实打印了
            System.out.print("============");
        }
        // 同样打印的都一样,但是对象内存的占据却不一样了,减少了内存的占用
    }
}

  类图如下:

  一般而言,享元工厂对象在整个系统中只有一个,因此也可以使用单例模式,由工厂方法产生所需要的享元对象且设计模式不用拘泥于具体代码, 代码实现可能有n多种方式,n多语言……再看一例子,有老师类,继承Person类,老师类里保存一个数字编号,客户端可以通过它来找到对应的老师。

  1 public class Person {
  2     private String name;
  3
  4     private int age;
  5
  6     private String sex;
  7
  8     /**
  9      * person是享元抽象角色
 10      *
 11      * @param age int
 12      * @param name String
 13      * @param sex String
 14      */
 15     public Person(int age, String name, String sex) {
 16         this.age = age;
 17         this.name = name;
 18         this.sex = sex;
 19     }
 20
 21     public Person() {
 22
 23     }
 24
 25     public int getAge() {
 26         return age;
 27     }
 28
 29     public void setAge(int age) {
 30         this.age = age;
 31     }
 32
 33     public String getName() {
 34         return name;
 35     }
 36
 37     public void setName(String name) {
 38         this.name = name;
 39     }
 40
 41     public String getSex() {
 42         return sex;
 43     }
 44
 45     public void setSex(String sex) {
 46         this.sex = sex;
 47     }
 48 }
 49
 50 public class Teacher extends Person {
 51     private int number;
 52
 53     /**
 54      * teacher是具体的享元角色
 55      *
 56      * @param number int
 57      * @param age int
 58      * @param name String
 59      * @param sex String
 60      */
 61     public Teacher(int number, int age, String name, String sex) {
 62         super(age, name, sex);
 63         this.number = number;
 64     }
 65
 66     public Teacher() {
 67         super();
 68     }
 69
 70     public int getNumber() {
 71         return number;
 72     }
 73
 74     public void setNumber(int number) {
 75         this.number = number;
 76     }
 77 }
 78
 79 public class TeacherFactory {
 80     private Map<Integer, Teacher> integerTeacherMapPool;
 81
 82     private TeacherFactory() {
 83         this.integerTeacherMapPool = new HashMap<>();
 84     }
 85
 86     public static TeacherFactory getInstance() {
 87         return Holder.instance;
 88     }
 89
 90     public Teacher getTeacher(int num) {
 91         Teacher teacher = integerTeacherMapPool.get(num);
 92
 93         if (teacher == null) {
 94             // TODO 模拟用,不要把teacher写死,每次使用set
 95             teacher = new Teacher();
 96             teacher.setNumber(num);
 97
 98             integerTeacherMapPool.put(num, teacher);
 99         }
100
101         return teacher;
102     }
103
104     /**
105      * 使用静态内部类,静态内部类相当于外部类的static域,它的对象与外部类对象间不存在依赖关系,因此可直接创建。
106      * 因为静态内部类相当于其外部类的成员,所以在第一次被使用的时候才被会装载。且只装载一次。
107      * 而对象内部类的实例,是绑定在外部对象实例中的。
108      * 静态内部类中可以定义静态方法,在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
109      *
110      * 在某些情况中,JVM 含地了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:
111      *1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
112      *2.访问final字段时
113      *3.在创建线程之前创建对象时
114      *4.线程可以看见它将要处理的对象时
115      *
116      * 故,我使用了静态初始化器来实现线程安全的单例类,它由 JVM 来保证线程安全性。
117      * 且这种实现方式,会在类装载的时候(使用这个类的时候)就初始化对象,不管使用者需要不需要,且只实例化一次。
118      *
119      * 故,我在外部类里再创建一个静态内部类,在静态内部类里去创建本类(外部类)的对象,这样只要不使用这个静态内部类,那就不创建对象实例,从而同时实现延迟加载和线程安全。
120      */
121     private static class Holder {
122         private static final TeacherFactory instance = new TeacherFactory();
123     }
124 }

客户端调用

public class MainClass {
    public static void main(String[] args) {
        // 先创建工厂
        TeacherFactory teacherFactory = TeacherFactory.getInstance();
        // 通过工厂得到具体对象
        Teacher teacher = teacherFactory.getTeacher(1000);
        Teacher teacher1 = teacherFactory.getTeacher(1001);
        Teacher teacher2 = teacherFactory.getTeacher(1000);

        System.out.println(teacher.getNumber());
        System.out.println(teacher1.getNumber());
        System.out.println(teacher2.getNumber());

        // 判断是否是相等对象
        if (teacher == teacher2) {
            // 确实打印了,ok
            System.out.print("____________-");
        }
    }
}

类图

  小结,到底系统需要满足什么样的条件才能使用享元模式。对于这个问题,总结出以下几点: 

  • 一个系统中存在着大量的细粒度对象,且这些细粒度对象耗费了大量的内存。 
  • 这些细粒度对象的状态中的大部分都可以外部化
  • 这些细粒度对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。 
  • 软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。

满足以上的这些条件的系统可以使用享元模型。最后,使用享元模式需要维护一个记录了系统已有的所有享元的哈希表,也称之为对象池,而这也需要耗费一定的资源。因此应当在有足够多的享元实例可供共享时才值得使用享元模式。

  好了,现在多了几个新的概念(外部化,内蕴,外蕴……),再次用教科书的理论,分析之前的享元模式的例子和概念:

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

  外蕴状态:随环境的改变而改变不可以共享的对象。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态不可以影响享元对象的内蕴状态,它们是相互独立的。

  享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。回忆之前的例子:最近的Teacher例子,具体享元角色类Teacher类其实就是有一个内蕴状态,在本例中一个int类型的number属性,它的值应当在享元对象被创建时赋予,也就是内蕴状态对象让享元对象自己去保存,且可以被客户端共享所有的内蕴状态在对象创建之后,就不会再改变了。下面看一个具有外蕴状态的享元模式:

public interface BaseDao {
    /**
     * 连接数据源,享元模式的抽象享元角色
     *
     * @param session String 和数据源连接的session,该参数就是外蕴状态
     */
    void connect(String session);
}

  如果一个享元对象有外蕴状态的话,所有的外部状态都必须存储在客户端,在使用享元对象时,再由客户端传入享元对象。这里只有一个外蕴状态,connect()方法的参数就是由外部传入的外蕴状态

public class DaoA implements BaseDao {
    /**
     * 内蕴状态
     */
    private String strConn = null;

    /**
     * 内蕴状态在创建享元对象的时候作为参数传入构造器
     *
     * @param s String
     */
    public DaoA(String s) {
        this.strConn = s;
    }

    /**
     * 外蕴状态 session 作为参数传入抽象方法,可以改变方法的行为,但是对于内蕴状态不做改变,两者独立
     * 外蕴状态(对象)存储在客户端,当客户端使用享元对象的时候才被传入享元对象,而不是开始就有。
     *
     * @param session Session 和数据源连接的session,该参数就是外蕴状态
     */
    @Override
    public void connect(String session) {
        System.out.print("内蕴状态 是" + this.strConn);
        System.out.print("外蕴状态 是" + session);
    }
}

  享元工厂

public enum Factory {
    /**
     * 单例模式的最佳实现是使用枚举类型。只需要编写一个包含单个元素的枚举类型即可
     * 简洁,且无偿提供序列化,并由JVM从根本上提供保障,绝对防止多次实例化,且能够抵御反射和序列化的攻击。
     */
    instance;

    /**
     * 可以有自己的操作
     */
    private Map<String, BaseDao> stringBaseDaoMapPool = new HashMap<>();

    public BaseDao factory(String s) {
        BaseDao baseDao = this.stringBaseDaoMapPool.get(s);

        if (baseDao == null) {
            baseDao = new DaoA(s);
            this.stringBaseDaoMapPool.put(s, baseDao);
        }

        return baseDao;
    }
}

虽然客户端申请了三个享元对象,但是实际创建的享元对象只有两个,这就是共享的含义

public class Client {
    public static void main(String[] args) {
        BaseDao baseDao = Factory.instance.factory("A连接数据源");
        BaseDao baseDao1 = Factory.instance.factory("B连接数据源");
        BaseDao baseDao2 = Factory.instance.factory("A连接数据源");
        baseDao.connect("session1");
        baseDao1.connect("session2");
        baseDao2.connect("session1");

        if (baseDao == baseDao2) {
            // 打印了
            System.out.print("===========");
        }
    }
}

 

  在JDK中有哪些使用享元模式的例子?举例说明。

  说两个,第一个是String类,第二个是java.lang.Integer 的 valueOf(int)方法。针对String,也是老生常谈了,它是final的,字符串常量通常是在编译的时候就确定好的,定义在类的方法区里,也就是说,不同的类,即使用了同样的字符串, 还是属于不同的对象。所以才需要通过引用字符串常量来减少相同的字符串的数量。

        String s1 = "hello";
        String s2 = "he" + "llo";

        if (s1 == s2) {
            System.out.print("====");// 打印了,说明是1,2引用了一个对象hello
        }

使用相同的字符序列而不是使用new关键字创建的两个字符串会创建指向Java字符串常量池中的同一个字符串的指针。字符串常量池是Java节约资源的一种方式。其实就是使用了享元模式的思想。字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。

  java.lang.Integer 的 valueOf(int)方法源码分析(8.0版本)

        Integer a = 1;
        Integer b = 1;

        System.out.print(a == b);// true

再看一例子;

        Integer a = new Integer(1);
        Integer b = new Integer(1);

        System.out.print(a == b);// false

很容易理解,再看

        Integer a = 200;
        Integer b = 200;

        System.out.println(a == b);// false

怎么还是false呢?看看到底发生了什么,反编译上述程序;

public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 19 L0
    SIPUSH 200
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    ASTORE 1
   L1
    LINENUMBER 20 L1
    SIPUSH 200
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    ASTORE 2

我发现每次都是使用了自动装箱

Integer c = Integer.valueOf(200);

再看该方法源码;

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

我发现,当使用Integer的自动装箱的时候,值在low和high之间时,会用缓存保存起来,供多次使用,以节约内存。如果不在这个范围内,则创建一个新的Integer对象。这不就是尼玛享元模式吗!看看范围:-128~+127

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

   

  享元模式的缺陷是什么?

  享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:

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

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

 

  享元模式比起工厂,单例,策略,装饰,观察者等模式,其实不算是常用的设计模式,它主要用在底层的设计上比较多,比如之前提到的String类,Integer的valueOf(int)方法等。好了,享元模式到这里总结的差不多了,记得之前有个老师的例子,对老师的工厂类使用了单例模式创建了工厂对象,后来又有一个BaseDao例子,工厂Factory类使用了枚举作为单例模式的实现,那么这里还要顺便总结一个老生常谈,但是又不见得真的谈对了的设计模式——单例模式,如下之前总结的C++版的; 软件开发常用设计模式—单例模式总结,发现Java实现的单例模式和C++的在线程安全上还是有些区别的。下面主要说下Java版的单例模式的线程安全性。之前的一些私有静态属性(饿汉式),双重检测加锁……不再赘述。

  简单看看,单例模式还得单开文章总结,涉及到了枚举实现和内存模型:单例类只能有一个实例,单例类必须自己创建自己的唯一实例,单例类必须给所有其他对象提供这一实例(提供全局访问点)。

结合Java多线程的内存模型对线程安全的单例模式总结分析

  小结:享元模式和单例模式的异同

  享元是对象级别的, 也就是说在多个使用到这个对象的地方都只需要使用这一个对象即可满足要求, 而单例是类级别的, 就是说这个类必须只能实例化出来一个对象, 可以这么说, 单例是享元的一种特例, 设计模式不用拘泥于具体代码, 代码实现可能有n多种方式, 而单例可以看做是享元的实现方式中的一种, 但是他比享元更加严格的控制了对象的唯一性。

  

辛苦的劳动,转载请注明出处,谢谢……

http://www.cnblogs.com/kubixuesheng/p/5174025.html

时间: 2024-11-02 23:53:37

减小内存的占用问题——享元模式和单例模式的对比分析的相关文章

总结JavaScript设计模式编程中的享元模式使用_基础知识

享元模式不同于一般的设计模式,它主要用来优化程序的性能,它最适合解决大量类似的对象而产生的性能问题.享元模式通过分析应用程序的对象,将其解析为内在数据和外在数据,减少对象的数量,从而提高应用程序的性能. 基本知识 享元模式通过共享大量的细粒度的对象,减少对象的数量,从而减少对象的内存,提高应用程序的性能.其基本思想就是分解现有类似对象的组成,将其展开为可以共享的内在数据和不可共享的外在数据,我们称内在数据的对象为享元对象.通常还需要一个工厂类来维护内在数据. 在JS中,享元模式主要有下面几个角色

跟屌丝大哥学习设计模式--享元模式

四.举例 这里就以去餐馆吃饭为例详细的说明下享元模式的使用方式.去菜馆点菜吃饭的过程大家一定都是轻车熟路了,这里就不赘述.在例子中我使用了一个list来存放外蕴状态和内蕴状态的对应关系,而且提供了查询每个客人点菜情况的方法.内蕴状态在这里代表了菜肴的种类,而外蕴状态就是每盘菜肴的点菜人.  A 让我们先来看看单纯享元模式的实现吧.     先看下抽象享元角色的定义:GoF对享元模式的描述是:运用共享技术有效地支持大量细粒度的对象. Flyweight模式是构造型模式之一,它通过与其他类似对象共享

C++设计模式13----Flyweight享元模式

Flyweight享元模式概述 作用:运用共享技术有效地支持大量细粒度的对象. 内部状态intrinsic和外部状态extrinsic: 1)Flyweight模式中,最重要的是将对象分解成intrinsic和extrinsic两部分. 2)内部状态:在享元对象内部并且不会随环境改变而改变的共享部分,可以称为是享元对象的内部状态 3)外部状态:而随环境改变而改变的,取决于应用环境,或是实时数据,这些不可以共享的东西就是外部状态了. 4)内部状态和外部状态之间的区别: 在Flyweight模式应用

享元模式

1.什么是享元模式? 享元模式(Flyweight Pattern):以共享的方式高效的支持大量的细粒度对象.通过复用内存中已存在的对象,降低系统创建对象实例的性能消耗. 享元的英文是Flyweight,是一个来自体育方面的专业用语,在拳击.摔跤和举重比赛中特指最轻量的级别.把这个单词移植到软件工程中,也是用来表示特别小的对象,即细粒度的对象.至于为什么把Flyweight翻译为"享元",可以理解为共享元对象,也就是共享细粒度对象. 在面向对象中,大量细粒度对象的创建.销毁及存储所造成

php设计模式 FlyWeight (享元模式)_php技巧

享元模式英文称为"Flyweight Pattern",我非常感谢将Flyweight Pattern翻译成享元模式的那位强人,因为这个词将这个模式使用的方式明白得表示了出来:如果翻译成为羽量级模式或者蝇量级模式等等,虽然可以含蓄的表现出使用此模式达到的目的,但是还是没有抓住此模式的关键. 享元模式的定义为:采用一个共享来避免大量拥有相同内容对象的开销.这种开销中最常见.直观的就是内存的损耗.享元模式以共享的方式高效的支持大量的细粒度对象. 在名字和定义中都体现出了共享这一个核心概念,

详解iOS App设计模式开发中对于享元模式的运用_IOS

享元模式的概念 在面向对象软件设计中,利用公共对象不仅能节省资源还能提高性能.共享的对象只能提供某些内在的信息,而不能用来识别对象.专门用于设计可共享对象的一种设计模式叫做享元模式(Flyweight pattern). 实现享元模式需要两个关键组件,通常是可共享的享元对象和保存他们的池.某种中央对象维护这个池,并从它返回适当的实例. 运用共享技术有效地支持大量细粒度的对象. 公共交通(如公共汽车)已有一百多年的历史了.大量去往相同方向的乘客可以分担保有和经营车辆(如公共汽车)的费用.公共汽车有

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

设计模式(十)享元模式Flyweight(结构型) 说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释放.我们只是为了学习而简单做了介绍. 1. 概述 面向对象技术可以很好地解决系统一些灵活性或可扩展性或抽象性的问题,但在很多情况下需要在系统中增加类和对象的个数.当对象数量太多时,将导致运行代价过高,带来性能下降等问题.比如: 例子1:图形应用中的图元等对象.字处理应用中的字符对象等. 2.解决方案

Java设计模式之享元模式学习笔记

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式.享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象. Java中String的实现就是一个典型的享元模式应用,Java中的String存在字符串常量池中,Java会确保一个字符串常量在常量池中只有一个拷贝.数据库连接池也是一个比较电信的享元模式应用,可简单理解为先初始化一定数量的数据库连接,

C++设计模式之享元模式_C 语言

前言 无聊的时候,也去QQ游戏大厅玩五子棋或者象棋:作为程序员,看到一个产品,总要去想想它是怎么设计的,怎么完成的,我想这个是所有程序员都会做的事情吧(强迫症???).有的时候,想完了,还要做一个DEMO出来,才能体现自己的NB,然后还有点小成就感. 在玩五子棋或象棋的时候,我就想过,腾讯那帮伙计是怎么做的呢?五子棋的棋子有黑白两色,难道每次放一个棋子就new一个对象么?象棋有车.马.相.士.帅.炮和兵,是不是每盘棋都要把所有的棋子都new出来呢?如果真的是每一个棋子都new一个,那么再加上那么