设计模式学习笔记

设计模式

作者:Grey

原文地址:

Github

语雀

博客园

单例模式

UML

饿汉式

类加载的时候就会初始化这个实例, 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
  • 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模式

状态迁移

解释器模式

UML和代码

UML图

代码

参考资料

原文地址:https://www.cnblogs.com/greyzeng/p/14107751.html