不记文档,白忙一场
0、概述
学习地址
1> 学习地址一 地址:菜鸟教程 -> 工厂模式 网址:https://www.runoob.com/design-pattern/factory-pattern.html
分类
总体来说设计模式分为三大类: 1> 创建型模式,共五种:工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式。 2> 结构型模式,共七种:适配器模式、桥接模式、代理模式、组合模式、装饰器模式、外观模式、享元模式。 3> 行为型模式,共十一种:观察者模式、访问者模式、中介者模式、解释器模式、迭代器模式、备忘录模式、策 略模式、命令模式、状态模式 <模板方法模式、责任链模式>。
1、工厂模式
工厂/抽象工厂区别
经典: https://m.php.cn/article/344745.html
应用场景
1> 场景: 通过使用一个共同的接口来指向新创建的对象(不暴露创建逻辑) ------------------------------------------------------------------------------------------- 2> 例子: 统一接口创建不同"形状接口"实现类的对象 ------------------------------------------------------------------------------------------- 3> 最终实现: 1> 一个抽象工厂类,可以派生出多个具体工厂类(一致)。 2> 一个抽象产品类,可以派生出多个具体产品类。 3> 一个具体工厂类,只能创建一个具体产品类的实例。 ------------------------------------------------------------------------------------------- 注: 1> 工厂类和抽象工厂类区别: 1) 抽象工厂模式,就是将多个工厂类又抽象出来一个超级工厂类(当然多个工厂类是相关联的) 2) 工厂模式:"圆"有一个工厂类,"矩形"有一个工厂类,"正方形"有一个工厂类 2> 解决简单工厂模式问题:创建一个工厂接口和创建多个工厂实现类 3> 简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行 修改,这违背了闭包原则。
2、抽象工厂模式
应用场景
1> 场景: 一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂 ------------------------------------------------------------------------------------------- 2>例子: 一个"图形"既有"形状"又有"颜色",这两个属性分别有多个实现类需要用工厂来创建 ------------------------------------------------------------------------------------------- 3> 最终实现: 1> 一个抽象工厂类,可以派生出多个具体工厂类(一致)。 2> 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。 3> 一个具体工厂类,可以创建多个具体产品类的实例。 ------------------------------------------------------------------------------------------- 注: 1> 抽象工厂模式,就是将多个工厂类又抽象出来一个超级工厂类(当然多个工厂类是相关联的) 2> 工厂模式:"圆"有一个工厂类,"矩形"有一个工厂类,"正方形"有一个工厂类
3、单例模式
应用场景
1> 场景: 一个类负责创建自己的对象,同时确保只有单个对象被创建 ------------------------------------------------------------------------------------------- 2>例子: 宇信eChain框架中,WorkflowCache类对象单例,且只有第一次访问创建一次 ------------------------------------------------------------------------------------------- 3> 最终实现: 1> 变量:静态的(内存中只有一份),私有(防止外部通过类.变量名调用)、final(可有可无) 2> 构造器:私有(阻止外部直接实例化对象) 3> 方法:公有(供外部调用) ------------------------------------------------------------------------------------------- 注: 1> 饱汉(懒汉),用的时候才实例化对象;饥汉,不管用不用先实例化出来 2> 饱汉模式,注意getInstance方法中创建对象new包括外面判断instance是否为null,需要加锁 3> 创建单例模式的所有方法:懒汉式、饿汉式、双重校验锁、静态内部类、枚举 4> 懒汉式:有线程安全和不安全两种写法 5> 懒汉式 vs 双重校验锁:一切为了性能,懒汉式锁方法,双重校验锁锁代码块,性能更高 6> 饥汉式:基于classloader 机制 7> 静态内部类:基于classloader 机制 ------------------------------------------------------------------------------------------- 注: 1> 双重校验锁解释:https://blog.csdn.net/java_1996/article/details/87472644 2> volatile修饰的变量不允许"指令重排序",确保所有线程看到这个变量的值是一致的 3> Test类中有Test t = new Test()成员变量,会stackOverflowError 4> Test类中有static Test t = new Test()静态成员变量,则正常创建 5> 对静态域使用延迟初始化,应使用静态内部类(因为更简单,且"静态内部类"只适用于静态域的情 况); 双检锁方式可在实例域需要延迟初始化时使用。 6> 饥汉式:它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例 化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不 能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。 7> classloader 机制: 8> 静态域 vs 实例域: ------------------------------------------------------------------------------------------- 注:双重校验锁 1> 执行双重检查是因为,如果多个线程同时了通过了第一次检查,并且其中一个线程首先通过了第二次检查 并实例化了对象,那么剩余通过了第一次检查的线程就不会再去实例化对象。 2> 这样,除了初始化的时候会出现加锁的情况,后续的所有调用都会避免加锁而直接返回,解决了性能消耗 的问题。 来源:https://www.cnblogs.com/xz816111/p/8470048.html 3> 第二层校验是否为null的必要性 1> 如果a、b两个线程同时执行完外层if (singleton == null) 的判断 2> 都卡在synchronized (Singleton.class)锁的地方 3> 如果没有内层if (singleton == null) 的判断,线程a先获得锁,执行了初始化实例,释放锁 4> 线程b会获得锁,在内部再次执行初始化实例 总结:所以内外两层if (singleton == null) 的判断都是有必要的,第二次之后只会经过外层判断 4> volitile的必要性 给类对象赋值的过程,分为3步: 1> 给singleton分配内存; 2> 调用 Singleton 的构造函数来初始化成员变量; 3> 将给singleton对象指向分配的内存空间(此时singleton才不为null) 而JVM的指令会重排序,如果线程a正在进行对象赋值, 线程b进行外层if (singleton == null) 的判断,而指令重排序使得给变量分配完空间后,直接执行 步骤3,将没有初始化的singleton对象指向分配的内存空间,则变量为null 那么步骤3执行后singleton已经不为null,但是未执行步骤2,singleton对象初始化不完全,此时线 程B执行getInstance()方法,第一步判断时singleton不为null,则直接将未完全初始化的 singleton对象返回了 来源:https://blog.csdn.net/java_1996/article/details/87472644 ------------------------------------------------------------------------------------------- 注:触发类加载的情形 1> 显示加载:直接使用类的加载器进行加载. 2> 隐式加载: 1> 构建类的对象(包括new对象、反射创建对象) 2> 访问类的成员(静态成员变量,静态成员方法)
4、建造者模式
应用场景
场景:使用多个简单的对象一步一步(一层套一层)构建成一个复杂的对象。 解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,该对象后期可能经常变动 ------------------------------------------------------------------------------------------- 例子: 1、肯德基点套餐,这个套餐是:一个汉堡(Burger)和一杯冷饮(Cold drink) 2、汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。 3、冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中 ------------------------------------------------------------------------------------------- 最终实现: 1、抽象最上层接口("物品接口"):包括 - 名称、包装、价格 注:因为包装还需要抽象,所以写代码时候,需要先抽象"包装接口" 2、其中物品接口的包装属性,可以抽象接口("包装接口") 3、"包装接口"两个实现类:纸质包装和瓶子包装 4、"物品接口"有两个抽象类的实现类:汉堡和冷饮(所有汉堡的包装属性一样,固定下来,继承就好。冷饮同 理) 5、具体"物品实现类": 蔬菜汉堡 - 继承汉堡抽象类,重写名称和价格方法 鸡肉汉堡 - 继承汉堡抽象类,重写名称和价格方法 可口可乐 - 继承冷饮抽象类,重写名称和价格方法 百事可乐 - 继承冷饮抽象类,重写名称和价格方法 6、创建一个"套餐类" 成员变量,是整合套餐中的"物品接口"对象 7、创建一个"创造者类" 内部方法,就是返回"套餐类"对象 ------------------------------------------------------------------------------------------- 注: 一层套一层 "包装接口"(瓶子和纸盒) -> "食品接口"(汉堡<蔬菜和鸡肉汉堡>和冷饮<可口和百世>) -> "套餐类"
5、原型模式
应用场景
场景: 用于创建重复的对象,同时又能保证性能 ------------------------------------------------------------------------------------------- 例子: 创建一个对象,需要连接数据库,耗费大量资源的时候 ------------------------------------------------------------------------------------------- 最终实现: 1> 抽象类,实现cloneable接口,重写clone方法。 2> 提前加载数据进行存储。 3> 需要创建重复对象时,从存储中拿到再调用clone方法 ------------------------------------------------------------------------------------------- 注: 与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的