这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天
一.简述
模式
创建型模式:
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
- 工厂模式(Factory Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 单例模式(Singleton Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
结构型模式:
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 过滤器模式(Filter、Criteria Pattern)
- 组合模式(Composite Pattern)
- 装饰器模式(Decorator Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
行为型模式:
- 责任链模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 解释器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 观察者模式(Observer Pattern)
- 状态模式(State Pattern)
- 空对象模式(Null Object Pattern)
- 策略模式(Strategy Pattern)
- 模板模式(Template Pattern)
- 访问者模式(Visitor Pattern)
J2EE 模式:
这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。
- MVC 模式(MVC Pattern)
- 业务代表模式(Business Delegate Pattern)
- 组合实体模式(Composite Entity Pattern)
- 数据访问对象模式(Data Access Object Pattern)
- 前端控制器模式(Front Controller Pattern)
- 拦截过滤器模式(Intercepting Filter Pattern)
- 服务定位器模式(Service Locator Pattern)
- 传输对象模式(Transfer Object Pattern)
六大原则
1、开闭原则(Open Close Principle)
对扩展开放,对修改关闭。定义抽象类和接口。直接实现取改变。不改变原有的类。
2、里氏代换原则(Liskov Substitution Principle)
将功能抽象给接口。子类重写父类的方法,在父类和子类使用该方法时,会出现问题。
3、依赖倒转原则(Dependence Inversion Principle)
针对接口编程,依赖于抽象而不依赖于具体。这个原则是开闭原则的基础。
4、接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口,比使用单个接口要好。不同的类只想用接口的一个方法,而不是全部方法,把这个方法抽象出去。
5、迪米特法则,又称最少知道原则(Demeter Principle)
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。通过中介沟通。
6、合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承。
继承会导致代码臃肿,将每个子类都有的属性或者功能抽象出去。
二.四大模式
创建者模式
1.单例模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
两种模式实现创建者模式:
- 饿汉式:在类加载的时候就会创建对象实例
-
- 通过私有属性、在类中静态创造实例、并创建一个静态创建该类的方法。
public class Singleton {
private String name;
private static Singleton singleton = new Singleton();
public static Singleton getInstance(){
return singleton;
}
}
-
- 通过静态代码块创建。可以在静态代码块中创建逻辑等方法
public class Singleton {
private Singleton(){};
private static Singleton singleton;
static {
singleton = new Singleton();
}
public static Singleton getInstance(){
return singleton;
}
}
- 懒汉式:类加载不会导致单例被创建,只有首次使用该对象时才会被创建。线程不安全
// 通过加锁解决多线程创建多个对象的问题
public class Singleton {
private Singleton(){};
private static Singleton singleton;
public static synchronized Singleton getInstance(){
if (singleton == null) singleton = new Singleton();
return singleton;
}
}
-
- 双重检查锁模式。多线程模式下可能会出现空指针问题。添加 volatile 可以解决
public class Singleton {
private Singleton(){};
private static volatile Singleton singleton;
public static Singleton getInstance(){
// 该对象只有为null才使用锁
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null) singleton = new Singleton();
}
}
return singleton;
}
}
-
- 通过静态内部类的方式创建
public class Singleton {
private Singleton(){};
// 匿名内部类
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
// 对外报漏一个方法
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
-
- 通过枚举类实现。饿汉式
public enum Singleton {
INSTANCE;
}
问题:
使用序列化反序列化、反射回破坏单例模式
解决:
反序列化问题:在类中定义一个readResolve方法,返回创建的实例。
在类中创建一个字段为false,只有第一次访问时正常,并设置字段为true。加锁
2.工厂模式
通过使用一个共同的接口来指向新创建的对象。
- 简单工厂模式:
客户 -> factory ->不同的对象
只不过是将逻辑放在了工厂里,不用自己new了。但是factory和不同的对象还是产生了新的耦合。
- 工厂方法模式:
定义一个接口,不同的接口实现类实现不同的方法
客户 -> 接口 -> 实现类 -> 不同的对象
-> 实现类 -> 不同的对象
3.抽象工厂模式
使用的是某一族产品
和工厂模式一样,只不过在接口中多定义了点方法
Collection.iterator方法用的就是抽象工厂模式
工厂接口的实现方法 -> 创建对象(实现了接口的对象)
4.原型模式
用一个已经创建的实例作为原型,通过该原型复制一样的新对象。
结构:
- 抽象原型类:规定具体原型对象必须实现的clone()方法
- 具体原型类:实现方法
- 访问类:使用方法
浅克隆:创建一个新的对象,属性完全一样。对于非基本类型,指向原对象地址。
深克隆:创建一个新的对象,创建新的属性
- 实现浅拷贝
步骤:
- 创建一个类实现Cloneable接口
- 要复制的时候,先创建实例对象,再调用clone方法
如果要实现深克隆:序列化
5.建造者模式
复杂对象的构建与表示分离,同样的构建构造不同的表示。如同装电脑。
定义了一个方法,传入不同的对象,怎么创建是自己定义的,不需要用户管。
和工厂模式一样。
结构:
- 抽象建造者类(Builder):接口
- 具体建造者类(ConcreateBuilder):实现接口,定义具体的内容
- 产品类(Product): 创建的是个啥
- 指挥者类(Director):传入Builder接口,构造方法确认具体实现。报漏一个接口调用的方法封装
将功能的实现方法定义在一个类中 -> 用户调用的时候传入不同的实现方法。
public class phone {
private String cpu;
private String screen;
private String b;
// 2.通过构建者Builder对象对外暴露创建
private phone(Builder builder) {
this.cpu = builder.cpu;
this.screen = builder.screen;
this.b = builder.b;
}
public static final class Builder {
private String cpu;
private String screen;
private String b;
public Builder cpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder screen(String screen) {
this.screen = screen;
return this;
}
public Builder b(String b) {
this.b = b;
return this;
}
public phone build() {
return new phone(this);
}
}
}
总结:
- 单例模式:只创建一个对象,常用的静态代码块、枚举类
- 工厂模式:具体功能的实现,接口、实现类、对象的结偶
- 抽象工厂模式:很多功能放一个接口里
- 原型模式:复制
- 建造者模式:注重的组装的过程,具体实现看工厂模式
对于调用接口而不是客户端的工厂/Directer,需要通过用构造方法传入接口的实现方法。客户端在调用工厂的时候传入需要的实现方法。
结构型模式
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
1.代理模式
概述:
通过代理取获取对象
分为静态代理和动态代理:
- 静态代理:编译期就生效
- 动态代理:Java运行期动态生成
结构:
- 抽象:接口或者抽象类
- 真实
- 代理:提供和真实主题相同的接口,含有对真实主题的引用。可以扩展
代理可以进行扩展
Java动态代理
// 获取真实对象
private Com com = new Com();
// 对外提供一个获取代理对象接口的方法
public Sell getProxy() {
// 通过Proxy类
Sell 代理对象 = (Sell) Proxy.newProxyInstance(
com.getClass().getClassLoader(),
com.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(com, args);
System.out.println("代理对象");
return invoke;
}
});
return 代理对象;
}
实现原理:
客户端调用接口的方法,实际调用的是代理类($Proxy0)中的sell方法。
在创建代理类($Proxy0)时,会传入我们输入的method方法,并将此方法传入父类Proxy中,
父类会将传入的方法设置给InvocationHandler接口的invoke方法。
调用代理类($Proxy0)的方法,会调用Proxy父类的InvocationHandler接口invoke的方法,也就是我们自己传入的方法。
invoke方法通过反射执行真实对象所属类的方法。
CGLIB动态代理
没有接口实现动态代理
通过Enhancer实例创建原类的子类对象,继承Method类并设置对象的方法
- 创建Enhancer对象
- setSuperClass设置父类 setCallback设置回调函数(继承Method类)
- create方法货去子类对象
对比:
CGLIB代理和JDK对比
- CGLIB底层使用ASM生成框架,使用字节码技术获取代理类。不能对final修饰的类或方法进行代理。(本质子类)
- JDK代理在有接口的情况下使用
动态代理和静态代理
- 代理对象使用任何方法只需调用一个方法
- 静态代理代理类也要实现方法,维护高
2.适配器模式
接口转换
InputStreamReader使用
StreamEdcoder继承了InputStream、实现了Reader的接口
InputStreamReader继承了StreamEdcoder类、实现了Reader接口
结构:
- 目标
- 适配者
- 适配器
类适配器模式:继承适配者的功能,实现要适配的接口
对象适配模式:将适配者接口作为自己的参数,实现要适配的接口
3.装饰者模式
BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
结构:
- 抽象构建角色:定义一个抽象接口规范准备接收附加责任的对象
- 具体构建角色:实现抽象构建,通过装饰角色添加职责
- 抽象装饰角色:继承或实现抽象构建,并包含具体构建的实例,可以通过其子类扩展具体构建的功能
- 具体装饰角色:实现抽象装饰的相关方法,并给具体构建对象添加附加的责任
我想在你的基础上加点东西,所以我要继承你和包括你,再加点自己的东西。
4.侨接模式
有不同类型的用户 用不同的方法
多对多
给不同的用户自己选择需要什么方法,所以在创建时要传入自己想要的方法
结构:
- 抽象化角色:抽象类,包含一个实现化对象的引用
- 扩展抽象化角色:抽象化角色的子类,实现父类中的方法,可调用实现化的方法
- 实现化角色:定义实现化角色的接口,供扩展抽象角色调用
- 具体实现化:给实现化角色实现
5.外观模式
将功能全部隐藏,只对外暴露一个接口
结构:
- 外观角色:为多个子系统提供一个共同的接口
- 子系统角色:实现系统的部分功能
6.组合模式
树形结构
分类
- 透明组合模式
所有子模块与抽象接口一样,如果调用未实现的方法,会编译错误
- 安全组合模式
new 对象的时候自己创建方法
7.享元模式
大量创建相同的对象,但是只用一会,容易资源浪费
结构
- 内部状态:不会随着环境的改变而改变的可共享部分
- 外部状态:随环境改变而改变的不可以共享的部分
- 抽象享元角色
- 具体享元角色
- 非享元角色
- 享元工厂
常量池中-128 ~ 127就是提前缓存好的,只需要在用的时候直接调用数组下标即可
行为型模式
对象的组合 -> 完成任务
责任链模式(Chain of Responsibility Pattern)
if else
结构:
- 抽象处理者:定义一个处理请求的接口,下一后继连接
- 具体处理者:实现请求,不行就传给后继连接
- 客户类:传入个数值
命令模式(Command Pattern)
类似于消息队列,发消息 与 接收消息 的对象进行解耦
Runnable接口相当于命令角色,Thread相当于调用者,start方法相当于方法
Thread包含并下达Runnable命令 Runnable下的实现方法包含了实现对象和实现方法
结构
- 抽象命令类
- 具体命令类
- 接收者
- 调用者
解释器模式(Interpreter Pattern)
根绝定义的规定,解析这个语句
迭代器模式(Iterator Pattern)
一个对象属性访问聚合对象的数据,而不暴露内部对象。
把迭代的方法放在迭代器里实现,在集合中只要选择迭代器的一个实现类,让这个实现类去完成迭代就行了
结构:
- 抽象聚合角色:
- 具体聚合对象
- 抽象迭代器角色
- 具体迭代器角色
中介者模式(Mediator Pattern)
将所有对象都介入中介者,所有类的关系通过中介者
结构:
- 抽象中介者
- 具体中介者
- 抽象同事类
- 具体同事类
备忘录模式(Memento Patter)
回滚
结构:
- 发起人
- 备忘录
- 管理者
观察者模式(Observer Pattern)
一对多的状态,多个监听器监听某一个主题对象。
发布——订阅 模式
结构:
- 抽象主题(抽象被观察者),抽象主题角色把所有观察者保存在一个集合中,对外提供增加和删除观察者对象
- 具体主题(具体被观察者)
- 抽象观察者
- 具体观察者
-
- 窄接口
- 宽接口
状态模式(State Pattern)
不同的逻辑状态有不同的方法
结构:
- 环境角色
- 抽象状态角色
- 具体状态橘色
空对象模式(Null Object Pattern)
策略模式(Strategy Pattern)
一个要求有很多个方法可以实现,选哪个交给用户决定
将算法实现和算法调用进行解耦
结构:
- 抽象策略类
- 具体策略:提供具体的抽象策略的实现
- 环境:只有一个策略类的引用,最终给客户端引用
模板模式(Template Pattern)
结构:
- 抽象类
-
- 模版方法:骨架
- 基本方法:具体实现骨架
-
-
- 抽象方法
- 具体方法
- 钩子方法
-
- 具体子类
InputStream
访问者模式(Visitor Pattern)
封装一些作用于某种数据结构的各元素的操作,在不改变数据结构的前提下定义操作
操作
- 抽象访问者
- 具体访问者
- 抽象元素角色
- 具体元素角色
- 结构对象角色