外观(门面)模式
化零为整,把零碎的功能拼成一个整体,对外提供一个统一接口,用来访问子系统中的多个接口。
总结
解耦,不需要一个个对接,使用简单。
单例模式
负责创建对象,同时确保只有单个对象被创建。
饿汉式
线程安全,在类加载时就会进行初始化,访问时直接使用。
public class Student { private static final Student student = new Student(); private Student() {} public static Student getInstance() { return student; } }
在类加载的时候便完成了实例化。
饱汉(懒汉)式
需要使用时才会去创建实例。
public class Student { private volatile static Student student; private Student() {} public static Student getInstance() { if (student == null) { synchronized (Student.class) { if (student == null) { student = new Student(); } } } return SINGLETON; } }
如果要保证线程安全,需要添加双重锁,当两个线程同时执行 getInstance 时,结果都为 true,会导致一起进入排队状态,从而重复创建新的对象;
所以需要双重判断,预防多线程重复创建。
静态内部类
public class Student { private Student() {} private static class Instance { private static final Student student = new Student(); } public static Student getInstance() { return Instance.student; } }
线程安全,只有使用时才会创建实例,不会造成资源浪费的问题。
枚举式
public class Student { private enum StudentEnum { INSTANCE; private final Student instance; StudentEnum() { instance = new Student(); } private Student getInstance() { return instance; } } private Student() {} public static Student getInstance() { return StudentEnum.INSTANCE.getInstance(); } }
线程安全,只会装载一次,无论是序列化、反序列化、反射还是克隆都不会新创建对象。
简单工厂 & 抽象工厂
工厂模式将对象的创建和使用分离,属于一个产品系列,而抽象工厂是多个产品系列一个工厂类。
//宝马系列
BMWCar bmBean = CarFactory.create(Car.BMW); BMWX1 x1 = bmBean.getX1();
x1.drive();
BMWZ4 z4 = bmBean.getZ4();
z4.drive();
//奔驰系列 BenZCar benZBean = CarFactory.create(Car.BenZ);
BenZSUV suvBean = benZBean.getSUV(); suvBean.speed(); suvBean.stop();
总结
解耦,提高扩展,但是每次新增需要修改原代码。
构键者模式
用于一步步创建复杂对象,一般分产品类,构键类和组装类,通常用于配置工具类使用。
代理模式
为两者之间提供一个代理对象(拦截层),并且目标对象只能被代理对象访问,调用目标对象都要先通过代理对象。
静态代理
public interface A { void test(); } public class B implements A{ @Override public void test() { System.out.println("B"); } } public class C implements A{ @Override public void test() { System.out.println("C"); } }
//我们这里也让Utils实现A接口,复写test方法 public class Utils implements A { A a; public Utils(A a) { this.a = a; } @Override a.test(); } } class Test { public static void main(String[] args) { C c = new C(); Utils utils = new Utils(c); utils.test(); } }
目标对象和代理对象实现共同的接口或继承相同的父类,在不修改目标对象的前提下进行扩展。
平时接手代码,就可以在不修改原代码的基础上扩展原有功能。
总结
代理对象需要与目标对象实现一样的接口,一对一模式,会导致代理类比较多;
接口修改后,目标对象与代理对象都要修改,维护成本高,适合目标类比较少的情况。
动态代理
一对多模式,一个代理类可利用反射机制代理多个委托类,并且是在运行时创建动态代理类。
JDK接口动态代理
public class Http implements A{ @Override public void test() { System.out.println("test"); } } public class Utils implements InvocationHandler { Object object; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("请求网络"); method.invoke(object, args); System.out.println("结束"); return null; } } //api public interface A{ void test(); } class Test { public static void main(String[] args) { Http http = new Http(); Utils utils = new Utils(); utils.object = http; A p = (A) Proxy.newProxyInstance(http.getClass().getClassLoader(), http.getClass().getInterfaces(), utils); p.test(); } } 输出 请求网络 Http 结束
代理对象不需要实现接口,目标对象必须实现接口。
Proxy.newProxyInstance(classLoader 类加载器, classInterfaces[] 接口列表, InvocationHandler 拦截处理类
);
总结
可以将代理类的创建推迟到运行时期,代理类通过 JDK 的 API 动态在内存中创建,这样就可以大大减少代理类的创建和维护,但前提是目标类必须实现接口。
Cglib子类动态代理
不强制实现接口,在内存中动态的创建一个目标类的子类对象,从而实现对目标类的代理。
总结
通过 ASM 框架转换字节码并生成新类,不能使用 final 修饰代理方法。
可以直接生成二进制 class 文件,也可以在类被加载到 Java 虚拟机之前改变类行为。
责任链模式
请求在链上一个个传递,直到链中某个对象处理。
总结
可以控制请求顺序,新增不用修改原代码,逻辑解耦,单一职责,效率不高,比较复杂,符合多个认证或者校验等场景。
享元模式
共享基本的单元、元件,提升已有元件的复用率,减少重复制造开销。
代码中主要分元件跟工厂类,工厂中编写获取方法,方法判断该元件是否存在,如果不存在新建元件,存入共享池中,如果存在直接从池中获取返回,类似单例。
总结
减少了资源的使用,但是增加了代码的复杂程度。
模板方法模式
在一个抽象类公开定义了抽象的执行方法跟逻辑方法,子类继承实现父类逻辑方法,但调用将以抽象类中定义的执行方法执行。
public abstract class Student { public final void getInfo() { info1(); info2(); ToastUtils.show("获取信息成功"); } /** 逻辑方法 */ abstract void info1(); abstract void info2(); }
只需要继承方法就能实现代码复用,不过父子类存在耦合。