单例模式:保证类只有一个实例,并提供一个全局访问点。

      前面讨论过类的原型模式,这说明使用类的一大优势是类是可以创建多个实例,而且彼此之间是相互隔离的,互相之间并不影响。今天讨论的是相反的情况,即类的单一实例问题。

      EXE文件、VI都存在单一实例的问题,比如 我们在操作一个硬件时,一般不允许多个执行文件同时操作,这就要求该EXE 文件不能启动两次,VI 也是如此。默认的VI 设置是不可重入的,不可重入VI本身就保证了该VI 必须运行完毕后,下一次调用才能执行,所以常规VI是单一实例的。可重入VI恰恰相反,可以同时运行VI的多个实例,类也是如此,个别情况下,只允许单个实例,这就是所说的单例模式。

      单例模式有两个重要的特点:

一、只能创建唯一的一个实例。
二、必须提供一个基于全局的访问方法,在任何场合都可以随意调用这个实例。


      我们自然会想到能否用LV内置的全局变量解决这个问题,如果把类的实例存储在全局变量中,这解决了全局访问点的问题,任何场合都可以调用这个实例。但是, 这种方法有两个重大的缺陷,一是全局变量的竞争问题,更为重要的他无法满足只能创建唯一一个实例的要求。通过全局变量,并不能防止其他地方创建这个类的实 例。

      常规的面向对象编程语言是这样解决的。

Picture
Picture

      在类的定义中, 定义一个本身类的引用,该引用为静态变量,VB.NET 称作共享型变量,在所有类的实例中共享。LVOOP 中,我们在类中也可以定义这样的共享变量。在LV的类中,如果定义一个全局变量作为类的数据成员,这实际上限定了这个全局变量的作用范围仅限于类中。

      构造函数为私有函数,这意味着无法在外部直接创建类的实例。提供了一个静态(共享)方法,通过它返回唯一的实例。如果尚未创建实例,则创建之,如果已经创建,则返回这个类的实例。

      类的引用(指针)作为类的数据成员,这是一般面向对象编程语言都支持的,事实上即使面向过程的C语言中,结构类型虽然不允许把结构本身所为成员,但是支持结构指针作为结构的成员,由此导致了链表等重要的数据结构。

      在类本身检查是否是唯一实例无疑是最为合适的,因为这非常有利于数据封装,不需要外部参与,但是LVOOP是值传递的,是无法在类中定义类本身的引用作为数据成员的,因此无法直接在类中实现单例模式。
     
       LV 的例程中,提供了一个单例模式的例子,下面我们分析一下它实现的过程。


Picture
      如上所述,我们无法在类本身实现单例模式,所以只能通过再次封装这个类实现。在Singleton.lvlib库中建立一个一个类,它的库属性为私有。注意上图中,如果通过快捷菜单设置为私有访问范围,则显示一个暗红色的钥匙,表示这是库中的私有数据。这样就不保证了外部无法直接访问这个类,自然也就无法 直接建立类的实例了, 这实际上对外隐藏了类的存在。

      由于单例模式要求任意时刻只能有唯一一个类的实例存在,并且全局都可以访问,我们自然应该想到用LV2 函数变量存储这个类的实例,这样就可以保证访问是全局的,而且实例是唯一的,因为无法在外部访问类,所以自然不会有其它地方能够实例化这个类。

      Get Queue.VI就是一个典型的LV2 型全局变量,程序框图如下图所示。

Picture

      在首次调用Get Queue.vi或者队列为空的时候,建立一个类的实例。使用之包含一个元素的队列,是LV 常用的传引用的方法,而不是直接传值,这个方法我在之前的文章中提及过。如果不是首次调用,则直接返回队列引用。

      我们注意到该LV2全局变量也设置为库私有的,所以库的外部是不允许直接使用的。上面我们解决了单例模式中的唯一实例化问题,下面要解决的是如何全局访问的问题,当然如果该VI设置为公共,是可以直接进行全局访问的。

      该例子中建立两个公有VI,分别是Check In Vi 和Check Out VI,作为外部访问接口。
Picture

      在Check Out VI 中取出类的单一实例,由于存储类的应用的对列为单一元素,因此多个Check Out 同时操作时,同一时刻只能有一个能够获取类的实例,这样就保证了可以多处访问,访问是全局的,但是唯一实例是共享的。

      取出类实例后,进行相应操作后,完成时必须重新把类实例再次写入队列,这就是Check In.vi实现的功能,写入队列后,由于队列中已经存在一个元素,因此其他Check Out 可以获取类的实例。程序框图如下所示:

Picture

      由于是LV 本身提供的例子,这里就不过多地描述了,上面主要是解释一下单例模式实现的思想。

Picture



Leave a Reply.