观察者模式
设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)
定义
角色
使用场景
缺点
Java实现
总结
定义当一个对象状态发生改变时,依赖它的所有对象都会得到通知并通知自动更新
角色抽象主题(Subject):也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
具体主题(ConcreteSubject):也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
抽象观察者(Observer):它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
具体观察者(ConcreteObserver):实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
使用场景1、存在一对多的对象关系,一个对象改变影响其他对象
2、广播机制。如邮件订阅,公众号订阅、up的关注等
缺点在观察者过多的时候,通知效率会降低
Java实现uml类图,可以看到具体目标对象与观察者的耦合很小
如何调用
...
迭代器模式
设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)
定义
为什么需要
常见写法
角色
场景举例
总结
定义提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示
为什么需要迭代器模式在我们遍历元素的时候,不能将内部元素的数据结构(如数组,集合)暴露给用户。需要保证数据安全性。其次也增加了用户的负担,它原本也不需要知道内部结构
常见的迭代器写法123456List<String> list = new ArrayList<>(); Iterator iter = list.iterator(); while(iter.hasNext()){ String str = (String) iter.next(); System.out.println(str); }
日常开发中不需要重复造轮子,框架给的API就够用了,自己模拟实现迭代器实现如下
12345MyIterable<Student> list = new ConcreteIterabl ...
访问者模式
设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)
这个模式比较抽象,用到了双分派技术,比较复杂
定义
使用场景
角色
场景举例
总结
定义封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于其内部各个元素的新操作
使用场景类结构比较稳定,不会经常增删不同类型的元素。而需要经常给这些元素添加新的操作的时候,考虑使用此设计模式。(元素不变,操作变)
角色ObjectStructure
封装元素用的结构,可以操作或访问之中的元素
Visitor
访问者,接口,定义元素的方法
ConcreteVisitor
访问者实现类
Element
结构中的元素
ConcreteElement
元素实现类
场景举例王老板开了家公司,招了小李,小雷。打算疯狂压榨打工人。要求做前端,后端,测试,部署,产品销售等等,而且近期不准备招新人。
这种就符合元素不变(就一个人)而操作经常变(一会儿干这,一会儿干那的)
下面进行实现
这么一家有爱的公司,就叫它有爱公司
是这么调用的
123456//创建有爱公司LoveCompan ...
责任链模式
设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)
将请求发送到链条上,只关心它传给谁,不关心传递过程与处理细节,将请求者与处理者进行解耦
定义
优点
缺点
角色
场景模拟
定义为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
优点解耦,可扩展,灵活,符合单一职责
缺点不能保证每个请求一定被处理,请求没有明确的处理者
如果链子过长,会影响系统性能
搭链子需要靠客户端完成,增加了客户端的复杂性,如果搭错,可能形成环
角色抽象处理者(Handler):定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
具体处理者(Concrete Handler):实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
客户类(Client):创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
场景模拟王老板的下属打算请假,王老板给指定了一个请 ...
谈谈前后端分离
前后端分离,被网上抄的如火如荼。但真的有网上吹的那么神吗?还是为了追新而追新,为了分离而分离?事实上,我开发过的项目并没有感觉到给我带来了多少便利。总之,前后端分离,人不分离。分离后前端对之前的要求更高了,只会前端三剑客是远远不够的。这就有了学习成本,还得处理耦合,跨域什么的,所以让我感觉到并不是那么便利。
一提到前后端分离,脱口而出的就是一些技术,比如spring,vue,react,node,有了它们我们才可以去进行前后端分离。但是仔细想想,是技术导致分离吗,还是需要分离才有了技术。显然它并不是一个先鸡后蛋的哲学问题。
万恶之源jsp遥想当年,我们小团队主要是后端为主,作为项目组的攻坚核心力量,也在干着前端的活。当时也有专门的前端,但不多,也叫“切图仔”,主要帮助后端搜集一些模板和处理一些图片资源。当时前端要求不高,web化趋势也不明显,jsp就好似其汽车中五菱宏光,发挥着不可估量的作用。后端也被奉为一个有技术含量的岗位。但从2020年初开始,甲方逐渐变态,需要前端展示的东西也复杂起来,jsp弊端也逐渐放大,模板套模板,东拼西凑,没有规范,导致开发人员经常扯皮。从项目管理的角度看, ...
八大经典排序图解
排序的种类
时间频度和特点
时间复杂度
冒泡排序
选择排序
插入排序
希尔排序
快速排序
归并排序
基数排序
堆排序
以下只给出思路与关键方法,算法的源代码放在了git中,需要的自取
leidl97/algorithm-src
排序的种类排序分为内部排序和外部排序
一般为内部排序,可以划分为8大排序
插入排序:直接插入 | 希尔排序
选择排序:简单选择 | 堆排序
交换排序:冒泡排序 | 快速排序
归并排序(分治算法)
基数排序(桶排序)
时间频度和特点1、常数项可以忽略
2、低次方项可以忽略
3、次方项的系数可以忽略
时间复杂度1、一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大的时候,T(n) / f(n)的极限值为不等于0的常数,则称f(n)是T(n)的同数量级函数,记作T(n) = O(f(n)), O(f(n))称为算法的渐进时间复杂度,简称时间复杂度
2、T(n)不同,但时间复杂度可能相同
计算方法用常数1表示运行中所有加法常数
只保留最高阶项
去掉最高阶的系数
常用的时间复杂度由小到大 ...
查找
Java中常用的查找有
线性查找
二分查找/折半查找
插值查找
斐波那契查找/黄金分割点查找
都是比较简单的,除了斐波那契查找
以下只给出思路与关键方法,算法的源代码放在了git中,需要的自取
leidl97/algorithm-src
二分查找思想
必须为有序的,可以正序,也可以倒序
每次切一半,找到返回找不到向左右继续查
先按照给定数据的一半找起,如果目标值比mid大,则向右递归,反之,向左递归。
优化:可以查找重复数据,在找到元素之后,向左右依次进行比较,查找所有相同的元素
需要注意的是边界值问题 left必须为<=而不是<
代码实现
123456789101112131415161718192021222324252627282930private static void search(int[] arr,int left, int right, int act) { if (left <= right) { int mid = (left + right) / 2; if (arr[mid] > ...
图
为什么需要图
图的相关概念
图的表示方式
DFS(深度优先遍历)
BFS(广度优先遍历)
最小生成树之prim算法
最小生成树之KrusKal算法
最短路径之Dijkstra算法
最短路径之Floyed算法
以下只给出思路与关键方法,算法的源代码放在了git中,需要的自取
https://gitee.com/leidl97/algorithm-src.git
一、为什么需要图?处理多对多
二、图的相关概念顶点
边
路径
无向图—顶点之间没有方向的图
有向图—顶点之间有方向的图
带权图—每个路径有值的图,也叫网
三、图的表示方式二维数组和链表,也可以称为邻接矩阵和邻接表
可以看到0表示不是直接相连,有很多边都是不存在的,会造成空间上的一定损失
邻接表的话只关心存在的边,因此没有空间浪费,邻接表由数组+链表组成
图的遍历方式有两种,一种是DFS(深度优先)另一种是BFS(广度优先)四、DFS(深度优先遍历)depth first search
重点在于递归下去,回溯上来
难点
1、判断遍历的条件
for循环遍历二维数组,如果为1且未使用过,则将下个顶点继续递归
精髓
1、用任意一个 ...