一、为什么要学习设计模式?
1.概念
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
2.目的
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。其核心目的就是为了让系统更有弹性,能够面对改变,如需求变更,系统升级等。
二、面向对象编程的设计原则
1.最少知识原则
1)概念:也叫迪米特法则。即一个对象应对其他对象有尽可能少的了解。2)目的:为了降低类与类之间的耦合度。耦合越高,当一个类发生改变时,对另一个类的影响也越大。3)使用:在一个类的方法中只引用类自身的成员变量或方法以及通过参数传递进来的参数对象。
2.开闭原则
概念:模块应对扩展开放,而对修改关闭。模块应尽量在不修改原有代码的情况下进行扩展。
3.里氏代换原则
概念:使用父类型或超类型的地方都可以用其子类型进行替换。
4.依赖倒置原则
1)概念:抽象不应该依赖于细节,细节应依赖于抽象。要针对接口编程,而不是针对实现编程。2)针对具体实现类编程是静态的,在代码编译过程中就已经确定好,程序运行过程中无法修改。3)针对接口编程是动态的,依据里氏代换原则,使用超类型的地方都可以用其子类型替换,在程序运行过程中可以传递超类型的不同子类实现,让系统更有弹性。所以在传递参数,或者使用组合聚合关系中,尽量引用层次高的类。
5.合成复用原则
1)概念:多用组合,少用继承。2)使用:在一个新的对象中使用一些已有的对象,达到复用已有功能的目的。3)通过继承来实现代码复用,一旦父类中的代码进行修改,其所有的子类都将受到影响。
6.接口隔离原则
1)概念:一个类对另一个类的依赖应建立在最小的接口上,不应该依赖它不需要的接口。2)使用:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。
三、设计模式分为三种类型,共23种。
1.创建型模式5种
单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
2.结构型模式7种
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3.行为型模式11种
模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
四、创建型模式:
1.Singleton(单例模式)
1)概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2)实现:实现单例模式一般需要私有化构建方法,一个静态变量存放单例对象和一个静态方法提供全局访问点。3)多线程情况下保证单例方法:1.使用synchronize将getInstance()变成同步方法。2.使用静态初始化器创建单例对象,也称急切的单例模式。3.使用双重检查加锁的方式。4)优点:1.减少内存开支。2.减少创建对象所需系统的性能开销。3.避免资源的多重占用。5)缺点:1.扩展困难,只能直接对代码进行修改。6)适用场景:1.要求生成唯一序列号的环境。2.整个项目中只需要一个共享访问点。3.创建一个对象需要消耗的资源过多。
2.Prototype(原型模式)
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
1)实现:在Java中已有实现,通过实现Cloneable和重写clone()方法实现。2)浅克隆与深克隆:1.浅克隆,只负责克隆按值传递的数据(比如基本数据类型、String类型),而其他对象的引用都仍然指向原来的对象。2.深克隆,除了浅克隆要克隆的值外,还负责克隆引用类型的数据。浅克隆与深克隆建议分开实现。3)优点:1.创建新对象比较复杂时,可以简化对象的创建过程。2.利用深克隆可以保持对象的状态。4)缺点:1.每一个类都必须配备一个克隆方法。对于已经存在的类需要进行改造。2.深度克隆时可能需要比较复杂的代码。5)适用场景:1.当一个类的实例只能有几个不同状态组合中的一种时。2.对象创建成本较大时,可以利用拷贝来创建。
3.Builder(建造者模式)
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1)原因:复杂对象通过new直接创建,比较麻烦,可读性不好,参数意思不明确,容易产生错误。2)表示:一个产品常有不同的组成成分作为产品的零件,它们叫做产品的内部表象,不同的产品可以有不同的内部表象。3)实现:通过建造者角色将产品的内部表象与创建分离,将产品的内部表示的设置和构建都放在建造者角色内,要创建复杂的产品对象,先设置产品的内部表示最后创建对象。4)优点:1.建造者独立,便于扩展。2.提高代码的可读性。5)缺点:1.会创建多余的Builder对象。6)适用场景:1.需要生成的产品对象有复杂的内部结构。2.需要生成的产品对象的属性相互依赖,建造模式可以强制实行一种分步骤进行的建造过程。
4.Factory Method(工厂方法模式)
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。
1)实现:利用继承,在父类中定义一个抽象工厂方法,在子类中进行实现,通过子类来创建对象。这样,客户只需要知道超类型即可,而由子类负责决定具体类型。2)优点:1.扩展性好,在增加产品类时,只需要适当修改或新增具体工厂类即可。2.高层模块只需要知道产品的抽象类,不需要关注具体产品实现类,降低耦合度。3)缺点:1.当需要添加新的产品类时需要添加对应的具体工厂类,增加代码复杂度。4)适用环境:1.工厂方法模式是new一个对象的替代品2.需要可扩展的框架,一个类不知道它所需要的对象的类,一个类通过其子类来指定创建哪个对象。
5.Abstract Factory(抽象工厂模式)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
1)实现:利用组合,在父类中定义创建一系列相关对象的抽象工厂方法,通过子类去指定具体创建的对象。2)优点:1.一个工厂可以创建出一个产品族中的所有对象。2.分离接口和实现,客户端使用抽象工厂来创建需要的对象,而不需要知道具体的实现,降低耦合度。3.使切换产品族变得容易;4.产品族内的约束为非公开状态。3)缺点:1.不太容易扩展新的产品;如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。4)适用场景:1.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。2.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
五、结构型模式
1.Adapter(适配器模式)
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
1)目的:将一个接口--被适配者转换成目标接口,使得通过目标接口便可以调用被适配接口的对象。根据实现方式分为类适配器和接口适配器,类适配器通过组合的方式,适配器类直接引用被适配对象。接口适配器通过继承或实现的方式,适配器类同时实现目标接口与被适配接口,可以通过重写重定义Adaptee的部分行为 。2)优点:1.可以让接口不合的系统现有类,功能得到复用。2.在实现适配时,可以对适配的接口进行扩展。3.可以让两个没有任何关系的类在一起运行。3)缺点:过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如调用A接口,其实内部被适配成B的接口实现。4)适用场景:它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题。5)缺省适配器:为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有接中进行扩展。用意是为了方便建立一个不平庸的类而提供的一种平庸实现。
2.Bridge(桥接模式)
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
3.Decorator(装饰模式)
动态地给一个对象添加一些额外的职责。就扩展功能而言, 它比生成子类方式更为灵活。
1)目的:为了扩展对象的功能,是继承的一个替代方案。装饰模式有透明和半透明两种:透明装饰者模式增强功能、不改变接口。半透明装饰者模式增强功能、改变接口。适配器模式:不增加功能、改变接口。2)优点:1.扩展对象的功能比继承更好的灵活性。2.通过使用不同的装饰类及对它们的排列组合,可以创造出许多不同行为的组合。3)缺点:1.造成类数量太多。2.增加代码复杂度。3.调。4)适用场景:1.需要扩展一个类的功能, 或给一个类增加附加功能。
4.Composite(组合模式)
将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
5.Facade(外观模式)
为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
1)目的:其目的简化接口。能够有选择性地暴露方法。,保护子系统中的接口。2)优点:1.减少系统的相互依赖。2.提高了灵活性;只要子系统的变化不影响到门面对象,外界便不需要发生变化。3.提高安全性;外界只能访问门面上开通的方法。4.简化接口,易于使用。5.更好的划分访问层次;通过合理使用Facade,有些方法是对系统外的,有些方法是系统内部使用的。3)缺点:1.不符合开闭原则,可能需要修改外观角色代码。2.外观类高耦合,一旦有变更,只能对外观类进行修改。修改外观类需要非常谨慎。4)适用场景:1.简化并统一一个很大的接口或者一群复杂的接口时。2.子系统相对独立,外界对子系统只要黑箱操作即可。
6.Flyweight(享元模式)
运用共享技术有效地支持大量细粒度的对象。
7.Proxy(代理模式)
为其他对象提供一个代理以控制对这个对象的访问。
1)动态代理之所以被称为动态,是因为运行时才将它的类创建出来。代码开始执行时,还没有proxy类,它是根据需要从你传入的接口集创建的。2)代理种类:代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理访问的方式有许多种。1.远程代理管理客户和远程对象之间的交互。2.虚拟代理控制访问实例化开销大的对象。3.保护代理基于调用者控制对象方法的访问。3)缺点:代理会造成你的设计中类的数目增加。4)比较:代理在结构上类似装饰者,但是目的不同。装饰者模式为对象加上行为,而代理则是控制访问。
六、行为型模式
1.Template Method(模板方法模式)
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
1)原理:将算法定义成一组步骤,其中任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。2)防止依赖腐败:避免低层组件调用高层组件。
2.Command(命令模式)
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
1)实现:客户发出一个请求就像发出一个命令或指指令一样,需要有接收者接收这个请求,然后进行处理。不同的请求,可能需要不同的接收者接收和处理,实现客户与这些具体接收者进行解耦。支持请求排队或记录请求日志以及可取消的操作。2)优点:1.调用者角色与接收者角色之间解耦。2.可扩展,新的命令很容易加入到系统中。3.宏命令使系统功能更强大。3)缺点:类数量随命令数量增长而增长。可能造成类数量过多。
3.Iterator(迭代器模式)
提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
4.Observer(观察者模式)
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
1)目的:让主题和观察者实现松耦合。任何时候我们都可以任意添加或者删除观察者。2)优点:1.观察者和被观察者之间是抽象耦合。2.建立一套触发机制。3)缺点:1.一个被观察者,多个观察者,开发和调试就会比较复杂。2.顺序执行需要考虑执行效率;在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。一般考虑采用异步的方式。4)适用场景:1.事件多级触发场景。2.跨系统的消息交换场景, 如消息队列的处理机制。
5.Mediator(中介模式)
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
6.Memento(备忘录模式)
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
7.Interpreter(解析器模式)
给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
8.State(状态模式)
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
1)原理:状态模式它通过改变对象内部的状态来帮助对象控制自已的行为。行为封装在状态对象中,随着当前状态在状态对象集合中游走改变。2)比较:策略模式是围绕可以互换的算法来创建成功业务的。状态模式是围绕可转换的状态来控制行为。3)优点:可以简单地通过改变状态对象来改变应用的行为。4)缺点:导致设计中类的数目大量增加。
9.Strategy(策略模式)
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
1)原理:封装变化原则:把应用中需要变化的部分和那些不需要变化的部分分离开。2)优点:算法可以自由切换,扩展性良好。3)缺点:策略类数量增多,所有策略类都需要暴露给客户,增加客户的难度。4)使用场景:1.当需要频繁的增加else if或case时,可以考虑使用策略模式减少它们之间的耦合度。2.多个类只有在算法或行为上稍有不同的场景。3.算法需要自由切换的场景。
10.Chain of Responsibility(职责链模式)
为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
11.Visitor(访问者模式)
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。