创建型——单件模式.ppt

上传人:土8路 文档编号:11518379 上传时间:2021-08-12 格式:PPT 页数:35 大小:4.98MB
返回 下载 相关 举报
创建型——单件模式.ppt_第1页
第1页 / 共35页
创建型——单件模式.ppt_第2页
第2页 / 共35页
创建型——单件模式.ppt_第3页
第3页 / 共35页
创建型——单件模式.ppt_第4页
第4页 / 共35页
创建型——单件模式.ppt_第5页
第5页 / 共35页
点击查看更多>>
资源描述

《创建型——单件模式.ppt》由会员分享,可在线阅读,更多相关《创建型——单件模式.ppt(35页珍藏版)》请在三一文库上搜索。

1、创建型单件模式(Singleton),独一无二的对象,用来创建独一无二的,只能有一个实例的对象的入场券 单件模式的类图可以说是所有模式的类图中最简单的,其实上,它的类图上只有一个类 尽管从类设计的视角来说立很简单,但是实现上还是会遇到相当多的波折,有一些对象其实我们只需要一个,例如:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置和注册表(registry)的对象、日志对象,充当打印机、显卡等设备的驱动程序的对象 事实上,这些类对象只能有一个实例,如果制造出多个实例,就会导致许多问题产生,例如:程序的行为异常、资源使用过量,或者是不一致的结果 或许的确有一些类应该只存在

2、一个实例,但这需要花整个章节的篇幅来说明吗?难道不能靠程序员之间的约定或是利用全局变量做到?利用Java的静态变量就可以做到 单件模式是经得起时间考验的方法,可以确保只有一个实例会被创建,单件模式也给了我们一个全局的访问点,和全局变量一样方便,又没有全局变量的缺点,举例来说:如果将对象赋值给一个全局变量,那么你很可能在程序一开始就创建好对象。万一这个对象非常耗费资源,而程序在这次的执行过程中又一直没用到它,不就形成浪费了吗?稍后你会看到利用单件模式,我们可以在需要时才创建对象 利用静态类变量、静态方法和适当的访问修饰符(access modifier),你的确可以做到这一点。但是不管使用哪一种

3、方法,能够了解单件的运作方式仍然是很有趣的事 单件模式听起来简单,要做得对可不简单 要如何保证一个对象只能被实例化一次?答案可不是三言两语就说得完的,小小单件苏格拉底式的诱导问答,如何创建一个对象?,new MyObject();,万一另一个对象想创建MyObject会怎样?可以再次new MyObject吗?,是的,当然可以,所以,一旦有一个类,我们是否都能多次地实例化它?,如果是公开的类,就可以,如果不是的话,会怎样?,如果不是公开类,只有同一个包内的类可以实例化它,但是仍可以实例化它多次,嗯,有意思!你知道可以这么做吗?,我没想过。但是,这是合法的定义,有一定的道理,怎么说呢?,我认为含

4、有私有的构造器的类不能被实例化,小小单件苏格拉底式的诱导问答(续),有可以使用私有的构造器的对象吗?,嗯,我想MyClass内的代码是唯一能调用此构造器的机构。但是这又不太合乎常理,为什么?,因为必须有MyClass类的实例才能调用MyClass构造器,但是因为没有其他类能够实例化MyClass,所以我们得不到这样的实例。这是“鸡生蛋,蛋生鸡”的问题。我可以在MyClass类型的对象上使用MyClass构造器,但是在这之前,必须有一个MyClass实例。在产生MyCalss实例之前,又必须在MyClass实例内才能调用私有的构造器,嘿!我有个想法。你认为这样如何?,MyClass有一个静态方法

5、。我们可以这样调用这个方法:,小小单件苏格拉底式的诱导问答(续),为何调用的时候用MyClass的类名,而不是用对象名?,因为getInstance()是一个静态方法,换句话说,是一个“类”方法。引用一个静态方法,你需要使用类名。,有意思。假如把这些合在一起“是否”就可以初始化一个MyClass?,当然可以。,好了,你能想出第二种实例化对象的方式吗?,嗯,大概可以吧,你能够完成代码使MyClass只有一个实例被产生吗?,剖析经典的单件模式实现,延迟实例化(lazy instantiaze),巧克力工厂,现代化的巧克力工厂具备计算机控制的巧克力锅炉。锅炉做的事就是把巧克力和牛奶融在一起,然后送到

6、下一个阶段,以制造成巧克力棒 这里有4个Choc-O-Holic公司的工业强度巧克力锅炉控制器 看看它的代码(ChocolateBoiler),你会发现代码写得相当小心,他们在努力防止不好的事情发生 例如:排出500加仑的未煮沸的混合物,或者锅炉已经满了还继续放原料,或者锅炉内还没放原料就开始空烧,Choc-O-Holjc 公司在有意识地防止不好的事情发生 你可能会担心,如果同时存在两个ChocolateBoiler (巧克力锅炉)实例,可能将发生很糟糕的事情 万一同时有多于一个的ChocolateBoiler (巧克力锅炉)实例存在,可能发生哪些很糟糕的事呢?,定义单件模式,单件模式确保一个

