设计模式(三)单实例模式

引言

  对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。单实例模式的UML图如下所示。

  

  一个类只能产生一个对象,该怎么实现呢?我们知道,可以通过类定义对象创建实例,或者通过new关键字产生对象;这个该怎么控制?

  创建对象时,都会根据输入的参数调用相应的构造函数,如果我们把构造函数设置为private私有访问权限不就不可以创建对象了吗?

  还有个问题:那我总要创建一个对象啊,单实例啊,不是无实例!咋办?有办法!可以定义一个公有的函数GetInstance()用于返回对象,这样就能创建辣!

  这又出现问题了,那这样做和将构造函数公有化有什么区别?你还是可以通过GetInstance()函数来创建多个对象...

  咋办?能不能只创建一次,设置个标志位?再次创建的时候判断一下?那得保证创建的这个变量的生存期不随对象的创建和销毁而变化,有了! 用static关键字声明吧。

  因为GetInstance()的返回类型为对应的类,所以需要定义一个含有该类的静态私有对象。

  举例说明一下

  皇帝一般来说,一个时期只存在一个,下面用大臣参拜皇帝的例子来说明单实例模式。CEmperor代表皇帝类,大臣在主函数中参拜皇帝。

 1 /*
 2 Time:2016-9-25 18:26:39
 3 Author:CodingMengmeng
 4 Description:Singleton.
 5 */
 6 #include <iostream>
 7 #include <tchar.h>
 8 using namespace std;
 9
10 class CEmperor{
11 private:
12     CEmperor();//注意:构造函数私有
13     virtual ~CEmperor();
14     static CEmperor* instance;//唯一实例
15
16 public:
17     static CEmperor* GetInstance();//工厂方法(用来获得实例)
18     static void emperorSay(void);//类中其它方法,尽量是static
19
20 };
21
22 CEmperor::CEmperor()
23 {
24     //世俗和道德约束你,目的就是不希望产生第二个皇帝
25     cout << "CEmperor Constructor" << endl;
26 }
27
28 CEmperor::~CEmperor()
29 {
30     if (instance != NULL)
31         delete instance;
32 }
33
34 //静态成员变量必须在类外初始化
35 CEmperor* CEmperor::instance = NULL;
36
37 CEmperor* CEmperor::GetInstance()
38 {
39     if (instance == NULL)    //如果未创建过,则new一个出来,如果new过了,则直接返回
40         instance = new CEmperor();
41     return instance;
42 }
43
44 void CEmperor::emperorSay()
45 {
46     cout << "我就是皇帝某某某...." << endl;
47 }
48
49 class CMinister{
50
51 };
52
53 int _tmain(int argc, _TCHAR* argv[])
54 {
55     for (int day = 0; day < 3; day++)
56     {
57         CEmperor* emperor = CEmperor::GetInstance();//当GetInstance()为static时,才能保证实例由类本身来创建,否则一个非static成员函数必须与特定对象搭配才能调用
58         emperor->emperorSay();
59     }
60
61     //三天见的皇帝都是同一个人。
62     return 0;
63 }

 

  运行结果:

  

  

  分析:

  当第一次调用GetInstance()时,instance为NULL,所以会执行instance=new Emperor();把这个新建的实例保存到静态成员instance,并返回这个指针。

  第二次到第N次调用GetInstance()时,由于instance不为空,所以会直接返回instance。也就是第一次调用GetInstance创建的那个实例,这样就实现了单实例。

要点

  显然单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例

  从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数二是类定义中含有一个该类的静态私有对象三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。上面的例子中,

static CEmperor* instance;

  即表示要点二:类定义中含有一个该类的静态私有对象;

static CEmperor* GetInstance();

  即表示要点三:该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。

单实例的应用

1、单实例模式的优点

  • 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁的被创建、销毁,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显;
  • 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
  • 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
  • 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

2、单实例模式的缺点

  • 单例模式没有接口,扩展很困难,若要扩展,除了修改代码没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何的意义,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。
  • 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
  • 单例模式与单一职责原则有冲突。一个类应该只实现一个的逻辑,而不关心它是否是单例的,决定它是不是要单例是环境决定的,单例模式把“要单例”和业务逻辑融合也在一个类中。

3、单实例模式的使用场景

  在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反应”时,则可以采用单例模式,具体的场景如下:

  • 要求生成唯一序列号的环境;
  • 在整个项目中需要有访问一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
  • 创建一个对象需要消耗的资源过多,如要访问IO、访问数据库等资源;
  • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式);

4、单实例模式的注意事项

  在高并发情况下,请注意单例模式的线程同步问题。

时间: 2024-10-14 18:10:21

设计模式(三)单实例模式的相关文章

关于python的单实例模式

