主要参考: HeadFirst设计模式 / 百度
学习进行中,努力努力!最近一次更新 03.29
目录
1. 策略模式
定义算法族,将他们封装起来,可相互替换,让算法的变化独立于使用算法的客户
BUG:橡皮鸭会飞?
解决:利用覆盖(可能N多个子类都需要修改)
设计原则:找出应用中需要变化之处,把他们独立出来,不和不需要变化的代码混在一起
需要变化:fly(飞行方式),quack(叫声) 不需要变化:swim
设计原则:针对接口编程,不针对实现编程
「有一个」 可能比「是一个」更好:每一鸭子都有一个FlyBehavior且 有一个QuackBehavior,让鸭子将“飞 行和呱呱叫委托它们代为处理。
设计原则:多用组合,少用继承
实现:
2.观察者(Observer)模式
定义了对象之间一对多依赖,当一个对象状态改变时,所有依赖者都会收到通知并自动更新。
关键词:松耦合
【例:气象站】
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
关于观察者的一切,主题只知道观察者实现了某个接口( 也就是Observer接口)。主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。
有新类型的观察者出现时,主题的代码不需要修改。
假如我们有个新的具体类需要当观察者,不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里 实现此观察者接口,然后注册为观察者即可。
设计原则:为了交互对象之间的松耦合设计而努力。
实现
3 单例模式
来源:https://m.runoob.com/design-pattern/singleton-pattern.html
确保一个类只有一个实例,并且自行实例化向整个系统提供这个实例。
优点:
- 在内存里只有一个实例,减少内存开销,尤其是频繁的创建和销毁实例(比如页面缓存)。
- 避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 要求生产唯一序列号。
- WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
1)饿汉式
Lazy初始化?是 线程安全?否
/* 饿汉单例模式 */
public Myclass{
private MyClass(){}
public static Myclass getInstance(){
return new MyClass();
}
}
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。容易产生垃圾对象。
2)懒汉式 线程不安全
Lazy初始化?是 线程安全?否
public class Singleton{
//利用一个静态变量来记录Singleton类的唯一实例
private static Singleton uniqueInstance;
//把构造器声明为私有的,只有Sington类内才可调用构造器
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance==null){
uniqueInstance=new Singleton();
}
return uniqueInstance;
}
}
因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
问题: 该单例模式在低并发的情况下会出现问题,若系统压力增大,并发量增加时则可能在内存中出现多个实例。如一个线程A执行到uniqueInstance = new Singleton(),但还没有获得对象(对象初始化需要时间),第二个线程B也在执行,执行到(singleton== null)判断,那么线程B获得判断条件也是为真,于是线程A线程B都获得了一个对象,在内存中就出现两个对象。
3)懒汉式 线程安全
Lazy初始化?是 线程安全?是
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。99% 情况下不需要同步。
4)双检锁 / 双重校验锁
Lazy初始化?是 线程安全?是
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
采用双锁机制,安全且在多线程情况下能保持高性能。
5)登记式 / 静态内部类
Lazy初始化?是 线程安全?是
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
对比双检锁机制:
功效相同,实现更简单。
双检锁适用于:实例域需要延迟初始化
登记式适用于:对静态域使用延迟初始化
对比线程不安全的懒汉式:
Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。
6)枚举
Lazy初始化?否 线程安全?是
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
简洁,自动支持序列化机制,绝对防止多次实例化。
使用:
public class SingletonPatternDemo {
public static void main(String[] args) {
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();//需要自定义的函数
}
}
小结:
一般情况下,不建议使用懒汉方式,建议使用第饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。
想要个赞嘻