学会放下包袱,热爱单例
企业应用程序与移动应用程序有着截然不同的要求。你启动一次企业应用程序,它会连续运行数月或数年。另一方面,大部分手机应用可能是被正在无聊排队或者坐公交车的用户启动的,它们经常连续运行不超过几分钟,这就意味着移动应用程序必须即时开启,而启动一个企业应用程序则需要足够长的时间。
对于企业应用,依赖注入和早期验证是非常重要的, Spring为此提供了极大的便利。 但是别欺骗自己,Spring是好,但它不是万金油。尤其在崇尚快速启动、低内存消耗、避免接口的移动开发领域。
企业应用程序的瓶颈几乎都在数据库,在这里或者其它的地方多花费几毫秒并无大碍。而在性能弱得多的移动设备上, 这些时钟周期不仅算在用户等待的时间上,并且会损耗设备的电量。一个简单的事实,使用接口来替换抽象父类,都要慢上1倍。甚至在嵌套调用的构造函数中多传入个参数,稍微多几层的调用,都会产生影响。
选择性延迟加载单例
Java中的类加载是懒惰式的,Java虚拟机只有在类被引用的时候才会将其加载。用户短时间内运行的移动应用一般不会触发加载其内部的所有类。如果用户只是检查未读信息后退出,则写信息相关的类是不必加载的。早期依赖注入打破了这种做法,所有的类在启动时引入。大部分组件都将会被徒劳的初始化,因为其实它们从未被真正的使用。
所以我们在开发移动应用的做法应该效仿Java虚拟机,而不是像Spring那样。要尽可能只在需要的时候创建某个组件,最好的实现方式就是使用单例模式,但不是那种通常的严格的单例模式,而是一个可选的单例。使用公用的构造函数,信任你的用户,并用getSharedFoo()来命名你的获取器(getter),而不是使用getInstance()。我使用URL缓存组件的例子给你示范下。
01
|
public class URLCache {
|
02
|
03
|
private static URLCache sharedCache;
|
04
|
05
|
public static URLCache getSharedURLCache() {
|
06
|
07
|
synchronized (URLCache.class) {
|
08
|
09
|
if (sharedCache == null) {
|
10
|
11
|
sharedCache = new URLCache();
|
12
|
13
|
}
|
14
|
15
|
}
|
16
|
17
|
return sharedCache;
|
18
|
19
|
}
|
20
|
21
|
public URLCache() {
|
22
|
23
|
// More code...
|
24
|
25
|
}
|
26
|
27
|
// Allot more code here...
|
28
|
29
|
}
|
在我们想象中的HTTP提供者(HTTPProvider)中使用这个共享的URL缓存(URLCache)组件,将会是超级简单的, 但不是强制的:
01
|
public class HTTPProvider {
|
02
|
03
|
public InputStream inputStreamForURL(Stringurl) {
|
04
|
05
|
URLCache cache =URLCache.getSharedURLCache();
|
06
|
07
|
// Use the cache...
|
08
|
09
|
}
|
10
|
11
|
}
|
这里最大的好处就是,如果这个应用程序的代码路径从未执行到尝试打开一个输入流,那么URLCache的cache对象就永远不必创建。用来读取缓存索引、验证、等等的几百毫秒时间被节省了。
但是测试呢?
对于单元测试和mocking组件,单例不是不好吗? 他们以前是不好,如今我们有PowerMock,你真的应该用它。如果我们只是稍微改了下单例模式,而且可以外部设置共享的组件,那就连PowerMock实际上也不需要了:
01
|
public class URLCache {
|
02
|
03
|
private static URLCache sharedCache;
|
04
|
05
|
public static void setSharedURLCache(URLCachecache) {
|
06
|
07
|
synchronized (URLCache.class) {
|
08
|
09
|
sharedCache = cache;
|
10
|
11
|
}
|
12
|
13
|
}
|
14
|
15
|
publicstatic URLCache getSharedURLCache() {
|
16
|
17
|
synchronized (URLCache.class) {
|
18
|
19
|
if (sharedCache == null) {
|
20
|
21
|
sharedCache = new URLCache();
|
22
|
23
|
}
|
24
|
25
|
}
|
26
|
27
|
return sharedCache;
|
28
|
29
|
}
|
30
|
31
|
|
32
|
33
|
// Allot more code here...
|
上述添加的那小段代码让我们可以在装入单元测试类时设置自己的mockcache对象。简单示例如下:
01
|
public class SomeTest extendsTestCase {
|
02
|
03
|
public void setUp() {
|
04
|
05
|
URLCache.setSharedURLCache(newNoOpURLCache());
|
06
|
07
|
}
|
08
|
09
|
public void testRemoteResource() {
|
10
|
11
|
assertNotNull(HTTPProvider.getSharedHTTPProvider().inputStreamForURL(TEST_URL));
|
12
|
13
|
}
|
14
|
15
|
}
|
如果应用程序有特别的需求, 我们还可以显式的重载一个单例。这些需求也许是低带宽下的移动终端数据连接的一个更先进的缓存方式, 或是在问题多多的JavaME平台上的一个特殊实现。
要点
- 不要惧怕单例,它是个非常好的延迟创建的方式,可以大大改善移动应用程序的启动时间和内存消耗。
- 避免使用接口,它们比类至少要慢1倍,而且接口不可以有用于获取延迟创建的组件的静态方法。
- 不要强制使用单例模式,为了测试和特殊情况的易于实现,永远使用可选单例。
- 转载自 并发编程网 - ifeve.com
学会放下包袱,热爱单例相关推荐
- 单例设计模式-序列化破坏单例模式原理解析及解决方案
越来越成熟了,那是不是坚不可摧的呢,现在我们就要用序列号和反序列化来破坏单例模式,后面也会重点讲一下原理,好好听,让我们来一起破坏单例模式吧,首先还是来到Test类里边 package com.lea ...
- 身边的设计模式(一):单例 与 RedisCacheManager
大家好,以后我会用23篇文章,来给大家讲解设计模式,当然如果你看过我的项目,很多设计模式已经很会了,只是没有注意到,我这里会讲解一下,大家就会发现,如果你看懂了我的项目,其实已经至少学会了六种设计模式 ...
- java servlet是单例吗_关于java:为什么apache servlet是单例?
本问题已经有最佳答案,请猛点这里访问. HttpServletRequest request; HttpServletResponse response; public void doGet(Http ...
- 设计模式是什么鬼(单例)
转自:设计模式是什么鬼(单例) 之前我们讲过面向对象以及封装.继承.多态三大特性,底子打好了那我们就把设计模式一个个拆开来看看到底都是神些什么鬼,我们先从简单的单例说起吧.单例,顾名思义,整个系统其实 ...
- 【设计模式】-创造篇-单例
单例定义 单例模式(Singleton)是一种非常简单且容易理解的设计模式.顾名思义,单例即单一的实例,确切地讲就是指在某个系统中只存在一个实例,同时提供集中.统一的访问接口,以使系统行为保持协调一致 ...
- JAVA设计模式什么鬼(单例)——作者:凸凹里歐
之前我们讲过面向对象以及封装.继承.多态三大特性,底子打好了那我们就把设计模式一个个拆开来看看到底都是神些什么鬼,我们先从简单的单例说起吧.单例,顾名思义,整个系统其实就只有一个实例存在,不能再多,否 ...
- 2021年大数据常用语言Scala(二十九):scala面向对象 单例对象
目录 单例对象 定义object - 掌握 伴生对象 - 掌握 apply方法 - 掌握 main方法 单例对象 Scala中没有static关键字,但是它支持静态 如果要定义静态的东西,统统定义到o ...
- Android/Java 单例使用总结
学而时习之,温故而知新. 什么是单例? 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例 单例模式特点 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3. ...
- python基础知识整理 第七节:单例设计模式、异常、模块、包、制作模块、文件
1.单例设计模式 单例设计模式就是为对象在内存中分配空间的时候,永远只会返回一个唯一的固定的内存空间.这样就能保证在内存中这个类的对象只有唯一的一份,这个就叫做单例.(为对象分配空间使用的是内置方法_ ...
最新文章
- Laravel 7发行说明
- 实验三 编程、编译、连接、跟踪
- 用自然语言从GitHub搜代码,跳过论坛提问环节,来自Facebook新研究
- 5G NGC — NEF PFD Management Service
- python django 表单_Django-表单处理
- NOMURA Programming Contest 2021(AtCoder Regular Contest 121)
- jq金钱如何加千分位_拼多多如何玩转场景推广
- arcgis制作空间变化图怎么做_听说如果做数据分析不用GIS,会被开?
- gtx780有html接口吗,笔记本玩转游戏大作 达人外接GTX780Ti
- 支付宝借呗利息万3和万2.5的,都是些什么大神级的人物?
- java 比较2个时间大小写_date - Java 8:计算两个LocalDateTime之间的差异
- 计算机网上邻居怎么隐藏,Win7桌面不显示网上邻居图标方法 win7系统如何隐藏网上邻居图标...
- macos复制粘贴快捷键 快速_苹果电脑复制粘贴快捷键是什么 如何操作【图文】...
- 云服务器下行_阿里云ECS服务器下行带宽和上行带宽详解及选择
- Real-Time Rendering 4th Edition 实时渲染第四版 第五章 着色基础(Shading Basics)
- 【Golang】对接百度翻译API-golang版本sdk代码
- Ubuntu 测网速
- 拼多多2021校招2020.9.1笔试题 T2 and T4
- 马丁福勒《UML精粹》读书笔记_第四章
- Axure 9 实战案例,基本元件的应用 5,利用情形实现B站图文登录验证
热门文章
- 二十二:制作app的时候超出部分不能滑动
- 《看透SpringMVC》第十二章 HandlerMapping
- 200多位专家热议“智慧城市” 建议尽快完善标准体系
- Maven实战(三)Eclipse构建Maven项目
- 借助acs来实现telnet、ssh的远程认证
- 【转载】Real6410 Linux 常见问题总结(截至2010/07/26)
- Populating Next Right Pointers in Each Node II
- python中*的用法
- java线程入门篇(一)
- springboot 配置多线程