设计模式目的是: 提高代码的复用性,扩展性, 减少代码的冗余,重构整体的代码。
六大原则:
1: 开闭原则, 易于扩展和升级及维护。 前提是基于接口和抽象类。 正常生产中都是基于接口和抽象类的。
对已经正常使用的接口方法禁止修改,因为一旦修改后测试不完全,很容易影响原来正常的流程造成未知的bug.
对增加开放, 原来的接口方法不动, 需要就增加一个接口, 不会影响原来正常的接口流程。
2: 里氏代换原则, 是对开闭原则的补充。
父类出现的地方, 子类一定可以出现,当子类可以替换父类而软件功能不受影响时,父类才是真正的被复用。子类可以在父类的基础增加新的方法。
里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实 现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3:依赖倒转原则
这是开闭原则的基础, 基于接口编程,依赖于抽象,而不依赖与基础。
4:接口隔离原则
不要在一个接口里面放很多的方法,这样会显得这个类很臃肿不堪。接口应该尽量细化,一个接口对应一个功能模块,同时接口里面的方法应该尽可能的少,使接口更加轻便灵活。
5:迪米特法则
一个对象尽量少和不相干的对象产生相互作用,使的功能模块相对的独立。
需要和其他对象联系时,最好通过第三者进行关联通信。
6:合成复用原则
在进行复用时, 尽量使用合成或者集合等方式进行, 其次才考虑使用继承。
要使用已有的对象时,最好将其纳入到新的对象中,使他成为新对象的一部分。就可以使用已有对象的功能。
设计模式的分类:
1:创建型:
工厂方法,抽象工厂,单例, 建造者,原型。
2:结构型
适配器,代理,装饰,外观,桥接, 组合, 享元
3: 行为型
策略,责任链,模版方法,观察者,迭代,命令, 备忘录, 状态,访问者,终结者,解释器
一: 工厂模式: 简单工厂,抽象工厂,静态工厂,工厂方法
工厂模式的目的: 将创建者和使用者分离开来。 工厂就是创建,生产。
优点:代码结构简单,满足开闭原则,对扩展开放,对修改关闭。
缺点:扩展时繁琐。
springmvc中的 beanFactory, IOC控制反转等都是工厂模式。
二: 策略模式 + 工厂
对算法的包装,把使用算法和算法分开。
用于解决多重if 判断。
三: 责任链模式 + 工厂,
多个对象处理同一个任务。对象使用链式存储结构,每一个对象都知道自己的下一个对象。
或者工厂中保证顺序
应用场景: 多条件流程判断, 权限控制,filer过滤器等。
四: 模版方法 + 工厂,
将共同的行为抽象到父类中实现, 不同的行为定义为抽象, 交由子类实现, 通过继承实现代码复用,通过父类调用子类实现操作。将共同的行为抽象为框架。
优点: 将共同行为抽象到父类中,去除子类重复代码, 子类实现自己的细节,有助于扩展,符合开闭原则。
缺点: 每一个不通的实现都需要增加一个子类,会导致类的个数增加,更加抽象。
模版方法与策略的区别:
模版是 将共同的行为抽象到父类实现, 子类实现不同的行为。 子类扩展维护性好, 模版关注的是一种算法
策略是定义公共的抽象接口, 子类各自实现不同的行为。 策略关注的是多种算法
五: 装饰者模式
在不改变原有对象的功能下,动态扩展或者撤销功能, 按照想要的顺序执行。
分为:
1: 父类抽象组件: 定义一个抽象的接口,规范准备扩展的功能的类
2: 父类抽象组件的具体实现组件: 要被附加扩展功能的类, 实现父类抽象组件的类。
3: 抽象装饰组件: 持有对具体实现组件的引用并定义父类抽象组件一致的接口
4: 具体装饰组件: 实现抽象装饰组件,负责对具体实现组件添加扩展额外的功能。
与责任链的区别:
装饰者是从子类开始,找到最上层的父类开始执行, 执行完父类在执行子类, 执行顺序与责任链相反。
责任链是先执行第一步,在第二步,第三步。
IO流就是用的装饰者模式 : FileReader, Reader.
6:代理模式
代理: 相等于中介一样的, 客户端访问 》 代理 》 目标
代理和目标都实现相同的接口, 可以在代理中扩展, 而不需要改动目标类。
满足开闭原则,扩展开放, 修改关闭
代理模式分为两种:
1: 静态代理, 手动创建 代理类, 效率低
两种实现方式: 继承, 接口实现
2: 动态代理
两种实现方式:
2.1: jdk动态代理
通过实现 InvocationHandler 接口, 反射 方式实现代理
2.2: cglib动态代理
通过 asm字节码框架,转换字节码并生成子类实现代理。
asm 是一个强大的, 高性能的,高质量的字节码技术框架。
jdk与cglib的区别:
jdk通过实现接口,反射方式实现代理, 必须是接口, 不是接口的类无法代理
cglib 通过封装asm字节码框架,生成子类继承目标类的方式实现代理。 无法重写代理final修饰的方法。
7: 观察者模式
相当于 发布订阅, 一个发布者, 一个订阅者, 一个主题。
8: 门面模式也称外观模式
提供统一的访问接口, 隐藏内部实现
9:状态模式
一个对象根据内部的状态而执行其行为,内部的状态改变后,其行为也会对应的改变,在不同的状态间切换
一个对象的行为取决于它的状态,并且它在运行时根据它的状态而改变它的行为
状态模式与策略模式是类似的,区别是:
状态模式 : 在不通的状态间切换,封装的是对象的状态, 对象针对的是不同的行为
策略模式:根据具体的情况选择策略, 封装的是算法或策略, 对象针对的是相同的行为
10 适配器
相当于转换器, 一个不能直接使用的接口, 通过转换变为可以直接使用的接口。 这个转换类就是适配器
类适配器同过继承两种方式
对象适配器则是组合模式
组合优于继承
11 单例模式
在jvm中保证只有一个实例存在。
单例分为:
懒汉式, 恶汉式, 静态内部类单例,静态块,枚举等。
懒汉式用的时候加载, 初始化的时候比较慢, 需要加双重检验锁。
恶汉式, 一开始就创建, 天生线程安全。
静态内部类,继承了懒汉式的优点,不需要加锁。
枚举,天生线程安全,可以有效防止反射破解。
推荐使用枚举单例
反射破解用的是权限参数设置为true, 暴力破解 构造函数.setAccessible(true),反射就可以访问无参或有参的构造方法破解单例
反射: 动态的获取所有的类信息, 如方法,属性。
如果是单例模式, 推荐使用枚举, 无法使用反射破解, 天生线程安全。在反射创建对象时,jdk源码中判断如果是反射创建枚举,则会直接抛出异常。
enum 单例是最优秀的实现方式,
enum单例源码分析
/**
* @ClassName: EnumDecompose
* @Description: enum源码分析
* @Author: tangjiandong
* @Time: 2020/8/8 18:03
* @Viersion 1.0
*/
public enum EnumDecompose {
SINGLE;
public void test()
{
System.out.println("-----enum单例源码分析");
}
}
下图是反编译出来的, 将定义的枚举类转换为普通类继承枚举类, 定义的枚举就是类对象。
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumDecompose.java
package com.tang.single;
import java.io.PrintStream;
public final class EnumDecompose extends Enum
{
public static EnumDecompose[] values()
{
return (EnumDecompose[])$VALUES.clone();
}
public static EnumDecompose valueOf(String name)
{
return (EnumDecompose)Enum.valueOf(com/tang/single/EnumDecompose, name);
}
private EnumDecompose(String s, int i)
{
super(s, i);
}
public void test()
{
System.out.println("-----enum\u5355\u4F8B\u6E90\u7801\u5206\u6790");
}
public static final EnumDecompose SINGLE;
private static final EnumDecompose $VALUES[];
static
{
SINGLE = new EnumDecompose("SINGLE", 0);
$VALUES = (new EnumDecompose[] {
SINGLE
});
}
}