要分析常用框架spring、mybatis、springboot、springcloud等的源码,首先要了解各种设计模式,因为框架里面应用了各种设计模式
学习设计模式最重要的是掌握设计思想和设计原则,理解了设计思想和设计原则并运用到平时的编码中是最重要的!!!
天天加班编程,编程到底都做的是什么?
撸代码,加班撸代码,写接口、写类、写方法
为什么要做设计、用设计模式?
代码会变,为应对变化,为了以后方便扩展。做到以不变应万变,做一个会偷懒的程序员!

首先是从现实出发理清现实,在写代码之前先从实际分析,然后就开始写代码,写代码时要区分出不变的代码和会变化的代码,会变得代码会怎么变,使用者如何隔绝这种变化,所谓的隔绝这种变化就是不让调用者感知到内部的变化,只需要很简单的方式就能使用不必关心内部的逻辑,这样的话就要用到各种设计模式。不同的变化方式对应不同的设计模式。
设计的最终体现:如何来定义类、接口、方法


定义功能使用者和功能提供者间的接口
3)为什么要有接口?
隔离变化


1. 找出变化,分开变化和不变的
隔离,封装变化的部分,让其他部分不受它的影响。
2. 面向接口编程 ——隔离变化的方式
使用者使用接口,提供者实现接口。“接口”可以是超类!
3. 依赖倒置原则(里氏替换原则)——隔离变化的方式
依赖抽象,不要依赖具体类!
4. 对修改闭合,对扩展开放——隔离变化的方式
可以继承一个类或者接口扩展功能,但是不能修改类或者接口的原有功能
5. 多用组合,少用继承——灵活变化的方式
“有一个”可能比“是一个”更好。
6. 单一职责原则——方法设计的原则
每个方法只负责一个功能,不要把很多功能写在一个方法里面
应用设计模式的目的:
易扩展,易维护
少改代码,不改代码
示例:
京东、天猫双十一促销,各种商品有多种不同的促销活动:
满减:满400减50
每满减:每满100减20
数量折扣:买两件8折、三件7折
数量减:满三件减最低价的一件
……
顾客下单时可选择多种促销活动的其中一种来下单
后端代码中如何来灵活应对订单金额的计算?以后还会有很多的促销活动出现!
控制器OrderController.java
OrderService.java改怎么来写呢
营销活动有很多,这个switch会变得很庞大,不利于维护,并且很容易引入新的问题
把每个促销算法单独抽出一个方法,新加入一个促销活动只需要新增一个方法和case就可以了
这里利用了设计原则的方法设计原则:单一职责原则
但是这样写还会存在如下问题:
营销活动经常变,这个switch就得经常改,还得不断加促销的算法方法…….
改代码是bug的源泉,我们希望少改动OrderService!!!
分析:这里变的是什么?
促销的金额的算法!同一行为的不同算法!
我们不希望OrderService被算法代码爆炸!
同一行为的不同算法实现,我们可以用接口来定义行为,不同的算法分别去实现接口。
这里利用了设计原则:对修改关闭,对扩展开放!

这就是策略模式的应用!
策略模式的的定义:
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的用户而独立变化。

但是switch中的代码还是会不断变!!!switch中需要知道所有的实现!
如何让OrderService的代码不要改变?
把变的部分移出去!改怎么移呢?
这是简单工厂模式:所有产品由一个工厂创建
想要工厂中的代码也不要随促销的变化而变化,你觉得该怎么办?
方式一:promotion = beanName
把各种促销算法的实现交给spring容器来管理,用户选择的促销活动promotion 作为bean的名字,在PromotionCalculationFactory 工厂里面通过getBean("promotion")就能拿到各种促销算法的实现了
方式一的伪代码实现:
spring里面的bean配置:
PromotionCalculationFactory 工厂改写:
方式二: 配置promotion与实现类的对应关系
把用户选择的促销活动promotion和对应的促销算法的实现类放到map里面,或者存到数据库里面,在PromotionCalculationFactory 工厂里面通过map.get("promotion"),或者从数据库里面获取对应促销算法的实现类路径通过Class.forName("促销算法的实现类路径")就能拿到各种促销算法的实现了
方式二的伪代码实现:
PromotionCalculationFactory 工厂改写:
一个工厂负责创建所有实例。比如上面的策略模式中使用的就是简单工厂模式

