设计模式-单例模式学习笔记及心得感悟

单例模式

单例模式的定义

    确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。
    通用代码如下
 
   
//饿汉单例模式
public class Singleton{
private static final Singleton singleton = new Singleton();
//限制产生多个对象
private Singleton(){}
//通过该方法获得实例对象
public static Singleton getSingleton(){
return single;
}
//类中其他的方法,尽量是static
}

单例模式的优点

    1.在内存中只有一个实例,减少了内存的开支,特别是一个对象需要频繁的创建、销毁时,而且创建或者销毁时性能又无法优化,此时其优势就很明显。
    2.减少了系统性能开销。当一个对象的产生需要消耗较多的资源时,可以通过在启动应用时直接产生一个单例对象,用永久驻留内存的方式来解决。
    3.避免对资源的多重占用,避免对一个资源同时进行多种冲突的操作。
    4.单例模式可以在系统设置全局的访问点,优化和共享资源访问。例如cocos2dx中的导演类CCDirector,网上查到的这个类的职能是:
         它负责管理初始化OpenGL渲染窗口以及游戏场景的流程控制,它是cocos2dx游戏开发中必不可少的类之一。为什么要把此类设计成单例对象呢?因为,一个游戏只需要有一个游戏窗口就够了,所以,只需要初始化一次OpenGL渲染窗口。而且场景的流程控制功能,也只需要存在一个这样的场景控制对象即可。为了保证CCDirector类只存在一个实例对象,就必须使用单例模式。

单例模式的缺点

    1.单例模式一般没有接口,扩展很困难,若要扩展,基本上就是修改代码了。为嘛单例模式不能增加接口,因为接口对单例模式没有任何意义,它要求“自行实例化”,并且提供单一实例。接口或者抽象是不可能被实例化的(就是说设计接口或者抽象类不要套用单例模式,单例模式的‘自行实例化’和他俩的属性是冲突的)。在特殊情况下,单例模式可以实现接口、被继承等,需要在实际情况判断。
    2.单例模式对于测试不利,单例未完成之前是不能进行测试的,没有接口也不能用mock方式虚拟一个对象。
     mock测试就是在测试过程中,对于某些不容易构造或者 不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。 http://baike.baidu.com/view/2445748.htm?fr=aladdin
     3.单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是单例取决于环境,单例模式把“要单例”和业务逻辑融合到一个类中。

单例模式适用于

    1.要求生成唯一标识符的环境。
    2.整个项目中需要一个共享访问点或者共享数据,例如一个web页面上的计数器,可以不用每次刷新都记录到数据库中,适用单例模式保持计数器的值,并确保是线程安全的。
    3.创建一个对象需要消耗的资源过多。
    4.需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式,也可以直接声明为static方式。
         下面是静态类和单例模式的讨论,有一个2004年csdn论坛上的老帖子 http://bbs.csdn.net/topics/50351608
              我实际中最常用的的静态类的莫过于工具类了,和不变类的定义类似,类中没有变量都是常量,只提供一些方法,不会因为状态而有所改变。而且一旦静态方法结束,其中的局部变量被jvm垃圾回收
              单例类含有状态,一般是需要共享的资源,譬如系统的配置信息等。把不变类设计成单例类就是单例模式最常见的滥用。

注意事项

    1.在高并发的情况下,需要注意单例模式的线程同步问题
 
    
//线程不安全的单例
public class Singleton{
private static Singleton singleton = null;
//限制产生多个对象
private Singleton(){}
//通过该方式获得实例对象
public Singleton getSingleton(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
/*低并发不会出现问题,一旦系统压力过大,并发量增加则可能在系统内存中出现多个实例。
线程A在执行到 singleton = new Singleton(),还没有获得新对象,此时线程B执行
到了if判断,singleton为null,进入了if语句块,两个线程出现了两个实例。破坏了最初目的*/
     改进,加上synchronized,这个改进型又叫做懒汉式单例,代码如下,但不是最优秀的。推荐使用饿汉单例模式
 
   
//懒汉单例模式
public class Singleton{
private static Singleton singleton = null;
//限制产生多个对象
private Singleton(){}
//通过该方式获得实例对象
public synchronized Singleton getSingleton(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
     饿汉单例的测试
 
    
//file Singleton.java
package parctice;
 
public class Singleton {
private static final Singleton singleton = new Singleton();
private static int i = 1;
private Singleton(){
this.i++;
}
public static Singleton getInstance(){
return singleton;
}
public static int getI(){
return i;
}
}
 
//file ThreadTest.java
package parctice;
 
public class ThreadTest {
public static void main(String[] args){
Thread t1 = new Thread(new MyRunnable1());
Thread t2 = new Thread(new MyRunnable2());
t1.start();
t2.start();
}
}
class MyRunnable1 implements Runnable{
 
@Override
public void run() {
// TODO Auto-generated method stub
Singleton s = Singleton.getInstance();
System.out.println(" MyRunnable1 -- " + s.getI());
}
}
class MyRunnable2 implements Runnable{
 
@Override
public void run() {
// TODO Auto-generated method stub
Singleton s = Singleton.getInstance();
System.out.println(" MyRunnable2 -- " + s.getI());
}
}
结果截图


 复习一下static静态变量、静态方法
内存总体一共分为了
4个部分(stack segment、heap segment、code segment、data segment)
当我们在程序中,申明一个局部变量的时候,此变量就存放在了 stack segment(栈)当中;
当new 一个对象的时候,此对象放在了heap segment(堆)当中;
而static 的变量或者字符串常量 则存在在 data segment(数据区)中;
那么类中方法的话,是存在在 code segment(代码区)中了。
     在类Singleton 被加载的时候,static类型的singleton变量就被存在了data segment中,而不是随着类Singleton实例化出现在heap segment中。在其他代码中被调用时,调用的总是 data segment的被存储的唯一变量singleton,因此对象被保证为单例。

单例模式的扩展

 
    
//有上限的多例模式
public class Singleton{
private static final Singleton singleton = new Singleton();
//实例的最大数量
private static int maxNumOfSingleton = 5;
//一个数组容纳多个实例
private static ArrayList<Singleton> sinArr = new ArrayList<Singleton>();
//静态代码块来实例化
static{
for(int i=0;i<maxNumOfSingleton ;i++){
sinArr.add(new Singleton());
}
}
//限制产生多个对象
private Singleton(){}
//通过该方法获得实例对象
public static Singleton getSingleton(int index){
return sinArr.get(index);
}
}


你可能感兴趣的:(设计模式)