设计模式是一种思想,不能咬文嚼字、死抠字眼的去学,应该理解其中的思想。
设计的7大原则
1、开闭原则:扩展新类而不是修改旧类
原来有一个A类,现在需要添加新功能,我们要新建一个类,而不是在原来的A类进行修改
2、里氏替换原则:继承父类而不去改变父类
3、依赖倒置原则:面向接口编程,而不是面向实现类
4、单一职责原则:每个类只负责自己的事情,而不是变成万能的
5、接口隔离原则:各个类建立自己的专用接口,而不是建立万能接口
6、迪米特法则:无需直接交互的两个类,如果需要交互,使用中间者
过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低
7、合成复用原则:优先组合,其次继承
设计模式分类:
创建型模式
- 创建型模式关注点“怎样创建出对象?”“将对象的创建与使用分离”。
- 降低系统的耦合度使用者无需关注
-
- 对象的创建细节对象的创建由相关的工厂来完成;(各种工厂模式)
- 对象的创建由一个建造者来完成;(建造者模式)
- 对象的创建由原来对象克隆完成;(原型模式)
- 对象始终在系统中只有一个实例;(单例模式)
单例(Singleton)模式
一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。
特点
-
- 某个类只能有一个实例;(构造器私有)
- 它必须自行创建这个实例;(自己编写实例化逻辑)
- 它必须自行向整个系统提供这个实例;(对外提供实例化方法)
什么场景用到?
- 多线程中的线程池
- 数据库的连接池
- 系统环境信息上下文(ServletContext)
- ......
面试问题系统环境信息(System.getProperties())?
- Spring中怎么保持组件单例的?
- ServletContext是什么(封装Servlet的信息)?是单例吗?怎么保证?
- ApplicationContext是什么?是单例吗?怎么保证?
-
- ApplicationContext: tomcat:一个应用(部署的一个war包)会有一个应用上下文
- ApplicationContext: Spring:表示整个IOC容器(怎么保证单例的)。
- ioc容器中有很多组件(怎么保证单例)数据库连接池一般怎么创建出来的,怎么保证单实例?
- ......
原型(Prototype)模式
-
- 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。
- 本体给外部提供一个克隆体进行使用
什么场景用到?
- 资源优化
- 性能和安全要求
- 一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时可以考虑使用原型模式拷贝多个对象供调用者使用。
- 深(两个完全对象不一样的【递归克隆】,内容却完全一样)、浅(只是属性赋值)
- ..........
工厂(Factory)模式
工厂模式(Factory Pattern)提供了一种创建对象的最佳方式。我们不必关心对象的创建细节,只需要根据不同情况获取不同产品即可。难点:写好我们的工厂
工厂(Factory)模式-简单工厂(Simple Factory )
三个角色
- Factory:工厂角色,
- WuLinFactoryProduct:抽象产品角色,
- CarConcreteProduct:具体产品角色, VanCar、MiniCar
缺点:违背开闭,扩展不易
工厂(Factory)模式-工厂方法(Factory Method)
四个角色
- Product:抽象产品
- ConcreteProduct:具体产品
- Factory:抽象工厂
- ConcreteFactory:具体工厂
缺点:系统复杂度增加,产品单一
工厂(Factory)模式-抽象工厂(Abstract Factory)
- 如何实现,人民需要什么,五菱就造什么
- 硬编码抽象成简单工厂,简单工厂抽象成工厂方法,工厂方法抽象成抽象工厂
工厂模式的退化
当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。
什么场景用到?
- NumberFormat、SimpleDateFormat
- LoggerFactory:
- SqlSessionFactory:MyBatis
- BeanFactory:Spring的BeanFactory(就是为了造出bean)
- ......
建造者(Builder)模式
- 产品角色(Product):Phone
- 抽象建造者(Builder):AbstracPhoneBuilder
- 具体建造者(Concrete Builder):PhoneBuilder
- 创建的东西细节复杂,还必须暴露给使用者。屏蔽过程而不屏蔽细节
什么场景用到?
- StringBuilder:append(); 给谁append呢?
- Swagger-ApiBuilder:
- 快速实现。Lombok-Builder模式
- .......
编写代码的时候使用到了设计模式。类+模式
- xxxFactory
- xxxTemplate
- xxxBuilder
- xxxFacede
- ......
结构型模式
- 结构型模式关注点“怎样组合对象/类?”所以我们关注下类的组合关系
- 类结构型模式关心类的组合,由多个类可以组合成一个更大的(继承)
- 对象结构型模式关心类与对象的组合,通过关联关系在一个类中定义另一个类的实例对象(组合)
- 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。
适配器模式:两个不兼容接口之间适配的桥梁
- 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,适配器模式分为类结构型模式(继承)和对象结构型模式(组合)两种,前者(继承)类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
- 别名也可以是Wrapper,包装器
应用场景:
- Tomcat如何将Request流转为标准Request;
-
- tomcat.Request接口
- servlet.Request接口
- tomcat === CoyoteAdapte === ServletRequest
- Spring AOP中的AdvisorAdapter是什么:增强的适配器
-
- 前置、后置、返回、结束 Advisor(通知方法)
- 底层真的目标方法
- Spring MVC中经典的HandlerAdapter是什么;
-
- HelloController.hello()
- HandlerAdapter
- Servlet.doGet()
- SpringBoot 中 WebMvcConfigurerAdapter为什么存在又取消
- ......
桥接模式:相同功能抽象化与实现化解耦,抽象与实现可以独立升级。
- 将抽象与实现解耦,使两者都可以独立变化
- 在现实生活中,某些类具有两个或多个维度的变化,如图形既可按形状分,又可按颜色分。如何设计类似于 Photoshop 这样的软件,能画不同形状和不同颜色的图形呢?如果用继承方式,m 种形状和 n 种颜色的图形就有 m×n 种,不但对应的子类很多,而且扩展困难。不同颜色和字体的文字、不同品牌和功率的汽车
- 桥接将继承转为关联,降低类之间的耦合度,减少代码量
桥接(Bridge)模式包含以下主要角色。
- 系统设计期间,如果这个类里面的一些东西,会扩展很多,这个东西就应该分离出来
- 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
- 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
- 应用场景:
-
- 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
- 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
- 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
- InputStreamReader桥接模式。An InputStreamReader is a bridge from byte streams to character streams:
- InputStreamReader 桥接+适配器
过滤器模式:使用不同的标准来过滤一组对象
使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。结合多个标准来获得单一标准
组合模式:相似对象进行组合,形成树形结构
应用场景:
-
- 层级结构
- 部门组织结构
- 组合了别的对象还是组合模式吗?
- ......
装饰器模式:向一个现有的对象添加新的功能,同时又不改变其结构
- 适配器是连接两个类,可以增强一个类,装饰器是增强一个类
- 向一个现有的对象添加新的功能,同时又不改变其结构。属于对象结构型模式。
- 创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
- 应用场景
-
- 无处不在.....
- SpringSession中如何进行session与redis关联?HttpRequestWrapper
-
-
- session:数据存在了内存
- session:数据存在redis
- HttpSession;getAttribute();
- Wrapper(session){getAttribute(String param){ redis.get(param) };}
-
-
- MyBatisPlus提取了QueryWrapper,这是什么?
- Spring中的BeanWrapper是做什么?包装了Bean。bean的功能增强?
- Spring Webflux中的 WebHandlerDecorator?
- 已存的类,每一天在某个功能使用的时候发现不够,就可以装饰器。
外观模式:向现有的系统添加一个接口,客户端访问此接口来隐藏系统的复杂性。
外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式
应用场景:
- 去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便。以此类比......
- JAVA 的三层开发模式。
- 分布式系统的网关
- Tomcat源码中的RequestFacade干什么的?
- ......
享元模式:尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象
在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)用于存储具有相同内部状态的享元对象。
应用场景:
- 典型的代表:数据库连接池
- 所有的池化技术
- 享元和原型模式有什么区别?享元是预先准备好的对象进行复用,原型没法确定预先有哪些
- ......
代理模式:一个类代表另一个类的功能
代理模式(Proxy Pattern) ,给某一个对象提供一个代理,并由代理对象控制对原对象的引用,对象结构型模式。这种也是静态代理
-
- MyBatis的mapper到底是什么?怎么生成的?
-
-
- 动态代理
- UserMapper、CityMapper,mybatis帮我们写实现MapperProxy
-
-
- Alibaba Seata的DataSourceProxy是什么?
- DruidDataSource存在的Proxy模式
-
-
- 监控链...
-
-
- ......
- 装饰器、代理的区别:
-
- 装饰器和代理之间的区别很细微,可以认为装饰器是代理的一个子集。
- 静态代理就是装饰器的方式
行为型模式
- 行为型模式关注点“怎样运行对象/类?”所以我们关注下类/对象的运行时流程控制
- 行为型模式用于描述程序在运行时复杂的流程控制,
- 描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
- 行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
- 除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式
模板方法(Template Method)模式:父类定义算法骨架,某些实现放在子类
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
- Spring的整个继承体系都基本用到模板方法;
- BeanFactory.getBean(1,2,3,4)--A1---A2---A3---A4(全部被完成)
- JdbcTemplate、RedisTemplate都允许我们再扩展.....
- 我们自己的系统也应该使用模板方法组织类结构
- AQS
- ......
策略(Strategy)模式:每种算法独立封装,根据不同情况使用不同算法策略
- 策略(Strategy)模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。属于对象行为模式。
- 策略模式的主要角色如下。
-
- 抽象策略(Strategy)类:公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
- 应用场景:
-
- 使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句
- 什么是Spring的 InstantiationStrategy
- 线程池拒绝策略
状态(State)模式:每种状态独立封装,不同状态内部封装了不同行为
- 状态(State)模式:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
- 状态模式包含以下主要角色。
-
- 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
- 应用场景:
-
- 策略模式和状态模式是一样的?状态模式比策略模式多了个状态流转
- 状态模式核心需要具体状态类能在必要的时候切换状态
- 流程框架与状态机
- ......
命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开
- 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
- 命令模式包含以下主要角色。
-
- 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
- 具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
- 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
- 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
-
- 应用场景:
-
- mvc就是典型的命令模式
- 当系统需要执行一组操作时,命令模式可以定义宏命令(一个命令组合了多个命令)来实现该功能。
- 结合备忘录模式还可以实现命令的撤销和恢复
职责链(Chain of Responsibility)模式:所有处理者封装为链式结构,依次调用
- 责任链(Chain of Responsibility)模式:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。属于对象行为型模式
- 角色:
-
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
- Filter
-
- 应用场景:
-
- Tomcat的Pipeline、Valve
- Filter链
- Aop责任链
备忘录(Memento)模式:把核心信息抽取出来,可以进行保存
- 备忘录(Memento)模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。对象行为型模式
- 角色:
-
- 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
- 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
- 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
- 应用场景:
-
- 游戏存档
- 数据库保存点事务(savepoint)
- session活化钝化
解释器(Interpreter)模式:定义语法解析规则
- 解释器(Interpreter)模式:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。类行为型模式
- 角色:
-
- 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
应用场景:
- Spring的表达式解析:#{}
- Thymeleaf等模板引擎的语法解析
- 编译原理
- 编译器...
- execution(* com.atguigu..(int,..))
观察者(Observer)模式:维护多个观察者依赖,状态变化通知所有观察者
- 观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。对象行为型模式
- 角色:
-
- Subject: 目标
- ConcreteSubject: 具体目标
- Observer: 观察者
- ConcreteObserver: 具体观察者
应用场景:
- Spring事件机制如何实现?
- Vue的双向绑定核心
- 响应式编程核心思想
中介者(Mediator)模式:取消类/对象的直接调用关系,使用中介者维护
- 中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,减少对象间混乱的依赖关系,从而使其耦合松散,而且可以独立地改变它们之间的交互。对象行为型模式。
- 角色
-
- Mediator: 抽象中介者
- ConcreteMediator: 具体中介者
- Colleague: 抽象同事类
- ConcreteColleague: 具体同事类
应用场景:
- SpringMVC 的 DispatcherServlet是一个中介者,他会提取Controller、Model、View来进行调用。而无需controller直接调用view之类的渲染方法
- 分布式系统中的网关
- 迪米特法则的一个典型应用
- 中介者和外观(门面)模式区别?中介者双向操作,门面偏向于封装某一方
迭代器(Iterator)模式:定义集合数据的遍历规则
- 迭代器(Iterator)模式:提供一个对象(迭代器)来顺序访问聚合对象(迭代数据)中的一系列数据,而不暴露聚合对象的内部表示。对象行为型模式
- 角色:
-
- 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
- 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
- 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
- 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
-
- 应用场景:
-
- jdk容器接口的Iterator定义
- 现实开发中,我们几乎无需编写迭代器,基本数据结构链表、树、图的迭代器已经都有了。除非要重写迭代逻辑
访问者(Visitor)模式:分离对象结构,与元素的执行算法
- 访问者(Visitor)模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
- 角色:
-
- 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
- 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
- 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
- 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
-
- 应用场景:
-
- 在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
- 违反依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性
- 应用于对象结构相对稳定,但其操作算法经常变化的程序。
- Spring反射工具中的 MethodVisitor 是什么?