haohao

Android 设计模式之单例模式

Markdown

时刻尝试走出舒适区

最近在培养自己的广泛阅读习惯,感觉阅读中最重要的是动脑思考,阅读是接受讯息,思考是消化讯息。
本文是 Android 设计模式的开篇,接下来会陆续推出整个常用的设计模式系列。

设计模式

设计模式简而言之就是优秀的代码设计经验的总结。设计模式的产生是为了提高代码的可复用性,可扩展性以及可靠性,但是往往会降低可读性。

设计模式原则

  • 单一职责原则: 一个类只负责一项职责;
  • 里氏替换原则: 子类可以扩展父类的功能,但不能改变父类原有的功能;
  • 依赖倒置原则: 面向接口编程,将依赖对象的共同特征抽象成接口;
  • 接口隔离原则: 一个类不应该实现它不需要的接口方法,即建立单一接口,细化接口,尽量减少接口中的方法;
  • 迪米特法则: 一个对象应该对其他对象保持最少的了解,即降低类与类之间的耦合;
  • 开闭原则: 一个类对扩展开放,对修改关闭。

设计模式分类

创建型设计模式

设计对象的实例化,避免用户用 new 运算符创建对象。例如单例模式,Builder 模式,原型模式等。

行为型设计模式

设计对象之间的通信交互。如观察者模式,迭代器模式,责任链模式等。

结构型设计模式

设计如何组合类和对象以形成更大的结构。如适配器模式,代理模式,桥接模式等。

单例模式

简单介绍常用的五种单例模式的写法。

饿汉式单例

1
2
3
4
5
6
7
8
9
10
11
12
public class Person {
private static final Person INSTANCE = new Person();
private Person(){
}
public static Person getInstance() {
return INSTANCE;
}
}

优点:线程安全。
缺点:类加载的时候就进行初始化,对于初始化逻辑复杂的类,会导致类加载变慢。

(DCL) 懒汉式单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Person {
private static volatile Person sInstance = null;
private Person(){
}
public static Person getInstance() {
if (sInstance == null){
synchronized (Person.class) {
if (sInstance == null) {
sInstance = new Person();
}
}
}
return sInstance;
}
}

优点:volatile 声明单例引用,可以防止并发时初始化成员变量和对象实例化顺序可能会被打乱,双重检查锁定解决了多余的同步问题。
缺点:JVM 创建一个对象会执行 3 个原子操作,在高并发时仍存在极小的概率导致 DCL 失效。

静态内部类单例

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Person {
private Person(){
}
private static class Singleton {
private static final Person INSTANCE = new Person();
}
public static Person getInstance() {
return Singleton.INSTANCE;
}
}

优点:充分结合了懒汉式单例与饿汉式的优点,同时有效避开了它们的缺点,充分保证线程安全。推荐
缺点:会有的!

枚举型单例

1
2
3
4
5
6
7
8
9
10
11
public enum PersonEnum {
INSTANCE;
private PersonEnum() {
}
public void fun() {
// do something
}
}

使用方法:

1
PersonEnum.INSTANCE.fun();

优点:简洁,线程安全。
《Effective Java》的作者在书中是这样夸枚举型单例:

这种写法在功能上与共有域方法相近,但是它更简洁,无偿地提供了序列化机制,绝对防止对此实例化,即使是在面对复杂的序列化或者反射攻击的时候。
虽然这中方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。

缺点:Google 官方强烈建议尽量不要使用 enum ,它会增加额外的内存占用,同时增加可执行文件 .dex 的大小,也不适用继承场景。

容器型单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Person {
public Person () {
}
}
public class SingletonManager {
public static final KEY_SINGLETON = "singleton";
private static Map<String, Object> sInstanceMap = new HashMap<>();
public static void registerSingleton(String key, Object obj) {
if (!sInstanceMap.containsKey(key)) {
sInstanceMap.put(key, obj);
}
}
public static Person getSingleton(String key) {
return (Person) sInstanceMap.get(key);
}
}

使用:

1
2
SingletonManager.registerSingleton(KEY_SINGLETON, new Person);
Person person = SingletonManager.getSingleton();

优点:可以将不同单例注册到一个容器中统一管理,隐藏了类的具体实现,降低了耦合度。推荐
缺点:会有的!

单例模式的优点与缺点

优点:减少内存开销,避免资源重用。
缺点:单例类的扩展性很差,持有外部引用时,非常容易造成内存泄漏。



联系我

Wechat ID

公众号

生活不止于眼前的苟且, 还有诗和远方的田野