一句话了解设计模式

Author Avatar
WoodyXiong 3月 27, 2019
  • 在其它设备中阅读本文章

前言

以前认为能够实现功能即可,但是真正在应用中,随着代码的演变,后期代码的可维护性越来越差。例如改一处而动全身,大大增加了代码的出错隐患;主流程代码混入了奇奇怪怪的逻辑,导致代码晦涩难懂。而使用了设计模式之后,在代码演变时尽量都是增加函数或者类,降低了很多安全隐患,也减少了不必要的工作量。使用了合适的设计模式,绝对会让你事半功倍。

需要理解这么多的设计模式也不是一件容易的事情,我将第一次看设计模式的体会尽量用一句话体现出来,这样更加突出了其独特的设计风格,在者在需要使用的时候返回来查一些模式也比较容易查到。由于我也是第一次研究设计模式,按照自己理解得出的,且水平有限,可能跟别处会有出入。如有不同意见,欢迎留言。

设计模式的7大基本原则

此7大基本原则用来描述一种软件的设计方式,用于量化设计模式的好坏。相比于“好用、牛逼、屌的一笔”等形容词,对设计有了更具体的描述。根据网上的说法,分成6种和7种基本原则,一般是多出了单一职责原则。我认为设计模式里面很多种是互相穿插,有些重复的,所以到底几种并不重要,而重要的是对其进行理解,并用到实际搬砖中。(ps 为了更愉悦地吹逼,我觉得多了解几种还是挺不错的)

开闭原则

对扩展开放,对修改关闭。在增加新功能的时候,增加类和函数即可,无需改造之前的类,这点在软件设计时非常重要。策略模式

里氏代换原则

子类可以继承父类被进行调用,只要父类存在的时候,一定可以替换成子类。这是开闭原则的步骤,正是因为继承,才能实现新功能来的时候继承就好。

依赖倒置原则

设计和实现要依赖于抽象而非具体,当类之间互相依赖的时候,尽量用set方法将实例注入进去,而上层调用底层的抽象函数。这样可以很好地支持开闭原则。

接口隔离原则

子类都有的方法,用抽象类继承;子类不一定有的,用接口实现。门的例子

迪米特原则 | 最少知道原则

外界无需知道的,使用private;上层只需要知道中间层,中间层只需要知道底层。

合成复用原则

单一职责原则

一个类,只干这个类该做的事情;服务员是服务员,厨师是厨师。

具体到所有设计模式

创建型模式

简单工厂模式

工厂类通过传入的参数来生成对应的类,非常简单,但是有很多if语句来判断参数。

抽象工厂模式

当工厂类有很多个的时候,比如图形工厂颜色工厂,可以将工厂继续抽象,用一个总工厂来生成图形工厂颜色工厂,最后再有图形工厂颜色工厂生产产品。

单例模式

如果类只需被实例化一次,就将他放在static里面,并且开一个static方法将其送出去。值得注意的是,为了保证线程安全,不能同时被new两次,所以将同步锁加在new的时候。如果在加在类的时候实例化,则是饿汉模式;若是第一次调用的时候new,则为懒汉模式

建造者模式

类似套餐的情况,底层的东西基本不变,但是组合在一起经常变化;底层统一继承一个商品,外面造一个套餐类,保存一个List来存储底层不变的东西,这样就可以自由组合多种套餐

原型模式

当初始化一个类需要消耗大量资源的时候,将一个实例化保存到一个地方。如果之后需要继续实例化这个类的话,可以用clone()(java和php都有这个函数)将之前的实例化拷贝出来。值得注意的是,浅拷贝只是拷贝了引用,而没有真正去拷贝;而深拷贝是所有都拷贝。php浅拷贝和深拷贝

结构型模式

适配器模式

例如一个app需要调用摄像头,但是摄像头是不同公司的,有索尼、佳能等,每个公司提供的sdk都不一样,主要是函数名不一样。这时候我们可以在sdk上继续封装一层适配器层,这样无需更改sdk代码就可以实现函数名的统一。

桥接模式

在类的构造函数或是set方法将一些子属性传进去,以保证类的多样性防止类爆炸

过滤器模式

这种模式将各种过滤的方法抽象化并解耦出来,然后使用一些简单的orand等逻辑简单组合形成多样化的过滤组合模式。

组合模式

这个设计模式有点像观察者模式,与观察者不同的是,这个模式的结构是树状,并且是可以传递的。例如公司老板告诉各个部门主管我们公司要破产啦,然后各个部门主管告诉每个人咱们公司要破产了。这个消息传递的机制就是组合模式,怎么感觉名字和这个设计模式的原理匹配不上。

装饰器模式

感觉这个设计模式有点难理解,初步感觉是给某个类递归地附加几种平行的属性。防止互相继承导致类爆炸。(效果如同桥接模式,但是实现方法不一样)

外观模式

咋一眼看去感觉是个非常傻屌的设计模式,像是将很多类放在一个中心类中,然后for循环进行批量操作,有点像观察者模式,不过这个相对来说灵活些,因为子类可以不一样,但是写会更复杂些。

享元模式

一般结合工厂模式,类似单例模式,减少类的数量。主要内部是有个hashMap之类的结构存入“单例实体”。

代理模式

私认为代理模式适配器模式的差异不大,都是一个中间层对底层处理逻辑。适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口(来自runoob)。反正用的时候随心所欲,无需分这么细。

行为型模式

责任链模式

