设计模式复习
23种设计模式主要分为三类:创建型模式、结构型模式、行为型模式。
创建型模式
单例模式
实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
代码实现
- 饿汉式 线程安全
class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
- 懒汉式 双向检查锁定
class Singleton {
// volatile 多线程安全, 但屏蔽Java虚拟机优化, 效率降低
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
// 锁定代码块
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- 使用静态内部类实现
// IoDH 初始化不会失败使用, 延迟加载
public class Singleton {
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
private Singleton() {}
}
- 使用枚举类型
public enum Singleton{
INSTANCE;
}
枚举类反编译之后会被转换成形如public final class T extends Enum的定义。枚举中的各个枚举项通过static来定义。
一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的(因为虚拟机在加载枚举的类的时候,会使用ClassLoader的loadClass方法,而这个方法使用同步代码块保证了线程安全),所以枚举类是线程安全的。
结构型模式
代理模式
给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问,必须经过代理才能访问被代理对象。在应用场景上不同于装饰者模式,更偏向于代理的含义,代理模式重点在于控制对象的行为,而装饰模式侧重于增加对象的职能,为对象增加额外的职能。
结构如下:
Java中实现动态代理
Java中代理模式有动态代理、静态代理、Cglib代理。
Java中实现动态代理的步骤: 1.定义一个委托类和公共接口。
2.自己定义一个类(调用处理器类,即实现 InvocationHandler 接口),这个类的目的是指定运行时将生成的代理类需要完成的具体任务(包括Preprocess和Postprocess),即代理类调用任何方法都会经过这个调用处理器类
3.生成代理对象(当然也会生成代理类),需要为他指定(1)委托对象(2)实现的一系列接口(3)调用处理器类的实例。因此可以看出一个代理对象对应一个委托对象,对应一个调用处理器实例。
4.Java 实现动态代理主要涉及以下几个类:
java.lang.reflect.Proxy: 这是生成代理类的主类,通过 Proxy 类生成的代理类都继承了 Proxy 类,即 DynamicProxyClass extends Proxy。
java.lang.reflect.InvocationHandler: 这里称他为"调用处理器",他是一个接口,我们动态生成的代理类需要完成的具体内容需要自己定义一个类,而这个类必须实现 InvocationHandler 接口。
例子:
/**
* 接口
*/
interface Subject {
void request();
}
public class DynamicProxyDemo01 {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();//1.创建委托对象
ProxyHandler handler = new ProxyHandler(realSubject);//2.创建调用处理器对象
//3.动态生成代理对象
Subject proxySubject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler);
proxySubject.request(); //4.通过代理对象调用方法
}
}
/**
* 委托类
*/
class RealSubject implements Subject {
public void request() {
System.out.println("Real Subject Request");
}
}
/**
* 代理类的调用处理器
*/
class ProxyHandler implements InvocationHandler {
private Subject subject;
public ProxyHandler(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
System.out.println("====before====");
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
行为型模式
观察者模式
观察者模式优点:
观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。
观察者模式缺点:
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
策略模式
策略模式也叫政策模式,是一种行为型设计模式,是一种比较简单的设计模式。策略模式采用了面向对象的继承和多态机制。
策略模式适合使用在:1.多个类只有在算法或行为上稍有不同的场景。2.算法需要自由切换的场景。3.需要屏蔽算法规则的场景。
使用策略模式当然也有需要注意的地方,那么就是策略类不要太多,如果一个策略家族的具体策略数量超过4个,则需要考虑混合模式,解决策略类膨胀和对外暴露问题。在实际项目中,我们一般通过工厂方法模式来实现策略类的声明。
优点:1.算法可以自由切换。2.避免使用多重条件判断。3.扩展性良好。