zoukankan      html  css  js  c++  java
  • 设计模式学习笔记

    设计模式

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

    代码

    参考资料

  • 相关阅读:
    dotnet 新项目格式与对应框架预定义的宏
    dotnet 线程静态字段
    dotnet 线程静态字段
    dotnet 通过 WMI 拿到显卡信息
    dotnet 通过 WMI 拿到显卡信息
    dotnet 通过 WMI 获取指定进程的输入命令行
    dotnet 通过 WMI 获取指定进程的输入命令行
    dotnet 通过 WMI 获取系统信息
    dotnet 通过 WMI 获取系统信息
    PHP show_source() 函数
  • 原文地址:https://www.cnblogs.com/greyzeng/p/14107751.html
Copyright © 2011-2022 走看看