设计模式
作者:Grey
原文地址:
单例模式
饿汉式
类加载的时候就会初始化这个实例, JVM保证唯一实例,线程安全, 但是可以通过反射破坏
方式一
public class Singleton1 {
private final static Singleton1 INSTANCE = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return INSTANCE;
}
}
方式二
public class Singleton2 {
private static final Singleton2 INSTANCE;
static {
INSTANCE = new Singleton2();
}
public static Singleton2 getInstance() {
return INSTANCE;
}
}
懒汉式
虽然可以实现按需初始化,但是线程不安全, 因为在判断INSTANCE == null的时候,如果是多个线程操作的话, 一个线程还没有把INSTANCE初始化好,另外一个线程判断INSTANCE==null 得到true,就会继续初始化
public class Singleton3 {
private static Singleton3 INSTANCE;
private Singleton3() {
}
public static Singleton3 getInstance() {
if (INSTANCE == null) {
// 模拟初始化对象需要的耗时操作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton3();
}
return INSTANCE;
}
}
为了防止线程不安全,可以在getInstance方法上加锁,这样既实现了按需初始化,又保证了线程安全,但是加锁可能会导致一些性能的问题
public class Singleton4 {
private static Singleton4 INSTANCE;
private Singleton4() {
}
public static synchronized Singleton4 getInstance() {
if (INSTANCE == null) {
// 模拟初始化对象需要的耗时操作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton4();
}
return INSTANCE;
}
}
为了提升一点点性能,可以不给getInstance整个方法加锁,而是对INSTANCE判空这段代码加锁, 但是又带来了线程不安全的问题
public class Singleton5 {
private static Singleton5 INSTANCE;
private Singleton5() {
}
public static Singleton5 getInstance() {
if (INSTANCE == null) {
synchronized (Singleton5.class) {
// 模拟初始化对象需要的耗时操作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton5();
}
}
return INSTANCE;
}
}
Double Check Locking模式,就是双加锁检查模式
这种方式中,Volatile是必需的,目的为了防止指令重排,生成一个半初始化的的实例,导致生成两个实例
具体可参考 双重检索(DCL)的思考: 为什么要加volatile?
说了这个问题
public class Singleton6 {
private volatile static Singleton6 INSTANCE;
private Singleton6() {
}
public static Singleton6 getInstance() {
if (INSTANCE == null) {
synchronized (Singleton6.class) {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton6();
}
}
}
return INSTANCE;
}
}
以下两种更为优雅的方式,既保证了线程安全,又实现了按需加载
方式一:静态内部类方式,JVM保证单例,加载外部类时不会加载内部类,这样可以实现懒加载
public class Singleton7 {
private Singleton7() {
}
public static Singleton7 getInstance() {
return Holder.INSTANCE;
}
private static class Holder {
private static final Singleton7 INSTANCE = new Singleton7();
}
}
方式二: 使用枚举, 这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化,这种方式是 Effective Java 作者 Josh Bloch
提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
public enum Singleton8 {
INSTANCE;
}
策略模式
实例:
假设我们有一个猫类,这个类里面有体重和身高这两个属性,给你一个猫的集合,然后需要你按猫的体重从小到大排序
思路:
我们可以把体重从小到大这个看成是一个策略,后续可能衍生其他的策略,比如:
按身高从高到低
按体重从小到大,体重一样的身高从高到低
以身高从低到高排序这个策略为例
public class CatSortStrategy implements Comparator<Cat> {
@Override
public int compare(Cat o1, Cat o2) {
return o1.getHeight() - o2.getHeight();
}
}
假设我们定义猫排序的方法是: sort
那么这个方法必然需要传入一个排序策略的参数(否则我怎么知道要怎么排序猫?)
所以定义的sort方法可以是:
public class Sorter {
public Cat[] sort(Cat[] items, Comparator<Cat> strategy) {
int length = items.length;
for (int i = 0; i < length; i++) {
for (int j = i + 1; j < length; j++) {
if (strategy.compare(items[i], items[j]) > 0) {
Cat tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
}
}
return items;
}
}
进一步抽象,如果我想让Sorter这个工具类不仅可以对猫进行各种策略的排序(基于比较的排序算法),还可以对狗进行各种策略的排序(基于比较排序算法),可以将Sorter定义成泛型
public class Sorter<T> {
public T[] sort(T[] items, Comparator<T> strategy) {
int length = items.length;
for (int i = 0; i < length; i++) {
for (int j = i + 1; j < length; j++) {
if (strategy.compare(items[i], items[j]) > 0) {
T tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
}
}
return items;
}
}
调用的时候, 泛型版本的Sorter可以对猫和狗都进行基于特定排序策略的排序。
Sorter<Cat> sorter = new Sorter<>();
Cat[] sortedCats = sorter.sort(cats, new CatSortStrategy());
Sorter<Dog> sorter = new Sorter<>();
Dog[] sortedCats = sorter.sort(dogs, new DogSortStrategy());
工厂模式
- 简单工厂
- 静态工厂--单例模式
- 抽象工厂
- 工厂方法
- Spring IOC DI
- Hibernate 换数据库只需换方言和驱动就可以
门面模式
- 对外
- 消息中间件
- GameModel
调停者模式
- 对内
- 消息中间件
- GameModel
责任链模式
- 碰撞检测
- Servlet filter
- Structs interceptor
- SpringMVC interceptor
装饰器模式
- IO流, Read/InputStream ,Write/OutputStream
- Tail/RectDecrator
观察者模式
事件处理
往往和责任链模式搭配使用
Spring
ApplicationEvent
组合模式
树的遍历
享元模式
String
连接池管理
代理模式
-
静态代理
-
动态代理
- jdk自带
- ASM操作二进制码
- Java Instrumentation
- cglib
- final类不行,代理类的子类 底层也是ASM
- jdk自带
-
Spring AOP
迭代器模式
- 容器和容器遍历
访问者模式
在结构不变的情况下动态改变对于内部元素的动作
做编译器的时候,生成AST的时候,进行类型检查
根据抽象语法树,生成中间代码
XML文件解析
构建器模式
链式编程
适配器模式
java.io
jdbc-odbc bridge
ASM transformer
桥接模式
抽象和具体的发展单独分支,抽象中持有一个具体的引用
使用桥接模式:
分离抽象与具体实现,让他们可以独自发展
Gift -> WarmGift ColdGift WildGiftGiftImpl -> Flower Ring Car
命令模式
结合责任链模式实现多次undo
结合组合模式实现宏命令
结合记忆模式实现transaction回滚
原型模式
Object.clone()
备忘录模式
记录状态,记录快照,瞬时状态,存盘
Tank的GameModel的load/save方法(实现序列化接口)
便于回滚
模板方法
钩子函数
RestTemplate
JDBCTemplate
State模式
状态迁移