• 设计模式学习笔记


    设计模式

    作者: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图

    代码

    参考资料

  • 相关阅读:
    elk+redis
    elk7.4+filebeat收集日志
    k8s-高可用集群实现(keepalived+haproxy)
    k8s-高可用集群实现(keepalived)
    keepalived(双主模式)+haproxy+mysql_slave
    haproxy-实现mysql多slave读负载均衡
    MySQL数据库的配置
    前端模块化(AMD和CMD、CommonJs)
    一分钟配置jdk
    MySQL基础语法
  • 原文地址:https://www.cnblogs.com/greyzeng/p/14107751.html
Copyright © 2020-2023  润新知