设计模式 学习笔记

主要参考: HeadFirst设计模式 / 百度 

学习进行中,努力努力!最近一次更新 03.29

目录

1. 策略模式

2.观察者(Observer)模式

3 单例模式

1)饿汉式

2)懒汉式 线程不安全

3)懒汉式 线程安全

4)双检锁 / 双重校验锁

5)登记式 / 静态内部类

6)枚举

使用:

小结:


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 种双检锁方式。

想要个赞嘻

猜你喜欢

转载自blog.csdn.net/afeiererer/article/details/105019460
今日推荐