7、类只有一个实例,并提供一个全局访问点 我们正在把某个类设计成自己管理的一个唯独实例,同时也避免其他类再自行产生实例。要想取得单件实例,通过单件类是唯一的途径 我们也提供对这个实例的全局访问点:当你需要实例时,向类查询,它会返回单个实例。前面的例子利用延迟实例化的方式创建单件,这种做法对资源敏感的对象特别重要,类图,我们遇到麻烦了,看起来巧克力锅炉要让我们失望了,尽管我们利用经典的单件来改进代码,但是ChocolateBoiler的fill()方法竟然允许在加热的过程中继续加入原料 这可是会溢出五百加仑的原料(牛奶和巧克力)呀!怎么会这样!?,线程是个问题,多加线程,就会造成这样吗? 不是只要为

8、ChocolateBolier的单件设置好uniquelnstance变量,所有的getlnstance()调用都会取得相同的实例吗?对不对?,化身为JVM,这里有两个线程都要执行这段代码 你的工作是扮演JVM角色并判断出两个线程是否可能抓住不同的锅炉对象而扰乱这段代码 提示:你只需要检查getInstance()方法内的操作次序和uniqueInstance的值,看它们是否互相重叠 用代码帖来帮你研究这段代码为什么可能产生两个锅炉对象,处理多线程,只要将getInstance()变成同步(synchronized)方法,多线程灾难几乎就可以轻易地解决了,说得很对,的确是有一点不好 而比你所想

9、象的还要严重一些的是:只有第一次执行此方法时,才真正需要同步 换句话说,一旦设置好uniquelnstance变量,就不再需要同步这个方法了 之后每次调用这个方法,同步都是一种累赘,能够改善多线程吗?,为了要符合大多数Java应用程序,很明显地,我们需要确保单件模式能在多线段的状况下正常工作 但是似乎同步getInstance()的做法将拖垮性能,该怎么办呢? 可以有一些选择.,如果getInstance()的性能对应用程序不是很关键,就什么都别做 如果你的应用程序可以接受getlnstance()造成的额外负担,就忘了这件事吧 同步getlnstance()的方法既简单又有效 但是你必须知道

10、,同步一个方法可能造成程序执行效率下降100 倍 因此,如果将getlnstance()的程序使用在频繁运行的地方,你可能就得重新考虑了,使用“急切”创建实例,而不用延迟实例化的做法 如果应用程序总是创建并使用单件实例,或者在创建和运行时的 负担不太繁重,你可能想要急切(eagerly) 创建此单件,如下所示:,利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的单件实例 JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例,双重检查加锁,用“双重检查加锁”,在getlnstance()中减少使用同步 利用双重检查加锁(double-checked lock

11、ing),首先检查是否实例已经创建了,如果尚未创建,“才”进行同步 这样一来,只有第一次会同步,这正是我们想要的,如果性能是你关心的重点,那么这个做法可以帮你大大地减少getlnstance()的时间耗费,再度回到巧克力工厂,在研究如何摆脱多线程的梦魇同时,巧克力锅炉也被清理干净可以再度开工了 首先,得处理多线程的问题 我们有一些选作方案,每个方案部有优缺点,到底该采用哪一个?,一个实用的例子:属性管理器,什么是属性文件 配置信息在属性文件中以属性的方式存放,一个属性就是两个字符串组成的键值对,一个字符串是键(key),另一个是这个键的对应的值(value) 大多数的系统都有一些配置常量,如果

12、把这些常量存储在程序内部的,那么每一次修改它们都需要重新编译程序 将这些常量放在配置文件中,系统通过访问这个配置文件取得配置常量,就可以通过修改配置文件而无需修改程序而达到更改系统配置的目的 系统也可以在配置文件中存储一些工作环境信息,这样在系统重启时,这些信息可以延续到下一个运行周期中 示例Singleton.properties,Java 属性类 Java 提供了一个工具类,称做属性类,可以用来完成Java 属性和属性文件的操作 属性类的继承关系图,属性类提供了读取属性和设置属性的各种方法,其中读取属性的方法有: contains(Object value)、containsKey(Obj

13、ect key) getProperty(String key)、getProperty(String key, String default) list(PrintStream s)、list(PrintWriter w) size(),设置属性的方法有: put(Object key, Object value) remove(Object key),从属性文件加载属性的方法: load(InputStream inStream),将属性存入属性文件的方法: store(OutputStream out, String header),为什么需要使用单例模式 属性是系统的一种“资源”,应当

14、避免有多余一个的对象读取特别是存储属性 属性的读取可能会在很多地方发生,创建属性对象的地方应当在哪里不是很清楚 属性管理器应当自己创建自己的实例,并且自己向系统全程提供这一事例,系统设计 系统的核心是一个属性管理器,也就是一个叫做ConfigManager的类,这个类是一个单件类 这个类应当有一个静态工厂方法,不妨叫做getInstance(),用于提供自己的实例 为简单起见,在这里采取“饿汉”方式实现ConfigManager,ConfigManager的源代码ConfigManager.java Client,要点,单件模式确保程序中一个类最多只有一个实例 单件模式也提供访问这个实例的全局点 在Java 中实现单件模式需要私有的构造器、一个静态方法和一个静态变量 确定在性能和资源上的限制,然后小心地选择适当的方案来实现单件,以解决多线程的问题(我们必须认定所有的程序都是多线程的) 如果不是采用第五版的Java 2,双重检查加锁实现会失效,Any Question ?,Thank you,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 社会民生


经营许可证编号:宁ICP备18001539号-1