一
老规则边看边写书上的代码,磨磨蹭蹭三个多星期终于把一本1000+的java与模式看完了。
于是,在这里贴上自己对每个模式的思考和总结,其实这个东西在我边看边写的时候已经写了一大半,博文再写一次算是复盘和分享。
而且由于是总结所以不贴代码,有兴趣的可以直接看我的项目,里面自带博文全部中文,因为也是边写变更新一遍github。
附上自己的项目地址 https://github.com/247292980/Design-Patterns
必须要说的是本人其实大二开始敲代码,目前工作不满一年,一定有很多不足,甚至一些想当然的看法,所以有错请各位指出。
还有这书虽然是02年的时候的书,但是看完了之后,你会感慨经典不愧是经典
提醒一下,本文的主要作用是对各种模式过一下,适合有相当的编程基础的人看,下面所说的仅起到一个提示的作用。
代码分成几部分都是作者的原文,虽然我们不是这么叫,但了解一下规范还是有益处的。
二
模式这东西我一直以为只要知道设计模式就好了,但是看了之后,才明白大家通常说的设计模式其实并不是名词而是动词。
模式细分应该有三种 结构模式 创建模式 行为模式。当然,我们也不要做杠精,直接把大家说的设计模式当成模式就好了。
三 创建模式
简单工厂模式
很简单,代码分为三部分
工厂类 生产者
实体类 具体产品
接口类 抽象产品
工厂方法模式
代码分为4部分和简单工厂的区别就是多了一类
工厂接口类 抽象生产者
工厂类 具体生产者
实体类 具体产品
实体接口类 抽象产品
这里有一个变种,利用接口多实现原理,去掉工厂类,具体产品实现抽象生产者接口,抽象产品接口,代码更加美观!!
逻辑就是不太好理解就是了
抽象工厂模式
代码分为4部分和工厂方法模式一样
工厂接口类 抽象生产者
工厂类 具体生产者
实体类 具体产品
实体接口类 抽象产品
和工厂方法模式的区别在于,他的抽象产品有两个,也就是说这个模式支持多个类型的产品
虽然书上说,不改变原有代码就能balabala,但是这个代码的缺陷是加新的工厂当然不修改原有的代码,那么如果加新的抽象产品呢?其他工厂,有些有产品有些无呢?
那就抽象生产者加default方法,去掉具体生产者,产品继承抽象生产者,加新的产品必须要在接口加新的default方法。缺点就是一个类多了很多不必要的方法
所以说怎么不修改原有的代码呢?
好像总有这样那样的缺陷啊,其实很简单,新的具体生产多继承一个新的抽象接口...之后通过代码迭代,慢慢合并即可
饿汉单例-加载就创建单例
饿汉这名字真的不知道谁取的,我是倾向于叫他 加载就创建单例,或者叫普通单例
设计模式这些坑就在于,明明已经没有人用,依然有面试官问这些问题,意义不明啊
懒汉单例-使用才创建单例
曾经我觉得懒汉比饿汉差,因为饿汉至少为了吃东西会动,懒汉动都不动啊,然而我是错的,因为没有优劣之分只有使用的适合度之分。
所以我为了不记混,叫做使用才创建单例,同理叫饿汉 加载就创建单例
很多人都说饿汉比懒汉好,可以说是错的,也可以说是对的。
错的原因:因为他们都说是并发的情况下,然而并发的情况下用这两个的都是傻缺,应该用他们的变种并且具体问题具体分析,应该用他们的变种并且具体问题具体分析,应该用他们的变种并且具体问题具体分析,很重要说三次!
所以有人问并发的情况下那种好,你应该分析一波,说具体问题具体分析,但是并发下饿汉更好
对的原因:饿汉懒汉的性能区别是多对象少调用 用饿汉;少对象多调用 用懒汉;解释下就是多对象调用时,触发了懒汉的getInstance的同步方法,性能下降所以此时饿汉更好。
实际上,现在的程序运行时间不断拉长,加载时创建的消耗可以忽视...从这角度讲饿汉确实是比懒汉好。
双重检查已经被人说了很多次在java一点用都没有的,然而百度第一的csdn那篇文章依然有说双重检查
老铁,java与模式是02年的书啊,csdn的博客02年还没出生,是梁静茹给你的勇气写的吗?底下评论也没几个说到这问题,初次看到的人基本都被带歪了
登记单例类
优点克服懒汉饿汉不能继承的缺点
缺点是子类的构造方法必须是公开的,也就是存在不通过登记单例类,生成实体;登记单例类必须存在才能登记子类的单例类,这在有些情况下是种浪费
这里提一下这种的一个变种 只有父类没有子类,很多东西都登记到map里面。
这种写法在早期安卓国内起步阶段,15年的广州安卓很常见,估计是哪个培训或者大神的手笔。
说真的一点问题都没有,但是安卓到了后期有各种各样的内存调优问题,这类的代码就很可怕了,大家只管放进去而不管gc,无限增大
而且安卓上本来就没什么资源并发的问题,这玩意他们是拿来当消息传递的,结果就是消息越来越多,现在开发安卓的看到这个模式要留心前任的坑啊
多例模式
定义实体,有数量限制
建造模式-多个建造方法建造一个产品
Builder 建造者 抽象类,建造方法和返回产品的方法
ConcreteBuilder 具体建造者
Director 导演,具体建造者的调用
Product 产品
返回产品的方法若是固定的可写个默认方法,按书中所说默认方法的情况更为普遍。 但是实际使用,我的写法不仅有默认方法,还把具体建造者 和 具体建造者的调用 合并。这里那里好我就不说了,因为使用的情况不多,看过mybatis的源码发现默认方法写不写,其实很有可能是一个代码习惯。个人感觉写默认方法是个好习惯?求大佬解决疑问。
变种1
具体建造者的调用 导演里的成员可以直接是具体调用者。 这也是书中所说的大多数情况。
可以看出java于模式的作者也觉得建造者模式的实际使用其实一直都是他的阉割版
而单例,多例,工厂这些模式都是扩展板,哪个比较重要,实用。很明显了啊。建造者模式可以说了解即可了。
变种2
具体建造者 和 具体建造者的调用 合并。在返回产品的方法调用建造方法即可
变种3
产品也搞成抽象的,就能返回多种产品
变种4
具体建造者 和 导演 和 建造者合并
ps3.看到这里的时候,我真的想说,建造者模式存在的意义?
建造者有多个产品构造方法,生成不同的产品,,,,简单点的例子就是不同参数的构造方法...
重点:作者认为
1.需要生成的产品有复杂的内部结构。产品成员有多个对象,对象里面的成员也有多个对象
2.生成的产品的属性相互依赖,具体就是connect必须有ip,port什么的赋值
最后,思维发散的同学也应该发现了,建造者模式的变种不止以上四种
还有一种常见的
连接mq,数据库等的
Connect conn;
conn.setPort();//之类 本质上就是一种建造者模式的建造方法
Channel chan=con.getChannel();//则是生成产品类...
也就是说返回数据的方法的数据结构,包含多个数据都能说是建造者模式的变种....
完美契合作者的认为啊
那么,看起来作者并没有黑建造者模式,而是把建造者模式提炼的很精简!我喜欢!
原型模式-用于对象的复制
client 客户,具体原型的调用
prototype 原型,实现cloneable接口
concreteprototype 具体原型,被复制的对象
然而,我对象的复制大都自己写一个方法,后面则是直接用了工具类copier
在后面spring有个cglib包,所以在我看来也是过时的...包括后面的登记原型模式
登记原型模式-还是用来复制对象?感觉这个没什么了解的必要啊...
client 客户,具体原型的调用
prototype 原型,实现cloneable接口
concreteprototype 具体原型,被复制的对象
prototype manager 具体原型的管理器,记录每一个被创建的对象
23333,看了下代码,登记进去了,怎么取啊老铁?
这可能是我看的java与模式的书的问题?作者出了个大bug啊!
强烈建议看一下我的CopyUtil,现在看一下我当年竟然用登记原型模式封装了cglib的工具类,,,,
这里书上说了深复制和浅复制。浅复制就是clone了,深复制就是变成字节流再变回来。
区分就是复制前后的对象里面的成员指向的不是同一个
四 结构模式
类的适配器模式
Target 目标,所期待要得到的接口
Adaptee 源,目前已有的接口
Adapter 适配器,实现目标接口的类
其实就是,adaptee 老文件 ,Adapter 新文件,迭代开发很常见了
对象的适配器模式
就是继承类变成内部的一个成员对象,这种模式更加的符合适配器的使用
因为老的文件adaptee的方法名不一定和target的方法名一致
默认适配器模式
其实就是写个实现了所有接口方法的适配器,然后子类覆盖某些方法
具体区别是
类的适配器模式,本来只有老文件,后来为了增加功能,才有target接口和adapter类,本质上是一种迭代
但是他的变种 属性的适配器模式变成了一开始三个文件都写进代码里的,缺点是这样不好增加新的方法,不太灵活
然后为了更好的增加新的功能就有了默认适配器模式,通过新建子类来增加
安全型的合成模式
component 给参与组合的对象定义接口
leaf 参加组合的对象,实现了component的方法
composite 合成,不仅实现了component的方法,还有管理其他leaf的方法
和原始模型很像,但是一个是有对象有方法,这个则只有方法
透明的合成模式
component 给参与组合的对象定义接口,透明的包括管理的方法接口
leaf 参加组合的对象,实现了component的方法
composite 合成,实现了component的方法
注意。leaf实现的管理方法应该是空的,平庸实现,也就是什么都不做。
装饰器模式
component 给参与的对象定义接口
concreteComponent 参加的对象,定义一个将要接受附加责任的类
Decorator 装饰器,持有一个component的实例,并定义一个和接口一致的方法
concreteDecorator 具体装饰器,负责给对象贴上附加的责任
作者有一句话,他把装饰器模式称作 包裹器模式,我看到之后,作者这句话真的很形象。
装饰器的使用情况
需要扩展一个类,或给一个类加附加的责任
动态地对一个对象加功能,并可动态的撤销
需要一些基本功能能的排列组合而产生大量的工能,
ps.
其实就是把通用功能抽象出来,而且这些功能必须是大量被特定对象使用的,好像开发没遇到这种情况啊...
当然框架里面有各种各样的base接口,现在想想应该就是装饰器模式了。
ps2.
装饰器通过换装饰来保持核心的不变,策略则通过换策略使得核心改变。
代理模式
Subject 给参与的对象定义接口
proxySubject 代理的对象,内部有真实的对象的引用。并有自己的操作(如数据校验
realSubject 真正的对象,定义了对象必须完成的操作。
其实就是真正的对象必须完成的操作不变,代理的对象的方法可变,例如注册的数据监测之类的,或者空值不传之类的
代理模式很好理解啊,因为书中有很多例子,个人觉得讲的很好
亨元模式
Flyweight 给参与的对象定义接口,用状态state的操作必须实现
concreteFlyweight 具体的亨元对象,用状态state的操作,必须确保与周围环境无关,也就是不改变值,仅用于判断
FlyweightFactorySingleton 亨元工厂,管理创建亨元
我想起了switch...
复合的亨元模式
Flyweight 给参与的对象定义接口,用状态state的操作必须实现
concreteFlyweight 具体的亨元对象,用状态state的操作,必须确保与周围环境无关,也就是不改变值,仅用于判断
FlyweightFactorySingleton 亨元工厂,管理创建亨元
concreteCompositeFlyweight 复合亨元,本身是不可共享。由多个亨元对象组成
亨元模式看下来,只能说我是个小白.书里面其实还有个client但是没有相应的代码
平时写和看的框架源码都没有见过使用亨元的,应该水平太低我没看出来
单例亨元模式
Flyweight 给参与的对象定义接口,用状态state的操作必须实现
concreteFlyweight 具体的亨元对象,用状态state的操作,必须确保与周围环境无关,也就是不改变值,仅用于判断
FlyweightFactorySingleton 亨元工厂单例,管理创建亨元
Client 维护亨元的状态,使用亨元对象的对象
终于知道client是什么鬼了,就是一个使用亨元对象的对象,,,
单例的复合亨元模式
Flyweight 给参与的对象定义接口,用状态state的操作必须实现
concreteFlyweight 具体的亨元对象,用状态state的操作,必须确保与周围环境无关,也就是不改变值,仅用于判断
FlyweightFactorySingleton 亨元工厂单例,管理创建亨元
Client 维护亨元的状态,使用亨元对象的对象
当你理解不能的时候,就写代码吧,写了四个差不多的代码之后,感觉我懂了亨元模式
本质上就是map的存储管理,根据复合亨元的char型做key可以看出可能就是大类拆小类,用小类保存?
那么有个什么用啊???
感觉和一些命令 如 nginx -t 这些-t的运行args的实现有点类似,可是我没看过这方面的源码,求大神解答?
ps.
亨元模式书上有两个例子 coffee,看完了之后,我一脸懵逼,为什么要用亨元模式?
然后下一章说 亨元模式的使用条件
1.有大量的对象
2.这些对象不停的新建
3.这些对象大部分固定不变
4.这些对象可以有很多个分组
5.系统不依赖这些对象,也就是要有个default
emmmm,一个饮料表格,有多个属性 可以根据 冷热字段,酸甜字段,地区字段,颜色字段来分组。。。
我们只需要运行时从数据库取数据创建一个单例,使用时实例化即可....
或者使用时先取单例,单里没有取数据库,数据库没有用default什么的....
emmm,亨元模式或许要先在高并发,分布式的环境下是绝对用不上的吧????
ps2.
蛋疼的是看完了。理解了亨元模式之后,02年的书的作者很贴心的说一句,亨元模式不是一个常见的模式哦亲。我真的日狗了...
个人感觉 亨元模式的核心就是状态,也就是代码里的state,,,简单点说就是map的key所以...
我又想起了switch...
门面模式
facade 门面,提供接口
subsystem 子系统
举个例子
controller提供接口里面有用到很多个service
桥梁模式
Abstraction 抽象的对象,并保存一个实现类
RefinedAbstraction 扩展Abstraction,改变或修正父类的操作
Implementor 给参与的对象定义接口
ConcreteImplementor 具体实现类
就是覆盖了方法,没什么好说的
五 行为模式
不变模式
额例子,没有,书直接用源码说
不变模式其实名字很明显了,模式的值不变的话,引用的变量名再怎么不同,都是一样的
final 什么的 枚举什么的 接口常量什么的 大家都懂拉
要是要搞底层实现的话,你就不是看这本书了...
策略模式
context 环境,持有策略类的引用
strategy 策略,定义一个抽象接口
ConcreteStrategy 具体策略
没什么好说的,就是java的多态
ps.
看完了策略模式,觉得书中讲的真的简单,
但是由于之前搞过两三个月的人工智能相关的规则引擎。
里面很重要的一点就是策略,策略很重要的两点就是开闭原则-可扩展性,里氏替换原则-可替代性。
就是写新的策略不影响旧的策略的使用,新的策略使用的数据,进到旧的策略应该不报错
简单点数开闭原则,不修改其他策略对象的依赖,里氏替换原则,入参出参和老的模式一致
ps2.
人工智能的第一步!
人可以通过配置修改策略,甚至选择什么策略!
当时看了下,网上很多人工智能其实真的很垃圾,顶天能做到策略模式就已经很好了,所谓的算法,也就是策略模式之中的一个策略,而国内主要还是在做这一个
所以,大家不要觉得人工智能很难,大学教人工智能的那批人真的很垃圾,我有个校友人工智能的毕业设计,就是画个椭圆然后动态显示各种参数
当然,你们肯定觉得热更新就能实现这功能,确实。所以说现在的人工智能真的是起点低
模板模式
AbstractTemplate 抽象模板 定义几个方法,或实现或让子类实现
ConcreteTemplate 具体模板,实现父类没事闲的方法
日常和策略模式结合使用
ps.
重构的原则
行为向上,状态向下
就是方法靠向父类,状态值具体到每个子类!
ps2.
里面有个命名规范
模板模式里,留给子类的方法名应该用do开头,emmm他不说我还真不知道...
观察者模式-我这边倒是说监视者模式,监听者模式,大家都是用listener而不是observer
observer 抽象观察者,只定义一个方法update,java内部有实现的import java.util.Observer;
subject 抽象对象,定义了被观察者要做什么
ConcreteObserver 具体观察者,实现update
ConcreteSubject 具体对象,将有关状态保存,在对象改变的时候给观察者通知
ps.
安卓的mvp模式,早期的Presenter我们都是用listener,算是java开发安卓带过去的陋习了,不过这几年安卓的完善以及自成一圈,也不会出现这种情况了。
view的操作,通过listener监听,update方法走http或者socket连接服务器
这是安卓已经过期的思路,我只是举个例子,后面mvp模式也发展出谷歌MVP模式和非谷歌mvp模式,这里就不提了,我也只是用过非谷歌mvp模式
(这里我说的话应该过期了,安卓的大佬不要当真
变种1
把向量往上提到subject,subject改成抽象类。具体的对象只负责状态的操作不负责观察者管理,观察者根据状态update执行相应操作
越看越像当年的安卓开发,对view的监听状态是操作码,对服务器返回的监听的状态是返回码...当年的老大是按这本书来设计的吧?
想想这种代码,现在随随便便就能反编译啊
ps2.
观察者模式java内部就有实现 observerable类和observer类...
ps3.
后面有几章就是说开发java awt gui的,果然当初的安卓开发框架基本就是照搬这几章的思路,好东西永远不会过时啊
例子很好,建议这里看一下书
定时器本质上是一个轮询的监听器啊
例子很好,建议这里看一下书
当年的书还顺道告诉你怎么hack网站,,,开拓道路的人必定在法律的边缘吗?
例子很好,建议这里看一下书
迭代子模式
Iterator 迭代子 此抽象角色定义出遍历元素所需的接口
ConcreteIterator 具体迭代子 实现Iterator接口,并保持迭代过程中的游标位置
Aggregate 聚集,定义 创建Iterator的接口
ConcreteAggregate 具体聚集 返回一个具体的聚集
client 客户 通过聚集操作迭代子
书中说,优点是不用遍历,因为有遍历方法已经实现了!
23333写了1堆你说少一个for循环的事
ps.
后面看了一堆例子,发现java用了迭代子模式的数据类型由于经常被我们使用,所以我们才觉得他是一个for的事
但是没有实现他的数据模型可能会各种不方便吧?润物细无声型模式啊。
责任链模式
handle 处理者 定义一个处理请求的方法,还有返回设置责任链下家的方法
concretehandle 具体处理者 本身的请求以及下家的请求
ps.
处理者带上状态值之后配上switch可自定义执行顺序,也就是和亨元的状态值结合
这里有一些链表的基础,很容易理解。
令行模式
client 创建一个具体的命令对象concretecommand 并确定其接受者
command 声明一个所有具体的命令的抽象接口
concretecommand 具体命令定义一个接受者和行为之间的弱耦合,实现执行方法
invoker 请求者 负责调用命令对象 执行请求
receiver 负责具体实施和执行一个请求
降耦
备忘录模式
Originator 发起人,创建一个含有当前状态的备忘录角色
Memento 备忘录,将发起人的状态保存
caretaker 保存备忘录的角色
不知道这玩意的意义在哪,然后看了书上的例子,双重接口,对象不暴露,数据的回滚什么的。。。
回到首页?然后想想,真的挺感谢当年写过安卓,因为现在回想起来全是知识点...
一个framelayout,里面显示什么view放到一个类里,然后按照这个类回退或者返回主页面什么的,妥妥备忘录模式啊!
ps.
这几年n多app返回键直接退出app可以看出培训出来的水准还不如当初吃螃蟹的那批人啊
ps2.
例子里面改一下,就是我说的安卓的view的保存...真神书也!
状态模式
state 状态 用于封装对象一个特定状态的行为
ConcreteState 具体状态,实特定行为的方法
context 环境 ,提供设置状态的方法和调用相关状态的特定行为
例子真心易懂,用户登录,管理员登录等等,我相信是个开发都做过管理系统吧,简直是一看例子名字就知道这模式的使用啊
访问者模式
visitor 抽象访问者 声明一个或多个访问操作
Concretevisitor 具体访问者,实现访问接口
node 抽象节点 生命接受操作,并用访问者做参数
concretenode 具体节点 实现抽象元素所规定的的接受操作
ObjectStructure 结构对象,保存所有访问对象,并进行管理
委派 是参数同一个对象,不同类的方法具体不同
分派 是不同类的方法的参数一致,该方法相同,具体方法是参数的方法
访问就是合成模式加上面两个模式*n
解释器模式
Expression 抽象表达式,声明方法
terminal expression 终结符表达式,实现抽象表达式声明的方法
nonterminal expression 非终结符表达式 实现抽象表达式声明的方法
client 客户,调用解释器模式,其实后面几章可以看出client其实就是作者写个例子出来给我们看看怎么用
context 环境,上下文,提供一些变量的初始值
越到后面的模式,作者起到的作用反而只是提一下,甚至连例子都没有,和上几个模式有java源码结合说模式特点简直是天壤之别,但是这样也说明了一个事实,这个模式实际使用的情况很少
调停者模式
Mediator 抽象的调停者 定义一个事件方法
ConcreteMediator 具体调停者 实现抽象调停者定义的方法
Colleague 抽象的同事 实现调用调停者的方法,定义获取调停者的方法,定义一个抽象行动方法
ConcreteColleague 具体的同事 实现 抽象的同事定义的的抽象行动方法、
ps.
mediator.colleagueChanged(c1);
c2.change();
我觉得正常应该是用第二行的调用吧。也就是同事通知调停器开始调停和调停器自己调停...
ps2.
总感觉该书越到后面越简略,是默认读者已经成长的吗?但是我们不是通常把这些书当成工具书,缺啥翻到哪里直接开时看的吗?
还有调停者模式其实就是状态更新或者状态改变执行什么方法之类的,不就是mq的消息中间件做的事吗?
这一章看完的话,对mq的使用和实现更加清楚
六 文章总结
一开始是想代码也贴上去,但是由于代码太多,我是没这打算了。
而写这篇文章的主要就是,网上关于模式的很多文都太烂了,仅仅是复制黏贴,而且我上面也说过,他们根本没有思考,完完全全是大学老师的教案copy,不切实际。
项目里面有代码有中文应该能更好的理解,但是看完了书,写完了读后感我觉得,模式这玩意,看是真的学不到多少的。实际开发没使用过,就没有那种哦原来是这样的感慨,所以我重复一次开头的话,适合有相当的编程基础的人看。
最后说一句 java与模式真的是一把好书。