设计模式是对软件设计问题常见问题的通用解决方案,使用设计模式可以提高代码的可读性、可维护性和复用性。此外,设计模式还为设计决策提供了共同的语言和框架。
一般来说,设计模式可以分为三类:创建型模式、结构型模式、行为模式。
- 创建型模式提供了对象创建和使用分离的机制。
- 结构型模式关注对象和类如何组装成更大结构的方式。
- 行为模式负责对象之间的沟通协作和职责委派。
学习资料:设计模式目录:22种设计模式、图说设计模式 — Graphic Design Patterns。
一、创建型(5)
创建型模式提供了对象创建和使用分离的机制。
包括单例模式、建造者模式、工厂方法模式、抽象工厂模式、原型模式。
单例模式
单例模式保证一个类只有一个实例,并提供访问这个实例的全局节点,比如Java中的Runtime、DriverManager等,Spring中的bean默认也都是单例的。
饿汉式
- 优点:线程安全;
- 缺点:类初始化时就实例化,但也许不会被使用,浪费内存(一般单例类用于全局资源管理,涉及较多资源,因此使用时再实例化更好)
1 | class SingletonHungry { |
懒汉式
- 优点:延迟加载,节省内存;
- 缺点:线程不安全,多线程环境下可能会创建多个实例;
- 缺点(加锁):线程安全,但效率低,只允许一个线程访问;
1 | class SingletonLazy { |
双重校验锁
- 优点:延迟加载,节省内存;线程安全;
- 缺点:需要JDK1.5以上版本支持(volatile关键字在JDK1.5版本后才能保证其可见性);
1 | class SingletonDoubleCheck { |
为什么需要用volatile?
Java创建对象并不是原子操作,为了避免这个过程中的重排序,出现半个对象问题,使用Volatile禁止重排序,并保证多线程可见性
(比如避免new到一半被其他线程调用。)
静态内部类(推荐)
优点:1)延迟加载,节省内存(在调用getInstance()方法时才会加载SingletonHolder类);2)JVM保证线程安全(类加载是线程安全,且只加载一次)。
1 | class SingletonStaticInner { |
枚举
优点:线程安全,防止序列化攻击和反射攻击。
缺点:所有属性必须在创建时指定,不可以延迟加载;占用内存比静态变量高。
1 | enum SingletonEnum { |
破坏单例模式的方式
- 反射:可以在构造函数中判断是否存在实例,存在直接抛出异常
- 序列化攻击:通过反序列化破坏单例▶不实现序列化接口,或者反序列化直接返回相关单例对象。
- Clone:通过重写clone方法,抛出异常禁止复制(因为clone在Object中,不管是否实现Cloneable接口都可能被破坏)
建造者模式(Builder、生成器模式)
建造器模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
我的理解其实就是抽象出一个复杂对象的构建过程,向外提供一系列方法,提高代码可读性和可维护性。
比如Java中的StringBuilder
,lombok的@Builder注解会生成建造者模式相关代码。
这里通常也会和写成流式接口,可以链式调用。
工厂方法模式
简单工厂模式将创建对象的过程交给工厂类负责,需要创建对象时直接找工厂获取。
1 | public class TeaFactory { |
但是如果需要需要新增产品还需要修改原来的工厂类,不符合开闭原则;工厂类还需要负责每个产品的创建过程,不符合单一职责原则。
工厂方法模式将工厂也进行了抽象,提供了工厂接口,每种对象需要实现对相应的工厂类,由具体工厂类去决定实例化的对象。
也就是包括抽象工厂接口、具体工厂类、抽象产品、具体产品类。
1 | ITeaFactory factory = new LongjingTeaFactory(); |
比如Java中的Calendar、Spring的BeanFactory、JDBC的DriverManager都是工厂模式。
抽象工厂模式
抽象工厂模式是在工厂模式基础上,可以创建一系列相关对象。
比如UI组件库(如按钮、文本框等)。
原型模式
原型模式用于通过克隆创建重复对象,省略了复杂对象创建过程,适合需要大量重复对象的场景。
需要实现Cloneable接口,比如Spring中指定bean为prototype模式,就会开启原型模式。
二、结构型(7)
结构型模式关注对象和类如何组装成更大结构的方式
包括适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式、代理模式。
适配器模式
适配器模式是当接口不兼容时,引入适配器类允许将一个类转换为期望接口,使得原本不能一起工作的类一起工作。将接口之间解耦,扩展功能,增加复用性。
实现适配器模式有类适配器、对象适配器两种方式。
类适配器:将源类的方法转化为目标接口的方法,定义适配器类继承自源类并实现目标接口进行方法重写和转化。
对象适配器:定义适配器类继承/实现目标接口,里面包含一个源类对象,补充源类缺失的目标接口方法。
适用场景:第三方库不兼容,Java中的IO库通过适配器模式对不同的数据源进行适配。
桥接模式
桥接模式是指将抽象和实现分离,使得两者可以独立变化,适用于多个维度变化的场景。直白说就是抽象类里有抽象类对象。优势是解耦,扩展性强。
个人觉得三层架构实现就是桥接模式。
组合模式
组合模式是指将对象组合为属性
装饰器模式
外观模式
享元模式
代理模式
三、行为型(11)
行为模式负责对象之间的沟通协作和职责委派。
包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。