根据传入的工厂类型参数String创建对应的实例(产品)
父类中定义工厂方法,各子类在+factoryMethod():Product方法里面实现具体的实例创建

使用者持有具体的工厂ChildAClass、ChildBClass、ChildCClass,传入对应的工厂ChildAClass、ChildBClass、ChildCClass创建对应的工厂实例
定义一个工厂接口,所有具体工厂实现工厂接口

使用者调用FactoryProducer的getFactory(type)方法传入type,type为AFactory、BFactory、CFactory对应的类型,就会返回对应的工厂AFactory、BFactory、CFactory,不需要传入AFactory、BFactory、CFactory,因为type已经跟AFactory、BFactory、CFactory绑定了。
示例:促销活动可多重叠加,该如何灵活实现订单金额计算?
OrderController
OrderService
装饰者模式的定义:以装饰的方式,动态地将责任附加到对象上。

说明:
不改变具体类代码(被装饰者ConcreteComponent),动态叠加增强行为功能。
若要扩展功能,装饰者提供了比继承更有弹性的替代方案
相较于前面的for循环,有何区别?
当需要对一个类的多个方法进行增强,使用者会随意使用被增强方法时,for循环就不够灵活了。
责任链和装饰者模式完成的是相同的事情。
装饰者模式-代码示例:
共同的需装饰的行为定义成接口
被装饰者实现接口Component
装饰者实现接口Component
装饰者派生出的装饰者
调用示例:
输出结果:
concrete-object + A
110
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

作用:不改变原类的代码,而增强原类对象的功能,可选择前置、后置、环绕、异常处理增强
代理模式的类图:

类图与装饰者模式一样,那么代理模式和装饰者模式有什么区别呢?
代理模式意在在代理中控制使用者对目标对象的访问,以及进行功能增强。装饰者模式意在对功能的叠加,比如对多种促销活动的叠加
代理模式有两种实现方式:
静态代理:由程序员创建或由特定工具自动生成代理类源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:代理类在程序运行时,运用反射机制动态创建而成。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
4.3.1 静态代理:
有一个土豪要找苍老师约会,他不能直接和苍老师约,需要经过一个中间代理Tony

需要被代理控制增强的行为定义成接口或者超类
代理和被代理的目标对象都要实现接口Girl
代理:
被代理的目标对象
土豪使用者
调用示例:
输出结果:
老板,这个我试过了,很不错,推荐给你!
身高可以,可以约!
老板,你觉得怎样,欢迎下次再约!
静态代理缺点:
扩展能力差
横向扩展:代理更多的类
纵向扩展:增强更多的方法
可维护性差

由于静态代理的扩展能力差、可维护性差,这就需要使用动态代理了!!!
4.3.2 动态代理
在运行时,动态为不同类的对象创建代理,增强功能。灵活扩展,易维护!
动态代理的实现方式:
JDK动态代理:只可对接口创建代理
CGLIB动态代理:可对接口、类创建代理
(1) JDK动态代理
在运行时,对接口创建代理对象

生成代理类$Proxy0的方法:
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:需要被代理的目标对象实现的接口,可以传入多个
InvocationHandler h:功能增强的接口
功能增强的接口:
参数说明:
Object proxy:被代理的目标对象(接口)
Method method:要调用的目标对象的方法
Object[] args:要调用的目标对象的方法的参数
被代理控制增强的行为生成接口:
Girl
Boy
被代理的目标对象
TeacherCang
TeacherChen
JDK动态代理
调用示例:
输出结果:
(2) cglib动态代理
cglib是什么?
cglib( Byte Code Generation Library),一个高层次的java字节码生成和转换的api库.

ASM:一个低层次的字节码操作库
它的主要用途
在运行期为类、接口生成动态代理对象。 以达到不改动原类代码而实现功能增强的目的
常在哪里用它?
常在 AOP、test、orm框架中用来生成动态代理对象、拦截属性访问
如何使用它?
1)引入它的jar
2)学习它的API
https://github.com/cglib/cglib/wiki
cglib动态代理-类图和API