单实例模式一直是常用的设计模式,对于python的单实例模式,其实其本身就有实现 http://stackoverflow.com/questions/31875/is-there-a-simple-elegant-way-to-define-singletons-in-python/31887#31887 里面说到module,module只会初始化一次,天然的singleton.这是最为python的解决方案.将你所需要的属性和方法,直接暴露在模块中变成模块的全局变量和方法即可. 另外,如果

设计模式三—抽象工厂模式

设计模式三-抽象工厂模式 一.定义 抽象工厂模式是工厂方法模式的进一步抽象.如果产品簇中只有一种产品,则退化为工厂方法模式. 二.原理图 三.代码实例 * 苹果和土豆是园丁1的杰作 * 葡萄和西红柿是园丁2的杰作 1.Fruit.java public interface Fruit { /* * 生长 * 收获 * 栽种 */ public void grow(); public void harvest(); public void plant(); } 2.Apple.java publi

python的单实例模式

先看下单实例的定义 这里我们主要学习一下基于模块实现单例对象,这里利用的原理就是python的模块导入的特性,这个模块被第一次导入,会被执行一次,但是如果这个模块被再次导入,无论是在相同的文件还是在不同的文件中,第二次导入都不会再次执行 如果要想通过模块导入实现单实例模式,则必须要在一个文件中定义一个类,这里要切记,在这个文件中一定要实例化这个类,然后在其他文件中导入这个实例对象,那么所有的文件中用的实例对象都是相同的一个 比如我们看下下面的例子 在mysingle.py文件中我们定义了一个类,

单实例模式和互斥访问

场景说明:在实际的应用开发中,很多人没有注意到一点:在生成单实例的过程中,如果由线程去创建对象的实例,有可能在第一次检测到对象不存在的情况下,准备创建对象,此时由于多线程的缘故,恰巧当前线程被挂起,另一个线程同样执行到这个语句,于是创建一个对象,另一个线程冲睡眠中被唤醒,于是执行了创建对象,现在就有两个对象,完全背离了单实例的设计模式, 解决方法: 1)在主线程还没有创建多线程的时候,创建单实例,但是这里有一个问题:就不会达到延时加载的效果,变成了急剧加载,也就是说预先加载了对象,可能这个对象根

javascript设计模式-singleton(单例)模式

singleton(单例)模式被熟知的原因是因为它限制了类的实例化次数只能一次,单例模式,在该实例不存在的勤快下,可以通过一个方法创建一个类来实现创建类的新实例:如果实例已经存在,则会简单返回该对象的引用.单例模式不同于静态类(或对象),因为我们可以推迟它们的初始化,这通常是因为它需要一些信息,而这些信息在初始化期间可能无法获取,对于没有察觉到之前的引用代码,它们不会提供方便检索方法,这是因为它既不是对象,也不是由一个single返回的类,而是一个结构,在js中,singleton充当共享资源命

单实例模式

  前言:   单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中(jvm)一个类只有一个实例.即一个类只有一个对象实例:这几种模式有几种好处: 1.可以避免实例存在多个引起程序的逻辑错误(比如一个国家有多个主席,肯定会一团糟) 2.某些创建频繁的类,使用单列模式可以减轻内存的压力 3.因为类控制了实例化过程,所以类可以灵活更改实例化过程.   单列模式的三种模式: 1.饿汉模式 //饿汉式--典型的穷屌丝,吃了上顿没下顿.所以要提前准备(

Java经典23结构模型的设计模式(三)------附加代理模式、适配器型号、Facade模式的差异

本文介绍了7样的结构模型中的其余2种:轻量级.代理模式. 一.享元模式FlyWeight 享元模式比較简单且重要,在非常多场合都被用到.仅仅只是封装起来了用户看不到.其概念:运用共享内存技术最大限度的支持大量细粒度的对象. 这个概念给的有些抽象,说白了就是假设内存中存在某个对象A.假设再次须要使用对象A的时候假设内存中有A这个对象就直接使用它,不要再次new了.假设没有,则又一次new一个.基于这个特点,享元模式使用时通常会给待訪问对象传递一个Tag,用来标识这个对象,并且要同一时候使用抽象工厂

线程安全的单实例模式

我们都很清楚一个简单的单例模式该怎样去实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例.单例大约有两种实现方法:懒汉与饿汉.懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化,节省内存.饿汉:饿了肯定要饥不择食.所以在单例类定义的时候就进行实例化.浪费内存特点与选择:由于要进行线程同步,所以在访问量比

设计模式—三种工厂模式(JAVA)

一:简单工厂: 有一个实际工厂,这个工厂只能造一类的产品,这一类产品就是一个产品接口,会有多个具体产品实现这个接口,例 如,一个手机厂,生产苹果手机,三星手机: 缺点:在工厂类中集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中:对系统的维护和扩展不利: 使用场景:负责创建的对象比较少:客户只知道传入工厂类的参数,对于如何创建对象的逻辑不关心:容易违反高内聚责任分配原则,所以只在很简单的情况下应用: package com.designPattern.simp