原则归结:
1、单一职责原则
对类来说的,即一个类应该只负责一项职责
2、里式替换原则
所有引用基类的地方必须能透明地使用其子类的对象。
3、接口隔离原则
一个类对另一个类的依赖 应该建立在最小的接口上
4、迪米特原则
5、依赖倒置原则
6、开闭原则
7、合成复用原则
合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用功能的目的。简言之:复用时要尽量使用组合/聚合关系(关联关系),少用继承。
通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,所以这种复用又称“白箱”复用,如果基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;而且继承只能在有限的环境中使用(如类没有声明为不能被继承)。
模式归结:
创建模式5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构模式7种:适配器模式、装饰器模式、代理模式、享元模式、外观模式、桥接模式、组合模式
关系模式11种:
1、单例模式
饿汉方式:定义静态实例方式实现,事先初始化好实例,是线程安全的
懒汉方式:通过判断局部实例对象是否为空实现,用的时候进行初始化,是非线程安全的
双检锁方式:线程安全的,代码如下:
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance() {
if (singleton == null) { // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
synchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化
if (singleton == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
singleton = new Singleton();
}
}
}
return singleton;
}
}
2、代理模式
代理模式是给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
优点:
代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;
可以灵活地隐藏被代理对象的部分功能和服务,也增加额外的功能和服务。
缺点:
由于使用了代理模式,因此程序的性能没有直接调用性能高;
使用代理模式提高了代码的复杂度。
举例:jdk动态代理
3、装饰器模式
装饰器模式是动态地给一个对象增加一些额外的功能,同时又不改变其结构。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模
式可以动态扩展一个实现类的功能。
代理模式和装饰器模式有什么区别?
都是结构型模式,代理模式重在访问权限的控制,而装饰器模式重在功能的加强。
装饰类内部并不会创建被装饰的对象示例,而是在外部创建好之后传给装饰类。
被代理的对象在内部进行创建,因此不能像装饰那样递归调用。
装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。在这里应用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序了。
举例:jdk动态代理
4、模板方法模式
模板方法模式是指定义一个算法骨架,将具体内容延迟到子类去实现。
优点:
提高代码复用性:将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中;
实现了反向控制:通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,
实现了反向控制并且符合开闭原则。
举例:java抽象类 abstract class
public abstract class Cookie{
//模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
//声明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
final void cookProcess(){
//第一步:倒油
this.pourOil();
//第二步:热油
this.HeatOil();
//第三步:倒蔬菜
this.pourVegetable();
//第四步:倒调味料
this.pourSauce();
//第五步:翻炒
this.fry();
}
//定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的
//第一步:倒油是一样的,所以直接实现
void pourOil(){
System.out.println("倒油");
}
//第二步:热油是一样的,所以直接实现
void HeatOil(){
System.out.println("热油");
}
//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
//所以声明为抽象方法,具体由子类实现
abstract void pourVegetable();
//第四步:倒调味料是不一样的(一个下辣椒,一个是下蒜蓉)
//所以声明为抽象方法,具体由子类实现
abstract void pourSauce();
//第五步:翻炒是一样的,所以直接实现
void fry();{
System.out.println("炒啊炒啊炒到熟啊");
}
}
//炒手撕包菜的类
public class ConcreteClass_BaoCai extend Abstract Class{
@Override
public void pourVegetable(){
System.out.println(”下锅的蔬菜是包菜“);
}
@Override
public void pourSauce(){
System.out.println(”下锅的酱料是辣椒“);
}
}
//炒蒜蓉菜心的类
public class ConcreteClass_CaiXin extend Abstract Class{
@Override
public void pourVegetable(){
System.out.println(”下锅的蔬菜是菜心“);
}
@Override
public void pourSauce(){
System.out.println(”下锅的酱料是蒜蓉“);
}
}
public class Template Method{
public static void main(String[] args){
//炒 - 手撕包菜
ConcreteClass_BaoCai BaoCai = new ConcreteClass_BaoCai();
BaoCai.cookProcess();
//炒 - 蒜蓉菜心
ConcreteClass_ CaiXin = new ConcreteClass_CaiXin();
CaiXin.cookProcess();
}
}
5、策略模式
策略模式是指定义一系列算法,将每个算法都封装起来,并且使他们之间可以相互替换。
优点:遵循了开闭原则,扩展性良好。
缺点:随着策略的增加,对外暴露越来越多
举例:interface的各种实现
策略模式和模板方法模式的区别:
策略模式是整体灵活化实现,而模板方法模式是部分灵活化实现
6、享元模式
享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。
享元模式和单例模式的区别:
单例模式是创建型模式,重在只能有一个对象。而享元模式是结构型模式,重在节约内存使
用,提升程序性能。
享元模式:把一个或者多可对象霍村起来,用的时候,直接从缓存里获取。也就是说享元模式不一
定只有一个对象。
举例:java中的final关键字定义的对象
7、责任链模式
是行为型设计模式之一,其将链中每一个节点看作是一个对象,每个节点处理的请求均不同,
且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给
每一个节点对象,直至有对象处理这个请求为止。
个人理解:类似工作流之类的模式
举例:SpringMVC中的拦截器和Mybatis中的插件机制,都是拦截器经典实现。
8、适配器模式
适配器模式是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而无
法一起工作的两个类能够在一起工作。
优点:
缺点:
9、观察者模式
观察者模式是定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关
依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模
型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模
式。
优点:
缺点:
举例:在Spring中大量的使用的观察者模式,只要看到是以Event结尾或者Publish开头的基本上都是观察者模式。
10、原型模式
原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
举例:1、object的Cloneable实现 2、spring中原型bean的创建
关于浅拷贝和深拷贝的可看《一些基础知识的整理》
11、建造者模式(也称为生成器模式)
生成器模式理解为,假设我们有一个对象需要建立,这个对象是由多个组件(Component)组合而成,每个组件的建立都比较复杂,但运用组件来建立所需的对象非常简单,所以我们就可以将构建复杂组件的步骤与运用组件构建对象分离,使用builder模式可以建立。
生成器模式结构中包括四种角色:
(1)产品(Product):具体生产器要构造的复杂对象;
(2)抽象生成器(Bulider):抽象生成器是一个接口,该接口除了为创建一个Product对象的各个组件定义了若干个方法之外,还要定义返回Product对象的方法(定义构造步骤);
(3)具体生产器(ConcreteBuilder):实现Builder接口的类,具体生成器将实现Builder接口所定义的方法(生产各个组件);
(4)指挥者(Director):指挥者是一个类,该类需要含有Builder接口声明的变量。指挥者的职责是负责向用户提供具体生成器,即指挥者将请求具体生成器类来构造用户所需要的Product对象,如果所请求的具体生成器成功地构造出Product对象,指挥者就可以让该具体生产器返回所构造的Product对象。(按照步骤组装部件,并返回Product)
优点
将一个对象分解为各个组件
将对象组件的构造封装起来
可以控制整个对象的生成过程
缺点
对不同类型的对象需要实现不同的具体构造器的类,这可能回答大大增加类的数量
12、外观模式
外观模式是隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。
涉及的3个角色:
1).门面角色(Facede):客户调用,同时自身调用子系统功能
2).子系统角色:实现具体功能,对客户角色和门面角色时未知的
3).客户角色:通过调用门面角色来完成要实现的功能
优点
- 松散耦合
使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;
- 简单易用
客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。
- 更好的划分访问层次
13、桥接模式
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。例如手机(品牌)和手机软件的关系
桥接(Bridge)模式包含以下主要角色。
抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。提供高层控制逻辑, 依赖于完成底层实际工作的实现对象。
扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
客户端(Client):仅关心如何与抽象部分合作。但是,客户端需要将抽象对象与一个实现对象连接起来。
优点
14、组合模式
有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
例如学校、学院、系需要增加、删除、查询操作,不用管对象是学校、学院还是系。
优点:
组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
15、迭代器模式
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
举例:java中的iterator
16、命令模式
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
涉及的角色有:
17、状态模式
状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。
状态模式包含以下主要角色。
状态模式与责任链模式的区别
状态模式和责任链模式都能消除 if-else 分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用。
从定义来看,状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象间的改变。
从代码实现上来看,两者最大的区别就是状态模式的各个状态对象知道自己要进入的下一个状态对象,而责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责。
状态模式与策略模式的区别
状态模式和策略模式的 UML 类图架构几乎完全一样,但两者的应用场景是不一样的。策略模式的多种算法行为择其一都能满足,彼此之间是独立的,用户可自行更换策略算法,而状态模式的各个状态间存在相互关系,彼此之间在一定条件下存在自动切换状态的效果,并且用户无法指定状态,只能设置初始状态。
18、备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
优点:
缺点:
资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
19、访问者模式
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离。
访问者模式是迭代器模式的扩充,业务规则要求遍历多个不同的对象,针对访问对象的不同,执行不同的操作。访问者还有一个用途,就是充当拦截器角色。
20、中介者模式
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
21、解释器模式
解释器模式就是为不同的语法设置不同的解释器,让后利用解释器来计算表达式的值或解释表达式的含义,关键在于如何实现解释器。
Spring框架中都用到了哪些设计模式?