说明:
实现思想和前面的JDK动态代理一样,只是使用了不同的API。
代理类由Enhancer生成,代理类实现被代理的类或者接口,特定的功能增强的实现MyMethodInterceptor实现MethodInterceptor接口,特定的功能增强实现MyMethodInterceptor里面持有被代理的类或者接口target
eg2:cglib动态代理-代码示例
被代理对象的接口:
被代理对象:
cglib动态代理主类:
输出结果:
http web请求处理,请求过来后将经过转码、解析、参数封装、鉴权......一系列的处理(责任),而且要经过多少处理是可以灵活调整的。
将所有的处理都写在一个类中可否?
不行
分成多个类如何灵活组合在一起?
责任链:所有的处理者都加入到这个链式,一个处理完后,转给下一个。
责任链模式具体实现步骤:
1)抽象出责任接口,具体责任逻辑实现责任接口。
2)根据处理过程需要,将具体责任实现逻辑组合成链
3)使用者使用链
典型代表:Filter(过滤器)、Intercept(拦截器)
责任链模式类图:

和装饰者模式的区别在哪里?
装饰者模式意在功能的叠加,责任链模式意在链式的处理
eg:责任链模式代码示例
抽象出责任接口:
具体的责任逻辑实现责任接口:
ResponsibilityA
ResponsibilityB
责任链:
ResponsibilityChain
请求接口:
调用者调用示例
输出结果:
使用者依赖的接口与提供者的接口不匹配时,就加一层适配,而不改两端的代码。
适配器模式类图:

说明:
使用者使用Target接口,但是提供者Provider又没有实现Target接口,这个时候就需要加一层适配Adaper,Adaper里面持有Provider,在Adapter的methodA()方法里面调用Provider的methodB方法
和代理、装饰的区别在哪里?
适配器模式不进行功能增强
使用方要完成一个功能,需要调用提供方的多个接口、方法,调用过程复杂时,我们可以再提供一个高层接口(新的外观),将复杂的调用过程向使用方隐藏。
外观(门面)模式类图:

这里使用了设计原则:最少知识原则(迪米特原则)
观察者模式类图:

说明:
主题Subject面向观察者接口Observer编程,主题里面可以添加、删除和通知观察者Observer;
注意每个观察者都有一个回调方法update,如果有变化就会在主题的notifyObservers()方法里面调用update方法,把最新的变化给到观察者
变化之处:观察者会变,观察者的数量会变。
不变:主题的代码要不受观察者变化的影响。
观察者模式定义:
定义了对象之间一对多的依赖关系,当一端对象改变状态时,它的所有依赖者都会收到通知并自动更新(被调用更新方法)。也称为:监听模式、发布订阅模式。提供一种对象之间松耦合的设计方式。
设计原则:为了交互对象之间的松耦合设计而努力!
Java.util. Observable 可被观察的(主题),具体主题扩展它。
java.util.Observer 观察者接口,具体观察者实现该接口。
主题Observable:

观察者接口Observer

使用代码示例:
输出结果:
示例:
请为你的系统设计一个命令行界面,用户可输入命令来执行某项功能。
系统的功能会不断增加,命令也会不断的增加。
如何将一项一项的加入到这个命令行界面?

如何让我们的命令程序写好以后,不因为功能的添加而修改,又可灵活的加入命令、功能。
命令模式类图:

命令模式的定义:
以命令的方式,解耦调用者与功能的具体实现者,降低系统耦合度,提供了灵活性。
适用场景:Servlet、Controller、线程池
命令模式伪代码示例:
命令模式与策略模式的区别:
命令模式类图:

策略模式类图:

区别:
策略模式侧重的是一个行为的多个算法的实现,可互换算法。
命令模式侧重的是为多个行为提供灵活的执行方式
示例:一个类对外提供了多个行为,同时该类对象有多种状态,不同的状态下对外的行为表现不同,我们该如何来设计该类,让它对状态可以灵活扩展?
如请为无人自动咖啡售卖机开发一个控制程序。

说明:用户可以在咖啡机上进行支付、退款、购买、取咖啡等操作
咖啡机状态转换图:

