设计模式学习记录
最近一段时间学习了设计模式,在此记录一下自己对于设计模式的理解。
一 设计模式的原则
1、单一职责原则
一个类或者方法只做一件事情,或者说只有一个角色。例如一个短信工具类,只负责和短信有关的。
(扩展一下,一个类或方法,在写代码的时候每个模块做的事应该是一个水平的,就是说一件事 1,2,3三个步骤,每个步骤有诺干小步骤,小步骤应该放到子方法或子类中,当前模块就负责1,2,3步骤的整合。ps: 代码整洁之道里面的,这里记录一下)
2、接口隔离原则
一个类继承一个接口,接口的方法都应该是子类中能用的着的 ,不然的话用不着的部分可以 拆分成另外的接口
3、依赖倒转原则
new一个类的时候尽量用这个类的父级去接收,一个系统中,一个类的成员不要是同级别类的子类。
4、里氏替换原则
规范是子类不能改变覆盖父类的非抽象方法,重载父类方法时,入参可以更宽松(一开始没明白过来,后来反应过来,就是参数使用父类叫重载,是另外一个方法,相当于子类有两个方法,参数小于等于子类,那就是覆盖父类方法了,违反历史转换原则了)。目的就是在所有父类能用的地方替换成任何子类都能用
5、开闭原则
扩展了功能,对原有的功能不影响
6、迪米特法则
只与直接的朋友发生通信,直接的朋友指的是成员变量,方法参数,返回类型中的是成员参数,如果方法中出现有类型不是该类所有成员变量,方法参数,返回类型中的任何一个,都违反了迪米特法则
当然,这些原则就类似数据库三范式,盲目遵守写出的也并不一定是高效简洁的代码,还是需要掌握一个度才行。
二、设计模式
1、单例模式
单例模式有懒汉式和饿汉式,懒汉指的是需要的时候创建,饿汉式指的是程序运行的时候就创建了。
单例主要有静态变量、枚举、双重锁和静态内部类,前两个都是立即加载,算是饿汉式,后两者则是执行时加载,算是懒汉式。
2、工厂模式
工厂模式分为简单工厂、工厂方法以及抽象工厂。
简单工厂就是一个能够创建多种对象的工厂模式,问题在于这种方法需要有多个switch或if else分支,不符合开闭原则。
工厂方法是有一个抽象工厂父类,针对每个对象创建不同的工厂实例。(ps:以前一直没明白,我觉得工厂方法还是需要去if else决定使用什么工厂,感觉和简单工厂没区别,现在明白其实创建实例的地方一般都在写代码的时候明确了需要创建什么实例,而这种情况简单工厂还是需要条件分支选择创建什么对象,工厂方法则只需要直接使用相应对象的工厂就可以了)。
而抽象工厂和简单工厂类似,区别在于简单工厂一般都是只能创建一种对象,而抽象工厂则能够创建一组对象。
3、原型模式
原型模式差不多就是复制对象的实例,主要需要理解的地方就是浅拷贝和深拷贝的问题。(java深拷贝主要是自定义clone和序列化)
4、建造者模式
建造者模式是在构建一个参数比较多而且构建步骤可以选择组合时使用较多的一个设计模式。director这个角色感觉可有可无,这个角色是为了避免调用者去构建过程,不过有时候这个构建过程难免会要由调用者去自定义。
5、适配器模式
适配器宗旨是对原有的类型做一个适配,使得其他不能直接调用的类能够调用。
这里可能会疑惑第三方直接调用不行吗? 可能有一种情况,不能直接改第三方的代码,或者想解耦这两边的代码。
适配器一般两种模式,继承被适配者或者有一个被适配者的成员。
其实看了一些源码,适配器最常用的方式感觉是这样的: 加载所有定义好的适配器,不同的调用者在所有适配器中找到自己的适配器,然后执行适配器中的某个方法,该方法对应不同的调用者有不同的实现,达到开闭的原则。
6、桥接模式
如果有两个维度以上的变化,就可以选择桥接模式。注意是两个维度的变化不是两个变化的地方,两个维度比如人有男的女的,年龄有老中幼,这种能两两组合的才属于不同维度。
其实我一开始也没明白啥叫抽象部分与实现部分分离,后来琢磨了一下,可能是我读错了,不是与实现部分分离,应该是与实现,部分分离。
也就是说把另外一个维度的变化给分离出去了,还有一点一开始脑子没转过来时没想明白,为啥是一个抽象一个接口,后来仔细看了下代码,发现原因是不用抽象类不能用set把接口类给组合进去啊哈哈,当然两个抽象类也是ok的。
7、装饰者模式
装饰者模式主要解决过度的继承导致代码难以维护的问题。首先这个继承是属于扩展功能的继承,子类继承扩展了父类的功能,比如一个笔的父类,子类是钢笔毛笔,铅笔等各个实现完全独立的装饰者模式完全也用不上。
为啥能解决呢,看装饰者模式可以看出子类再多都只需要一个个都继承一个父类就够了,再多的子类也是两层,用继承的话多少层都有可能,而且最重要的是装饰者模式可以让使用者去进行组合达到各种扩展功能,而继承的话就需要去扩展继承类了。
装饰者属于一种组合,不过这个用于组合的成员都是同一个接口父类。这些个操作感jio和楼上的桥接模式有点类似,像是一个维度的两两组合和多个维度的组合
8、组合模式
组合模式是类似于一个叶子节点的结构,场景比较单一。
9、外观模式
facade,外观模式是为了给原本复杂的子系统提供一套简单的接口,比较类似适配器模式,不过适配器模式更主要是适配,是让本来不能调用的类变得能够调用,调用者和被调用者可能都是不可修改的,而外观模式是为了给客户端更简便的调用。
10、享元模式
各种常量池、线程池用的就是享元模式,主要就是复用一个对象,免得重复创建浪费性能。享元模式需要注意有内部和外部状态,内部状态可以共享,外部状态会改变。
11、代理模式
代理模式用过java的都知道,没啥好说的。主要是静态代理和动态代理,静态代理就是写代码的时候包装一下被代理的代码,动态代理是运行的时候进行的代理,分为接口代理和cglib方法代理。
12、模板模式
一些什么生命周期函数,钩子,用的就是模板方法,大致的流程已经在父类写好了,不同的操作通过覆盖几个特定的方法去实现。
13、命令模式
命令调用者,命令,以及命令接收者构成命令模式,总之就是命令调用者调命令,命令内部调用命令接收者。这个和适配器模式以及外观模式都有点类似。适配器是适配两个存在的类,外观模式是为了简化接口,而命令模式呢?为啥不直接命令调用者调用接收者?命令模式有一种特定的场景,就是需要记录日志回退什么的,还有就是可以做一个命令队列,组合命令,这个队列的动作可以相互组合。还有存粹为了命令调用者和命令接收者的解耦。而且试想一下,命令模式就像遥控器,我现在有一个遥控器,遥控器的按键是固定的,这时候我可以替换按键的命令去执行不同的操作。
14、访问者模式
访问者模式为了解决一个数据结构类中对于对象操作新增方法的需求,因为新增方法就需要在数据结构类中新增代码,不符合开闭原则,访问者模式把新增方法放到访问者类中,可以转换为新增不同的访问者类。访问者模式还可以有一个特点,就是访问者visitor的对不同对象的访问方法都是一个重载方法,参数是父类的不同实现类,当然这是违反了依赖倒置的。所以这种访问者的一大好处就是不用通过一推ifelse去判断,而是通过类型自动推断执行对应的方法,这个比较适合的场景就是在一个遍历中。访问者主要是扩充了原来类的方法,或者说将原本类中没啥关系的行为抽取出来。
15、迭代器模式
迭代器模式同样是java中比较常见的一个模式,不来细说了。
16、观察者模式
初步觉得观察者模式的应用场景比较单一,主要就是一种发布订阅的模式,在java中已经有实现好的观察者模式的类 observable。
17、中介模式
中介者模式感觉就像专门为了实现迪米特法则的一种模式。是为了解决各种类之间复杂的调用关系。和外观模式类似,区别在于中介模式主要为了解决类之间的相互调用,外观模式是给多个复杂的类统一对外提供接口。
18、备忘录模式
用来记录对象的状态,可以用来回退到其中一个状态
19、解释器模式
解析语法或者是表达式时能用到的一种设计模式
20、状态模式
状态在对象内部,由状态来决定行为
21、策略模式
策略模式适用于一些行为比较相近的对象,通过父类的成员变量,可以子类相互转换
22、职责链模式
java就直接参考springmvc的过滤器链
以上都是我在学习设计模式后的个人观点,有些地方也是一知半解的,希望看到博客的能多多讨论一下!
下一篇: SEO面试的技巧 大神教你如何去面试