共计 1817 个字符,预计需要花费 5 分钟才能阅读完成。
单例模式(Singleton)是设计模式里实现起来最简略,应用最广泛的一种。
单例模式确保了一个类在任意时刻只有一个实例存在,同时提供了一个全局拜访点来获取这个实例。在单例模式的应用场合下,确保某个类在整个零碎中只有一个实例十分重要,比方配置管理器、线程池、缓存或者日志对象等。应用单例模式能够防止因为多个实例造成的资源应用抵触,或是数据不统一的问题。
单例模式依照惟一的实例创立工夫点的不同,能够分为 Lazy
和 Eager
两种变体,有的文档里又称为 懒汉式单例模式
和饿汉式单例模式
。
应用 ABAP 编程时,当对象实例在全局拜访点里被申请时,才创立实例的实现形式,称为 Lazy 单例模式。
在 CLASS_CONSTRUCTOR 里创立实例的实现形式,称为 Eager 单例模式。
在 ABAP 里实现 Eager 型单例模式比较简单,新建一个类,将 Instance Generation 设置成 Private
:
而后定义一个 Static 属性 so_instance, 用来保护该类惟一的一个实例:
将类的构造函数设置成 Private:
在该构造函数里,轻易打印一些内容:
method CONSTRUCTOR.
WRITE:/ 'do some initialization work'.
endmethod.
在 CLASS_CONSTRUCTOR 里实现惟一实例的初始化工作。
method CLASS_CONSTRUCTOR.
CREATE OBJECT SO_INSTANCE.
endmethod.
而后在全局拜访点,GET_INSTANCE 办法里,返回之前在 CLASS_CONSTRUCTOR 里创立好的惟一实例。
该单例的消费者,在任意工夫点,都只能通过 GET_INSTANCE 办法,取得惟一的对象实例。在该类的实现体外,希图应用 CREATE OBJECT 额定创立一个新的对象实例,会遇到语法错误:
An instance of the class cannot be created outside the class.
到此为止,都是大家十分相熟的内容。
有没有一种方法,可能在 ZCL_PRIVATE_TEST 类的内部,再失去另一个不同的对象实例呢?如果能够的话,从某种程度上说,ABAP 的单例模式,就曾经被毁坏了。
技术上来说,的确有一种方法,即先把 GET_INSTANCE 返回的实例序列化成 XML 字符串,而后再把这个 XML 字符串,反序列化成对象实例。反序列化后失去的新对象实例,和原始的实例相比,曾经是两个不同的对象实例了
。
当然为了让一个类可能被序列化,在 ABAP 里,须要给它增加一个接口 IF_SERIALIZABLE_OBJECT
. 这个接口不蕴含任何办法,也称为 Tag Interface
,惟一的作用,就是通知 ABAP 框架,这个类须要反对 ABAP 的序列化和反序列化。
ABAP 无奈像 Java 和 TypeScript 这些编程语言一样,从语言层面反对注解 (Annotations) 个性,所以只有用 Tag Interface 来实现同样的目标,即给类提供额定的标注信息,这些规范信息仅供框架解析和应用,不会对应用程序的执行造成任何影响。
看上面这段代码。第三行到第五行,申明了类的三个实例,其中实例 1 和实例 2,都是通过 GET_INSTANCE 返回的,因而这两个实例完全相同,所以第 13 行的打印语句,lo_instance1 = lo_instance2
表达式的布尔值计算结果,为 abap_true
.
并且咱们留神到类的 CONSTRUCTOR 构造函数里的打印语句,只执行了一次,阐明在整个过程中,类的实例化过程,只产生了一次。
再持续往下看代码,第 17 行,实例 1 被序列化成 XML 字符串,存储在变量 s 里。
代码第 19 行,XML 字符串 s 又被反序列化,还原成了实例 3.
此时实例 3 和实例 1 曾经是齐全不同的两个实例了,所以 lo_instance1 = lo_instance3
的表达式布尔值计算结果,为 abap_false
.
这个行为也能在下图的 ABAP 调试器里分明地察看到:在 ABAP 调试器里,实例 1 和实例 2 的 id 雷同,都是 10,而实例 3 的编号为 11.
本文介绍了 ABAP 单例模式中一个不为人熟知的知识点:可序列化 Tag 接口 IF_SERIALIZABLE_OBJECT,算是给 ABAP 单例模式的实现,开了一个口子。然而防止踏入这个坑的措施也很简略,只有一个 ABAP 类没有被赋予 IF_SERIALIZABLE_OBJECT 接口,就不会呈现本文形容的这种单例行为被毁坏的问题。