有点像链表一样,有着传递的感觉,一般来说是将同一个变量传给A类的函数,A发现此操作无法处理,则传给B,B接着传给C,直到碰到可以处理的类。常用于审批流程,科室的任务先传给处级,处级操作完交到局里,局里操作完到部里,直到可以操作为止。

命令模式

命令模式大概就是先人执行命令行得出的灵感吧,接触到最深的例子就是在gui中,每个按钮都是几条命令行叠加的结果。而命令模式将这种方式解耦,拆分成三个类,即命令角色(gui)中间接受者(命令行的业务操作在这里,可以进行执行回退等功能)、实际命令(这些类是一个一个的命令),接收者通过调用这些一个一个的命名来对命令角色做出相应的相应。这样将一个类一步一步的解耦。

解释器模式

通常用于编译器运算表达式解释器模式指的是将一个句子拆分成语法树,底层一步一步将结果汇总,最后将结果输出,有点像组合模式

迭代器模式

一般用于自定义的容器,类似listmap等数据结构,调用方无需关心底层容器是如何将数据吐出来,只管用即可。而底层就是用了迭代器,像链表一样可以一直向下访问,以致于上层可以用forwhile等语句直接访问数据。

中介者模式

类之间的网状调用关系通过一个中间类变成星状调用关系,大家只关心如何跟中介进行交互,降低类与类之间的耦合。例如MVC模式的controller控制器就是一个中介,modelview层通过controller来进行相互交互。

备忘录模式

常用于通关存档ctrl-z浏览器的后退等场景,主要功能是将当时的数据保存下来,有可能是保存成类,也可能是序列化,在需要的时候可以反向序列化等方式回到当时的场景,常常配合原型模式

观察者模式

个人理解此种设计模式也是发布/订阅模式。一个很经典的例子就是,一个tcp服务器端和很多个tcp客户端,一个场景是服务器端全局广播信息给所有客户端,这个时候客户端就是观察者。实现方式是,每次客户端与服务端建立连接之后,就在服务器端注册这个socket,也就是服务器端有个list,每次有客户端连接就加一个。当需要全局发送广播的时候,服务端遍历list将信息发送给客户端,这尼玛就是观察者模式。

状态模式

状态模式比较注重的是获取当前状态,并对于当前状态去干此状态的事情。相比于一堆if……else……的逻辑判断,此模式是根据一个其成员的状态去运行的。最后,我觉得著名的状态机就是状态模式+责任链模式的结果,这样才能根据状态不停地将任务传递下去。

空对象模式

例如工厂模式,传入的值如果在工厂没有对应的类,所以工厂返回的是null,如果后面的程序没有继续判断是否是Null,则上层程序按照这个实例进行操作的话,就会报空指针异常的错误。为了程序的健壮性,空对空模式就是将本是返回的null值返回成一个专门的空实例,以致于上层程序如果调用的话,返回的函数都是有值的,但是会提示是空的。我认为这个模式可以提升一部分健壮性,但是不一定适合,如果内部太过复杂,可能很难模拟掉它的返回,所以要因地制宜。一些参考

策略模式

在一个流程下来,中间的某个部分可能有很多种方式去实现,例如将数里所有学生拿出来,并且将学生按照成绩排序,拿到最终的学生数组。中间的成绩排序有很多种方式,例如快排、冒泡、桶排等,这些排序可以拿出来专门的类,在上层可随意指定哪种排序方式。此设计模式经常可以运用的到,在php中,配合call_user_function使用最佳。

模板模式

这种妈卖批模式竟然有名字,咋一看就是抽象类将骨架定好,然后子类去继承然后实现每个步骤的方法。这应该就是最简单最简单的类继承了。

访问者模式

此种设计模式感觉看得比较懵逼。有一个容器类,装着各个元素,各个元素都有accept函数,专门用来接收访问者,然后将自己传给访问者,调用的时候将访问者放进容器中遍历一遍即可。类与类之间互相嵌套,耦合程度挺高的。是不是很懵逼,懵逼就对了,太复杂的设计模式可能并不是太好,所以这个放在最后一个行为模式可能就是这个原因吧。

J2EE 模式

MVC模式

最经常用的一个模式,M指model层,V指view层,C指controller层。controller层夹在view层和model层的中间,他们的交互全部是通过中间层controller进行交互的。这种模式经常用于网站的MVC框架中,实现了界面与数据库模型的解耦。

业务代表模式

这个模式没怎么看懂,感觉网上资料比较少,都是抄来抄去的。感觉底层就是一个工厂,然后有几层中间层,知道原理,但是不知道实现啥,为什么这样做。。。zZ

组合实体模式

组合模式的运用

数据访问对象模式

类似开发中的Controller->Service->Entity->Model,Model是一个一个的实体,Entity是多个实体进行操作的一层,对上层提供对Model层的方法,而Service为纯业务层。

前端控制器模式

将所有请求收敛,通过一个调度器,这个调度器可以做日志收集、权限校验等功能,然后再发散到各个策略中去,最后生成视图层View

拦截过滤器模式

就拿上一个前端控制器模式来讲吧,中间的调度器就可以做成一层一层的,并且是可以自由添加减少的,而一层一层的拦截器存储在一个列表中,这样就可以自由的添加减少拦截器

服务定位器模式

理解到是首先去取缓存,若缓存有就拿缓存,没有的话实例化一个再存入缓存。感觉上最好搭配原型模式,在需要的时候进行深拷贝以取到不同的实例

传输对象模式

将一个类型的多个实例打包成一个大的实例,实例里面大概率又是列表的数据结构,然后将这个大实例传给接收方,这样的做法是减少网络传输的中间断开的的概率,而进行打包传输