设计模式六大原则
1.单一职责:不要存在多于一个导致类变更的原因,即一个类只负责一项职责。(合成复用即多使用聚合);
2.里氏替换:所有引用基类的地方必须能透明的使用其子类的对象;
3.依赖倒置:高层模块不应该依赖底层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;
4.接口隔离:针对接口编程,而不是针对实现编程。客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上;
5.迪米特法则:一个对象应该对其它对象保持最少的了解;
6.动静分离原则:找出应用中可能需要变化的部分,把他们独立出来,不要和那些不需要变化的代码混在一起;
行为模式
1.中介者模式
用一个中介者对象封装一系列的对象交互,中介者使各个对象不需要显示的相互作用,从而使耦合松散,独立地改变他们之间的交互。
中介者模式的结构
- 抽象中介者:定义好同事对象到中介者对象之间的接口,用于各个同事类之间的通信,一般包括一个或者几个抽象的事件方法,并由子类去实现;
- 中介者实现类:从抽象中介者继承而来,实现抽象中介者中定义的事件方法,从一个同事类接收消息,然后通过消息影响其他同事类;
中介者模式优缺点
- 降低同事类之间的耦合,将对象间一对多的关系转化为一对一的关联,使对象间的关系易于理解和维护。
2.模板方法模式
定义一个操作中算法框架,而将一些步骤延迟到子类中,使得子类可以不改变算法结构即可重新定义该算法中的某些特定步骤。
模板方法的结构
- 模板方法模式由一个抽象类和一个(或一组)实现类通过继承结构组成;
- 抽象方法:父类中只声明但不加以实现,而是定义好规范,然后由他的子类去实现;
- 模板方法:由抽象类声明并加以实现主要逻辑功能,一般被声明为final类型,指明主要逻辑功能在子类中不能被重写;
模板方法的优缺点
- 容易扩展,便于维护,比较灵活,行为由父类控制,子类实现;每一个不同的实现都需要一个子类来实现,导致类的数量增加,系统庞大。
3.策略模式
定义算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户;策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换。策略模式和模板方法模式的不同在于:在模板方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Contex中,抽象策略Strategy一般是一个接口,目的只是为了定义规范,里面一般不包含逻辑。
策略模式的结构
- 封装类(Contex):对策略进行二次封装,目的是避免高层模块对策略的直接调用;
- 抽象策略:通常为一个接口,若各个类中存在重复逻辑代码时,则用抽象类来封装这部分代码,此时策略 模式更像是模板方法模式;
- 具体策略:一组封装了算法的类,这些类之间可以根据需求自由替换;
eg:对于鸭子类接口Duck(Context),不同颜色的鸭子类ColorDuck实现接口Duck,在接口Duck中横向扩展鸭子行为的接口FlyBehavior和鸭子的叫声接口QuackBehavior,在Duck接口中加入行为接口FlyBehavior的引用和叫声接口QuackBehavior的引用,且具体行为接口FlyBehavior的实现类(实现算法)可根据需要自由实现飞行的行为,叫声接口QuackBehavior的实现类(实现算法)可根据需要自由实现叫声的行为。
策略模式的优缺点
- 易于扩展,策略类之间可以自由切换,避免使用多重条件判断;策略维护带来额外开销,且必须对客户端(调用者)暴露所有策略,因为使用哪种策略是由客户端决定的。
4.观察者模式
定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于他的对象都会得到通知并自动更新;
观察者模式的结构
- 被观察者:类中含有一个用来存放观察者对象的Vector容器,容器观察者添加方法add(),容器观察者删除方法delete(),观察者通知方法notify();
- 观察者:观察者一般是一个接口,只有一个update()方法,在被观察者状态发生变化时,这个方法被触发调用;
- 具体被观察者:使用这个角色便于扩展,在此角色中定义具体的业务逻辑;
- 具体观察者:观察者接口的具体实现,定义被观察者对象状态发生变化时所需要处理的逻辑;
观察者模式的优缺点
- 观察者和被观察者是抽象耦合,容易进行扩展,观察者模式是一种常用的触发机制,它形成了条触发链,依次对各个观察者的方法进行处理;当观察者较多时,性能较差,并且在链式结构中,比较容易出现循环引用的错误,造成系统崩溃;
- Java语言中有一个接口Observer,以及他的实现类Observable,对观察者角色进行了实现。
5.访问者模式
- 封装某些作用于某种数据结构中各元素的操作,他可以在不改变数据结构的前提下定义作用于这些元素的新的操作。即通过在业务类中添加含中间类实例参数的方法,中间类中添加含有业务类实例参数的方法,通过调用中间类的方法调用业务类的方法逻辑,则中间类就是业务类的访问者。
访问者模式结构
- 抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素;
- 访问者:实现抽象访问者所声明的方法;
- 抽象元素类:接口或者抽象类,声明接受哪一类访问者访问。一般包含两类方法,一种是本身业务逻辑方法,另一种是接受哪些访问者来访问。
- 元素类:实现抽象元素所声明的accept方法;
- 结构对象:一个元素容器,一般包含一个容纳多个不同类、不同接口的容器(List、Set、Map);
访问者模式的优缺点
- 符合单一职责原则,扩展性、灵活性良好;具体元素对访问者公布细节,违反了迪米特法则,增加新的元素比较困难。
6.命令模式
- 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能,即把一系列的操作写到一个方法中,然后供客户端调用。
命令模式结构
- Command类:是一个抽象类声明需要执行的命令,且对外公布一个execute方法用来执行命令;
- ConcreteCommand类:Command类的实现类,对抽象类中声明的方法进行实现;
- Client类:最终的客户端调用类;
- Invoker类:调用者负责调用命令;
- Receiver:接收者,负责接受命令并且执行命令;
命令模式的优缺点
- 封装性、扩展性很好;如果命令很多的话会增加代码的复杂度;
7.责任链
模式
- 使多个对象都有机会处理请求,从而避免了请求的发送者和请求的接收者之间的耦合关系。将这些对象连成一条链,并且沿着这条链传递该请求,直到有对象处理它为止。
责任链模式结构
- 抽象处理类:包含一个指向下一个处理类的成员变量nextHandler和一个处理请求的方法handRequest,handRequest的主要思想是。如果满足处理条件,则由本处理类来进行处理,否则由nextHandler来处理;
- 具体处理类:具体处理类主要是对具体的处理逻辑和适用条件进行具体的实现;
责任链模式优缺点
- 降低了耦合度,简化了对象,增强给对象指派职责的灵活性;不能保证请求一定会被接收,对系统的性能有一定的影响。
- Java中的Filter类即是责任链模式的典型实现。
8.迭代器模式
- 提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节,是目前使用最多的设计模式之一。
迭代器模式结构
- 抽象容器:一般是一个接口,提供一个iterator()方法,如Collection、List、Set;
- 具体容器:抽象容器的具体实现类,如ArrayList、HashSet;
- 抽象迭代器:定义遍历元素所需要的方法,取第一个元素first(),取下一个元素next(),判断遍历是否结束hasNext(),移除当前对象remove();
- 迭代器实现:实现迭代器接口中定义的方法,完成集合的迭代;
迭代器模式优缺点
- 封装性良好,简化了集合对象的遍历方式;由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
9.备忘录
模式
- 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态;
备忘录模式结构
- 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据;
- 备忘录:负责存储发起人对象的内部状态,在需要的时候提供给发起人需要的内部状态;
- 管理角色:对备忘录进行管理,保存和提供备忘录;
备忘录模式优缺点
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态,实现了信息的封装,使得用户不需要关心状态的保存细节;消耗资源很大。
10.状态
模式
- 状态模式主要解决的是:控制一个对象内部的状态转换的条件表达式过于复杂时的情况,且客户端调用之前不需要了解具体状态。它把状态的判断逻辑转到表现不同状态的一系列类当中,可以把复杂的判断逻辑简化。维持开闭原则且方便维护,状态模式是让各个状态对象自己知道其下一个处理的对象是谁!即在状态子类编译时在代码上就设定好了!使用状态模式时,每个状态对应一个具体的状态类,使结构分散,类的数量变得很多!使得程序结构变得稍显复杂,阅读代码时相对之前比较困难。
创建型模式
- 单例模式
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,节省内存空间,提高系统性能,全局访问。没有接口,不能继承,与单一职责原则冲突。
- 工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法是一个类的实例化延迟到子类。使代码结构清晰,有效的封装变化,对调 用者屏蔽具体的产品类,降低耦合度。每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
- 抽象工厂模式
为创建一组相关或者相互依赖的对象提供一个接口,而无需指定他们的具体类;抽象工厂是工厂方法的升级版本,它用来创建一组相关或者相互依赖的对象。可以在类的内部对产品族进行约束,产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
- 建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不通的表示,建造者模式与工厂模式相比较只是多了一个“导演类”的角色,所以建造者模式一般用来创建过程更为复杂的对象。
- 原型模式
通过拷贝原型实例来创建新的对像,简化了对象的创建过程。模式使用Object类的本地方法clone,它直接操作内存中的二进制流,故提高了创建对象的性能。
结构模式
- 适配器模式
建一个类的接口转化为客户希望的另一个接口;使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作,提高了类的复用、增加了类的透明度、灵活性好;但过多地使用适配器,会让系统非常零乱,不易整体进行把握。
- 桥接模式
将抽象部分和它的实现部分分离,使得他们可以独立的变化;抽象和实现的分离、优秀的扩展能力、实现细节对客户透明;桥接模式的引入会增加系统的理解与设计难度。
- 组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构;高层模块调用简单、节点自由增加,在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
- 装饰模式
动态的给一个对象添加一些额外的职责;代理模式主要是控制对某个特定对象访问,而装饰模式主要是为了给对象添加行为。装饰类和被装饰类可以独立发展,易于扩展,降低了耦合。
- 外观模式
为子系统中的一组接口提供一个一致的界面;减少系统相互依赖、提高灵活性、提高了安全性;不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
- 代理模式
为其它对象提供一种代理以控制这个对象的访问;代理模式主要是控制对某个特定对象访问,而装饰模式主要是为了给对象添加行为。职责清晰、高扩展性、智能化,但系统的系统性能下降、实现过程相对复杂。