设计模式分为三大类:创建模式、结构模式、行为模式
一、创建型模式
对象实例化的模式,创建型模式用于解耦对象的实例化过程。
1、单例模式:某个类智能有一个实例,提供一个全局的访问点。
2、工厂模式:一个工厂类根据传入的参量决定创建出哪一种产品类的实例。
3、抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。
4、建造者模式:封装一个复杂对象的创建过程,并可以按步骤构造。
5、原型模式:通过复制现有的实例来创建新的实例。
二、结构型模式
把类或对象结合在一起形成一个更大的结构。
1、装饰器模式:动态的给对象添加新的功能。
递归结构,从而可以不断的装饰,增加新的功能,很好用
2、代理模式:为其它对象提供一个代理以便控制这个对象的访问。
3、桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
分开实现类的内容,比如水杯,都可以喝水,但喝水方式可能不一样,有的需要打开盖子,有的需要用吸管,这是实现部分,用不的类实现,抽象部分可以理解为杯子有不同的颜色和形状,看起来更美观或者拿起来更方便舒服。分开实现独立变化不影响。可以任意组合。
4、适配器模式:将一个类的方法接口转换成客户希望的另一个接口。
将调用原有方法或类的地方,修改为调用适配器,适配器中根据具不同情况调用其他类及方法。类似于现实中的转接头。可以解决版本的兼容性问题。
5、组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。
抽象构件、构件容器(也是一种构件)、构件组成,构件容器可以任意添加构件,树形结构,将具有树形特点的对象组合起来
6、外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
7、享元模式:通过共享技术来有效的支持大量细粒度的对象。
三、行为型模式
类和对象如何交互,及划分责任和算法。
1、策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
定义一系列的子类,同属于一个抽象类,他们就可以根据不同情况相互替换(调用不同的子类)。用于整体替换策略
2、模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
其主要分为两大类:模版方法和基本方法,而基本方法又分为:抽象方法(Abstract Method),具体方法(Concrete Method),钩子方法(Hook Method)。
(1)抽象方法:由抽象类声明,由具体子类实现,并以abstract关键字进行标识。
(2)具体方法:由抽象类声明并且实现,子类并不实现或者做覆盖操作。其实质就是普遍适用的方法,不需要子类来实现。
(3)钩子方法:由抽象类声明并且实现,子类也可以选择加以扩展。通常抽象类会给出一个空的钩子方法,也就是没有实现的扩展。它和具体方法在代码上没有区别,不过是一种意识的区别;而它和抽象方法有时候也是没有区别的,就是在子类都需要将其实现的时候。而不同的是抽象方法必须实现,而钩子方法可以不实现。也就是说钩子方法为你在实现某一个抽象类的时候提供了可选项,相当于预先提供了一个默认配置。
(4)模板方法:定义了一个方法,其中定义了整个逻辑的基本骨架。
3、命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
4、迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
将遍历与实现分开,创建一个迭代类用于包装原有类的遍历
5、观察者模式:对象间的一对多的依赖关系。
当被观察者发生变更时,通知各个观察者,进行自己的变更
6、仲裁者模式:用一个中介对象来封装一系列的对象交互。
当其中有观察者变更时,通过一定逻辑判断当前的各种数据状态,对所有观察者进行数据及状态设定
7、备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
在系统运行到某种状态时,建立一个快照,用于恢复,以便再次从此状态开始运行,可将信息转为文件避免信息丢失
8、解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。类似于编译器
9、状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
当执行同一操作时,根据当前状态的不同,执行不同的内容,这里就可以将不同的状态实现成为不同的类的实例,当状态变化时,就是不同的实例,这样调用的就是不同的实现方法。
10、责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
适用于不知道问题是什么该怎么解决(有很多种方式),所以写出很多种解决算法,按需求排序如性能,将不同的算法组成一个链表,当第一个不能解决时传递给下一个,直到问题解决或者链表传输完成也没解决。
11、访问者模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能。
通过在类外访问类中的数据结构从而得到想要的结果,便于程序的可扩展性和组件化;
间接递归,访问方式多种,访问者根据不同的情况进行不同的操作,这个不同情况是固定的,因为数据结构不变。就好像采访一个明星,那么这个明星接受采访,把自己基本信息(能问的问题以及某些答案)告诉主持人,问主持人有问题吗?如果主持人有问题(还能向下问,就是之前问题的答案引申出来的问题,访问方式的多种可以体现在这)要问那么就再次拿着新的问题问这个明星,这个明星再次将自己关于这方面的信息告诉主持人;如果没有问题(得到答案),主持人将信息总结之后说出来。就这样一直持续下去,直到主持人没问题问了,并且明星的信息也都被问到了,这样采访就结束了。
Open-Close Principle(OCP):
对于扩展是开放的,对于更改是封闭的。
Single-Responsibilitiy Principle(SRP):对一个类而言,应该仅有一个引起它变化的原因。 如果存在多于一个动机去改变一个类,那么这个类就具有多于一个的职责,就应该把多余的职责分离出去,再去创建一些类来完成每一个职责。
3. 里氏替换原则
Liskov Substitution Principle:子类可以扩展父类的功能,但是不能改变父类原有的功能。
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
也就是父类在任意位置可以被子类替换
4.依赖倒置原则
Dependence Inversion Principle(DIP):是一个类与类之间的调用规则。这里的依赖就是代码中的耦合。高层模块不应该依赖底层模块,二者都应该依赖其抽象了;抽象不依赖细节;细节应该依赖抽象、接口编程。
问题由来
类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方案
解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
在实际编程中,我们一般需要做到如下3点:
低层模块尽量都要有抽象类或接口,或者两者都有。
变量的声明类型尽量是抽象类或接口。
使用继承时遵循里氏替换原则。
依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。
5. 接口隔离原则
接口隔离原则(Interface Segregation Principle):用于恰当的划分角色和接口,具有两种含义:1、用户不应该依赖它不需要的接口;2、类间的依赖关系应该建立在最小的的接口上。
在接口隔离原则中,要求尽量使用多个专门的接口。
6. 迪米特原则
Law of Demeter(最小知识原则):一个对象应该对其他对象有最少的了解。通俗来说就是,一个类对自己需要耦合或者调用的类知道的最少,你类内部怎么复杂,我不管,那是你的事,我只知道你有那么多公用的方法,我能调用。
迪米特原则核心观念就是:类间解耦,弱耦合。
从迪米特法则的定义和特点可知,它强调以下两点:
从依赖者的角度来说,只依赖应该依赖的对象。
从被依赖者的角度说,只暴露应该暴露的方法。