说明:
不同的状态下这四种操作将有不同的表现。如在没有支付的状态下,用户在咖啡机上点退款、购买、取咖啡,和在已支付的状态下做这三个操作。
普通实现:
如何让状态可以灵活扩展?
从分析可以看出,变化的是状态,同时不同的状态同一个行为的表现不同,这样的话就可以把变化的状态抽象生成接口,然后不同的状态行为实现状态接口做该状态下的具体行为。这里可以采用状态模式
状态模式类图:

状态模式代码示例:
把变化的状态抽象生成接口State,里面含有不同状态下的行为方法
不同的状态实现状态接口State
没有支付状态
已支付状态
售出状态
售罄状态
使用了状态模式的咖啡机
状态模式、命令模式、策略模式的区别
状态模式类图:

命令模式类图:

策略模式类图:

区别:
状态模式应用于状态机的情况
策略模式侧重的是一个行为的多个算法的实现,可互换算法。
命令模式侧重的是为多个行为提供灵活的执行方式
请开发一个画图程序,可以画各种颜色不同形状的图像,请用面向对象的思想设计图像
分析:
1)比如有红、黄、蓝三种颜色
2)形状有方形、圆、三角形
3)圆可以是红圆、黄圆、蓝圆

变化:
会从两个维度发生变化:形状、颜色

任其在这两个维度各自变化,为这两个维度搭个桥,让他们可以融合在一起:桥接模式
桥接模式的实现步骤:
1)抽象:分别对各自的维度进行抽象,将共同部分抽取出来

2)组合:将抽象组合在一起(桥接)

桥接模式的定义:将多个维度的变化以抽象的方式组合在一起。使用者面向抽象。个维度间解耦,可自由变化。
饥汉式1——可用
饥汉式2——可用
懒汉式1——不可用
当两个线程同时进入if里面时就会创建两个实例,不是单例,线程不安全,所以不可用
懒汉式2——不推荐使用
线程安全,但不推荐使用。缺点是实例化后就不应该再同步了,效率低
懒汉式3——不可用
当两个线程同时进入if里面时就会产生两个实例,做不到单例
懒汉式4——双重检查——推荐使用
注意:volatile关键字修饰很关键,保证可见性,一个线程先创建了,其他线程就就会看到这个改变,不会再创建,如果没有这个关键字还是不能保证单例。
优点:线程安全;延迟加载;效率较高
懒汉式5——静态内部类方式——推荐使用
优点:避免了线程不安全,延迟加载,效率高
原理:类的静态属性只会在第一次加载类的时候初始化。在这里,JVM的加载机制帮助我们保证了线程安全性,在类进行初始化时,别的线程是无法进入的
懒汉式6——用枚举——推荐使用
示例:
当我们设计一个类时,我们能明确它对外提供的某个方法的内部执行步骤,但一些步骤,不同的子类有不同的行为时,我们该如何来设计该类?
可以用模板方法设计模式

优点:
1)封装不变的部分,扩展可变的部分
2)提取公共代码,便于维护。
3)行为由父控制,子类实现。
适用场景:
1)有多个子类共有的方法,且逻辑相同
2)重要的、复杂的方法,可以考虑作为模板方法
模板方法设计模式代码示例:
设计模式总结
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用新的运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
- 工厂模式(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)
设计原则总结
1. 变化隔离原则:找出变化,分开变化和不变的
隔离,封装变化的部分,让其他部分不受它的影响。
2. 面向接口编程 ——隔离变化的方式
使用者使用接口,提供者实现接口。“接口”可以是超类!
3. 依赖倒置原则(里氏替换原则)——隔离变化的方式
依赖抽象,不要依赖具体类!
4. 开闭原则:对修改闭合,对扩展开放——隔离变化的方式
可以继承一个类或者接口扩展功能,但是不能修改类或者接口的原有功能
5. 最少知道原则,又称迪米特法则
6. 多用组合,少用继承——灵活变化的方式
“有一个”可能比“是一个”更好。
7. 单一职责原则——方法设计的原则
每个方法只负责一个功能,不要把很多功能写在一个方法里面

说明:如果前面的设计思想和设计原则都忘记了,就要找出变化,区分出不变的和变化的,把变化的部分独立出接口,或者使用组合
示例代码获取地址:
https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/design-mode-study
参考文章:
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/3066.html