[TOC]
(单一职责、开放封闭、里氏替换、依赖倒置、合成聚合复合、接口隔离、迪米特法则)
-
单一原则:就一个类而言,应该只专注于做一件事和仅有一个引起它变化的原因,实现高内聚。
-
开放封闭:软件实体应当对扩展开放,对修改关闭。要做到开闭有两个要点:①抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点;②封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变得复杂而换乱。
-
依赖倒置原则: 高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。即面向接口编程。该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所替代。
- 任何变量都不应该持有一个指向具体类的指针或者引用;
- 任何类都不应该从具体类派生;
- 任何方法都不应该覆写它的任何基类中的已经实现的方法。
-
里氏替换原则:子类对象必须能够替换掉所有父类对象。简单的说就是能用父类型的地方就一定能使用子类型,子类一定是增加父类的能力而不是减少父类的能力,因为子类比父类的能力更多,把能力多的对象当成能力少的对象来用当然没有任何问题。
-
接口隔离原则:接口要小而专,绝不能大而全.臃肿的接口是对接口的污染,既然接口表示能力,那么一个接口只应该描述一种能力,接口也应该是高度内聚的。
-
合成聚合复用原则:优先使用聚合或合成关系复用代码。
-
迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。迪米特法则简单的说就是如何做到"低耦合",门面模式和调停者模式就是对迪米特法则的践行。
创建型抽象了实例化过程,它们帮助一个系统独立于如何创建、组合和表达它的那些对象。
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 优点(效果):
- 它分离了具体的类。 它将客户和类的实现分离,客户通过它们的抽象接口操作实例。
- 它使得易于交换产品系列。 它只要改变具体的工厂,就可以使用不同的产品配置。 网关那边就可以采用这种方式实现。
- 它有利于产品的一致性。 当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用一个系列中的对象。
缺点:
- 难以支持新种类的产品。 接口确定了可以被创建的产品集合,如果需要增加产品,则破坏了开放封闭原则。
适用场景:
- 一个系统要独立于它的产品
- 一个系统要由多个产品系列中的一个来配置时。
- 当你强调一系列相关产品对象的设计以便进行联合使用时。
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用场景:
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
效果:
- 它使你可以改变一个产品的内部表示。Builder 对象提供给导向器一个构造产品的抽象接口,隐藏了内部实现细节。当你需要改变该产品的内部表示时,只需要创建一个新的生成器。
- 它将构造代码和表示代码分开(解耦)
- 它使你可以对构造过程进行更精细的控制
现实应用:
- spring的beanDefination的构造过程
定义:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂模式使一个类的实例化延迟到其子类。
适用场景:
- 当一个类不知道它所必须创建的对象的类的时候
- 当一个类希望由它的子类来指定它所创建的对象的时候
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
效果:
- 灵活性。用工厂方法在一个类的内部创建对象通常比直接创建对象更加灵活。
- 连接平行的类层次。将一个类的职责委托给一个独立的类的时候,就产生了平行类层次。
缺点:
- 客户端可能仅仅为了创建一个特定的ConcreteProduct对象,就不得不创建Creator子类
现实应用:
定义:用原型实例指定创建对象的种类,并通过拷贝这个原型来创建新的对象。
适用性:
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载
- 为了避免创建一个与产品层次平行的工厂类层次
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手动实例化该类更方便些。
优点:
- 运行时刻增加和删除产品。 允许只通过客户注册原型实例,就可以将一个新的具体产品类并入系统。它比创建型模式更为灵活,因为客户可以在运行时刻建立和删除原型。
- 改变值以指定新对象。
- 改变结构以指定新对象
- 减少子类的构造
- 用类动态配置应用
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性:
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它
- 当这个唯一实例是通过子类实例化扩展的,并且客户应该无需修改代码就能使用一个扩展的实例时。
优点:
- 对唯一实例的受控访问
- 允许可变数目的实例
- 允许对操作和表示的精化
实际应用:
- 数据源
结构型类模式采用继承机制来组合接口或实现,这一模式有助于多个独立开发的类库协同工作。 结构型对象模式不是对接口或实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能的一些方法。
定义:将一个接口转换为客户希望的另一个接口,适配器模式使得原本由于接口不兼容而不能在一起工作的那些类可以在一起工作。 别名包装器(wrapper)
适用性:
- 你想使用一个已经存在的类,而它的接口不符合你的需求。
- 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作
- 你想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父接口。
效果:
- 用一个具体的Adapter对Adaptee和Target进行匹配。
- 使用Adapter可以重新定义Adaptee的部分行为,因为Adapter是Adaptee的子类。
- 允许一个Adapter与多个Adaptee同时工作
定义:将抽象部分和它的实现部分分离,使它们都可以独立地变化。
适用性:
- 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。
- 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充
- 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
- 你想在多个对象间共享实现,但同时要求客户并不知道。
效果:
- 实现接口以及其实现部分的分离
- 提高可扩充性,你可以独立地对Abstraction和Implementor层次结构进行扩充。
- 实现细节对客户透明
定义:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得客户对单个对象和复合对象的使用具有一致性。
适用性:
- 你想表示对象的部分-整体层次结构
- 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
效果:
- 包含了基本对象和组合对象的层次结构,基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合。
- 使得更容易加新类型的组件
- 使你的设计更加一般化。容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。
定义:动态地给一个对象添加一些额外的职责。就扩展功能而言,装饰模式比生成子类的方式更加灵活。 适用性:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
- 处理那些可能撤销的职责
- 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的拓展,为了支持每种组合而产生大量的子类,使得子类数目爆炸性增长。
效果:
- 比静态继承更灵活。
- 避免在层次结构高层的类有太多的特征。
缺点:
- 产生许多小对象: 这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。
应用:
- Dubbo的过滤器
定义:为系统中的一组接口提供一个一致的界面,Façade模式定义一个高层接口,这个接口使得这一子系统更加容易使用。
适用性:
- 为一个复杂的子系统提供一个简单接口时,子系统往往因为不断演化变得越来越复杂,给用户带来使用上的困难。
- 客户程序和抽象类的实现部分之间存在很大的依赖。引入Facade将这个子系统与客户以及其他子系统分离,可以提高子系统独立性和可移植性。
- 当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。
优点:
- 对客户屏蔽了系统组件,因而减少了客户处理对象的数目并使得子系统使用起来更加方便。
- 它实现子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。 松耦合关系使得子系统组件的变化不影响它的客户。Fcade模式有助于建立层次结构系统,也有助于对对象之间的依赖关系分层。
应用:
- 控制器
定义:运用共享技术有效支持大量细粒度的对象。
适用性:
- 一个应用程序使用了大量的对象
- 完全由于使用大量的对象,造成很大的存储开销
- 对象的大多状态都可变为外部状态
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖对象标识
效果:
- 节省空间开销
定义:为其他对象提供一个代理以控制这个对象的访问。
适用性:
- 远程代理:为一个对象在不同地址空间提供局部代表。
- 虚代理:根据需要创建开销很大的对象。
- 保护代理:控制对原始对象的访问,保护代理用于对象应该有不同的访问权限的时候。
- 智能代理: 取代了简单的指针,它在访问对象时执行一些附加操作如:引入计数、检查等。
效果:
- 远程代理隐藏一个对象存在不同地址空间的事实
- 虚拟代理可以进行优化,例如根据要求创建对象
- 保护代理和智能代理都允许方位一个对象时有一些附加的内务处理
应用:
- Spring aop
- mocktio , powermock
- Mybatis
定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链上传递该请求,直到一个对象处理它为止。
适用性:
- 有多个的对象可以处理一个请求,那个对象处理该请求运行时刻自动确定
- 你想在不明确指定接受者的情况下,向多个对象中的一个提交请求。
- 可以处理一个请求的对象集合应被动态指定。
效果:
- 降低耦合度:使得一个无需知道是其他哪个对象处理其请求。
- 增强了给对象指派职责的灵活性:当对象中分派职责时,职责链给你更多的灵活性。你可以通过在运行时刻对该链进行动态的增加或修改来增加或改变厨艺一个请求的那些职责。
- 不保证被接受:有可能该请求得不到处理
定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可取消的操作。
适用性:
- 抽象出待执行的动作以参数化某对象
- 在不同的时刻指定、排列和执行请求。
- 支持取消操作
- 支持修改日志,这样当系统奔溃时,这些修改可以被重做一遍
- 用构建在原语操作上的高层操作构造一个系统。
效果:
- 将调用操作的对象与知道如何实现该操作的对象解耦
- Command是头等对象,它们可像其他的对象一样被操纵和扩展
- 你可以将多个命令转配成一个复合命令
- 增加新的Command很容易,因为这无需改变已有的类
定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
适用性:
- 当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式
- 该文法简单对于复杂的文法,文法的类层次变得庞大而无法管理
- 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换为成另一种形式。
优点:
- 易于改变和拓展文法。因为该模式使用类来表示文法规则,你可使用继承来改变和扩展该文法
- 易于实现文法。定义抽象语法树中各个节点的类的实现大体类似。
缺点:
- 复杂的文法难以维护
定义:提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。 适用性:
- 访问一个聚合对象的内容,而无需暴露它的内部表示。
- 支持对聚合对象的多种遍历
- 为遍历不同的聚合结构提供一个统一的接口
效果:
- 它支持以不同的形式遍历一个聚合
- 迭代器简化了聚合的接口。 聚合本身不需要类似的遍历接口了
- 在同一个聚合上可以有多个遍历。
应用:
- java list的遍历
定义: 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
适用性:
- 一组对象以定义良好但复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解
- 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象
- 想定制一个分布在多个类中的行为,而又不想生成太多的子类
优点:
- 减少了子类生成。 中介者将原本分布于多个对象间的行为集中在一起,改变这些行为只需要生成Meditator的子类即可。
- 它简化了对象协议。使用Mediator和各个Colleague间的一对多的交互来替换多对多交互,一对多的关系更易于理解、维护和拓展。
- 它对对象如何协作进行了抽象
- 它使控制集中化。这可能使得中介者自身成为一个难以维护的庞然大物。
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到保持状态。
适用性:
- 必须保持一个对象在某一时刻的状态,这样以后需要时它才能恢复到先前的状态
- 如果一个用接口来让其他对象直接得到这些对象,将会暴露对象的实现细节并破坏对象的封装性
效果:
- 保护封装边界:使用备忘录可以避免暴露一些只应由原发器管理却又必须存储在原发器之外的信息。
- 它简化了原发器:这就把所有存储管理的重任交给Originator,让客户管理它们请求的状态将会简化Originator, 并且使得客户工作结束时无需通知原发器。
- 使用备忘录可能代价很高:如果原发器在生成备忘录时必须拷贝大量的存储信息,或者客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销。
- 定义窄接口和宽接口
- 维护备忘录的潜在代价:可能会产生大量的存储开销
定义:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时所有依赖于它的对象都得到通知并自动刷新。
适用性:
- 当一个抽象模型有两个方面,其中一个方面依赖与另一个方面。将这二者封装在独立的对象中以使他们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
优点:
- 目标和观察者间的抽象耦合
- 支持广播通信
缺点:
- 意外的更新。 因为一个观察者不知道其他观察者的存在,它可能对改变目标的最终代价一无所知。如果依赖准则的定义或维护不当,常常会引起错误的更新,这种错误通常很难捕捉。
应用:
- 消息队列
定义:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
适用性:
- 一个对象的行为取决于它的状态,并且必须在运行时刻根据状态改变它的行为
- 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态
效果:
- 它将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。State模式将所有与一个特定状态相关的行为都放在一个对象中,因为所有与状态相关的代码都存在于某个子类中,所以通过定义新的子类可以很容易的增加新的状态和转换。
- 它使得状态转换明显化
- state对象可以被共享。 如果state对象没有实例变量,那么context对象可以共享一个state对象。
定义: 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替代。本模式使得算法的变化可以独立于使用它的客户。
适用性:
- 许多相关的类仅仅是行为有异。策略提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不同变体。
- 算法使用客户不应该知道的数据。可使用策略模式避免暴露复杂的、与算法相关的数据结构。
- 一个类定义多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。
效果:
- 相关算法系列:定义了一系列可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
- 一个替代继承的方法。将算法封装在独立的Strategy类中,使得你可要独立于其Context改变它,使它易于切换、易于理解、易于扩展。
- 消除了一些条件语句。当不同的行为堆砌在一个类中,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Stragety类中消除了这些条件语句。
定义: 定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
适用性:
- 一次性实现一个算法的不变部分,并将可变行为部分留给子类实现
- 各个子类中公共行为应该被提取出来集中到一个公共父类中以避免代码重复。
- 控制子类扩展。模板方法只在特定点调用“hook”操作,这样就只允许在这些点进行扩展。
效果:
- 提高代码复用性
定义: 表示一个作用于某个对象结构中的各个元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用性:
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相干的操作,而你想避免让这些操作“污染”这些对象的类。
- 定义对象结构的类很少改变,当经常需要在此结构上定义新的操作。
效果:
- 访问者模式使得易于增加新的操作。访问者使得增加依赖于复杂对象结构的构建的操作变得容易。
- 访问者几种相关的操作而分离无关的操作。相关的行为不是分布在定义该对象结构的各个类上,而是集中在一个访问者中。
- 增加新的ConcreteElement类很困难。