设计模式-笔记总结
设计模式概述(Design Patterns)
具体内容可访问:http://c.biancheng.net/view/1317.html
目录
5、里氏替换原则(Liskov Substitution Principle)
6、依赖倒置原则(Dependence Inversion Principle)
7、单一职责原则(Single Responsibility Principle)
8、接口隔离原则(Interface Segregation Principle)
10、合成复用原则(Composite ReusePrinciple)
30、责任链模式(Chain of Responsibility)
1、软件设计模式概述
(1)设计模式的概念。设计模式是一套被人反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计中一些不断发生的问题,以及这些该问题的解决方案。换句话说,它是特定解决问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用,其目的是为了提高代码的可重用性、代码的可读性、代码的可靠性。
(2)学习设计模式的意义。设计模式的本质是面向对象设计原则的实际应用,是对类的封装、继承性和多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式有以下优点
-
可以提高程序员的思维能力、编程能力和设计能力。
-
是程序设计更加标准化、代码编制更加工程化、使软件开发效率大大提高,从而缩短软件的开发周期。
-
使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
(3)软件设计模式的基本要素。设计模式使人们可以更加方便简单的复用成功的设计和体系结构,它通常包含以下几个基本要素:模式名称、别名、动机、问题、解决方案、效果、结构、模式角色、合作关系、实现方法、通用性等、已知应用、例程、模式拓展及相关模式等。
2、GoF的23种设计模式
设计模式有两种分类方法,即根据模式的目的来划分和模式的作用来划分。
-
根据模式的目的来划分
-
创建型模式:用于描述“怎样创建对象”,它的主要特点是“将创建对象和使用分离”。GOF中提供了单例、原型、工厂方法、抽象工厂、建造者等五种建造者模式。
-
结构性模式:用于描述如何将类或对象按照某种布局组成更大的结构,GOF中提供了代理、适配器、桥接、装饰、外观、享元、组合等7种结构型模式。
-
行为型模式:用于描述类或对象通过如何协作共同完成单个对象无法完成的任务,以及怎样分配职责。GOF中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等11种行为型模式。
-
-
根据模式的作用来划分
-
类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。GoF中的工厂方法、(类)适配器、模板方法、解释器属于该模式。
-
对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。GoF 中除了以上 4 种,其他的都是对象模式。
-
-
GOF的23种设计模式的功能
前面说明了 GoF 的 23 种设计模式的分类,现在对各个模式的功能进行介绍。
-
单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
-
原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
-
工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
-
抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
-
建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
-
代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
-
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
-
桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
-
装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
-
外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
-
享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
-
组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
-
模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
-
策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
-
命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
-
职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
-
状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
-
观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
-
中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
-
迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
-
访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
-
备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
-
解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
-
3、UML中的类图及类图之间的关系
Tip:类之间的关系:根据类与类之间的耦合度从弱到强排列,UML中类图之间有以下几种关系:
-
依赖关系
-
关联关系
-
聚合关系
-
组合关系
-
泛化关系
-
实现关系(泛化关系和实现关系的耦合度相等)
4、开闭原则(Open Closed Principle)
-
开闭原则的定义:软件应当对拓展开放,对修改关闭,意思是:当应用需求发生改变时,在不修改软件实体的源代码或者二进制的前提下,可以拓展模块的功能,使其满足新的需求。
-
开闭原则的作用:
-
对软件测试的影响。遵守开闭原则,测试只需要测试拓展的代码就可以了。
-
提高代码的复用性。粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。(细粒度模型,通俗的讲就是将业务模型中的对象加以细分,从而得到更科学合理的对象模型,直观的讲就是划分出更多对象。)
-
可以提高软件的可维护性。遵守开闭原则的软件,其稳定性高,持续性强,从而易于维护和拓展。
-
-
开闭原则的实现方法
可以通过“抽象约束、封装变化”来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。
5、里氏替换原则(Liskov Substitution Principle)
-
里氏替换原则的定义:里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原则原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。
-
里氏替换原则的作用:
-
里氏替换原则是实现开闭原则的重要原则之一。
-
它克服了继承中重写父类造成的可复用性变差的缺点。
-
它是正确动作的保证。即类的拓展不会给原有的系统引入新的错误,降低了代码出错的可能性。
-
加强了代码的健壮性,同时变更时可以做到非常好的兼容性,提高代码的维护性、可拓展性,降低需求变更时引入的风险。
-
-
里氏替换原则的实现方法:
里氏替换原则通俗的讲就是:“子类可以拓展父类的功能,但不能改变父类原有的功能”,也就是说,子类在继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
根据上述理解,对里氏替换原则的定义可以总结如下:
-
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
-
子类可以增加自己特有的方法
-
当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松。
-
当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类的方法更严格或者相等。
-
6、依赖倒置原则(Dependence Inversion Principle)
-
依赖倒置原则的定义
面向过程的开发,是高层依赖底层,上层依赖下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。
面向对象的开发,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化太大,降低了客户程序与实现细节的耦合度。
总结:依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖具体实现。简单说就是要求对抽象进行编程,而不是对实现进行编程。
-
依赖倒置原则的作用
-
依赖倒置原则可以降低类之间的耦合度
-
依赖倒置原则可以提高系统的稳定性
-
依赖倒置原则可以减少并行并发引起的风险
-
依赖倒置原则可以提高代码的可读性和可维护性
-
-
依赖倒置原则的实现方法
依赖倒置原则的目的就是通过要面向接口编程来降低类之间的耦合度,所以我们应该遵循以下四点
-
每个类尽量提供接口或抽象类,或者两者都具备
-
变量的声明类型尽量是接口或者抽象类
-
任何类都不应该从具体类派生
-
使用继承时尽量遵循里氏替换原则
-
7、单一职责原则(Single Responsibility Principle)
-
单一职责原则的定义
单一职责原则(Single Resposibility Principle,SRP)又称单一功能原则 ,单一职责规定一个类应该有且仅有一个引起他变化的原因,否则就应该拆分。
该原则提出对象不应该承担太多的责任,承担太多的责任,有以下两个缺点:
-
一个职责的变化可能会削弱或抑制这个类实现其他职责的能力;
-
当客户端需要对该对象的某一个职责时,不得不将其他不需要的职责全部包含进来,从而造成冗余代码和代码浪费。
-
-
单一职责原则的优点
单一职责原则的核心就是控制类粒度大小、将对象解耦、提高其内聚性,遵循单一职责原则具有以下优点:
-
降低类的复杂度。一个类只负责一项职责,其逻辑性要比负责多项职责简单的多。
-
提高类的可读性。复杂度降低,自然其可读性会提高。
-
提高系统的可维护性。可读性提高,那自然就更容易维护了。
-
变更引起的风险降低。变更是必然的,如果单一职责原则不遵守好,当修改一个功能时,可以显著降低对其他功能的影响
-
-
单一职责的实现方法
需要设计人员发现类的不同职责并将其分离,再分装到不同的类或模块中。发现类的多重职责需要设计人员具有较强的分析能力和相关重构经验。
8、接口隔离原则(Interface Segregation Principle)
-
接口隔离原则的定义
接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小和更具体的接口,让接口中包含用户感兴趣的方法。另外一个定义是:一个类对另一个类的依赖应该建立在最小的接口上。
以上两个接口的含义是:要为各个类建立他们需要的专用接口,而不要试图去建立一个很庞大的接口供所有的依赖他的类去调用。
接口隔离原则和单一原则都是为了提高类的内聚性、降低他们之间的耦合性,体现了封装的思想,但是两者是不同的:
-
单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
-
单一职责原则主要是约束类,她真对的是程序中的实现和细节,接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
-
-
遵循接口隔离原则的优点
接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下优点:
-
将臃肿庞大的接口进行分解为多个粒度更小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
-
接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
-
如果接口的粒度大小定义合理,能够保证系统的稳定性,但是如果定义太小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
-
使用多个专门的接口还可以体现接口的层次,因为可以通过接口继承,实现对总接口的定义。
-
能减少项目工程中代码的冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口时,被迫设计冗余的代码。
-
-
接口隔离原则的实现方法
在具体的应用接口隔离原则上,应该有以下几个规则来衡量
-
接口尽量少,但是要有限度,一个接口只服务与一个子模块或业务逻辑。
-
为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
-
了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
-
提高内聚,减少对外交互。使接口用最少的方法来完成最多的事。
-
9、迪米特法则(Law of Demeter)
-
迪米特法则的定义
迪米特法则,又叫最少知识原则。迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
-
迪米特法则的优点
迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。
-
降低了类之间的耦合度,提高了模块的相对独立性。
-
由于亲合度降低,从而提高了类的可复用率和系统的扩展性。
-
-
费米特法则的实现方法
-
从依赖者的角度来说,只依赖应该依赖的对象。
-
从被依赖者的角度说,只暴露应该暴露的方法。
使用迪米特法则应该注意以下6点
-
在类的划分上,应该创建弱耦合的类。类和类之间的耦合度越低,就越有利于实现可复用的目标。
-
在类的结构设计上,尽量降低类成员的访问权限。
-
在类的设计上,优先考虑将一个类设置为不变类。
-
在对其他类的引用上,将引用其他对象的次数降到最低。
-
不暴露类的属性成员,而提供getter和setter。
-
谨慎使用序列化(serializable)
-
10、合成复用原则(Composite ReusePrinciple)
-
合成复用原则定义
合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。
-
合成复用原则的重要性
通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点。
-
继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
-
子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
-
它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
-
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点。
-
它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
-
新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
-
复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
-
合成复用原则的实现方法
合成复用原则是通过将已有的对象纳入到新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。
11、创建型模式的特点和分类
创建型模式的主要关注点是“怎么样创建对象?”,它的主要特点是“将对象的创建和使用分开”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要 知道商品是如何创建出来的一样。
创建型模式分为以下几种:
-
单例模式(Singleton):某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展为有限多例模式。
-
原型模式(Prototype):将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
-
工厂方法(FactoryMethod):定义一个用于创建产品的接口,由子类决定生产什么产品。
-
抽象工厂(AbstractFactory):提供一个创建产品族的接口,其每个子类可以生产一系列相关产品。
-
建造者模式(Builder):将一个复杂对象分解为多个相对简单的部分,然后根据不同需要分别创建他们,最后构建成复杂的对象。
12、单例模式(Simgleton)
1、单例模式的定义:指一个类只有一个实例,且该类能自行创建一个实体的一种模式。
2、单例模式的特点:
-
单例类只有一个实例对象。
-
该单例对象必须由单例类自己创造。
-
单例类对外提供一个访问该单例的全局访问点。
3、单例模式的优缺点:
优点:
-
单例模式可以保证内存中只有一个实例,减少了内存的消耗。
-
可以避免对资源的多重占用。
-
单例模式设置全局访问点,可以优化和共享资源的访问。
缺点:
-
单例模式一般没有接口,拓展困难,如果要扩展,则除了修改原来的代码外,没有第二种途径,违背开闭原则。
-
在开发测试中,单例莫斯不利于代码调试,在调试过程中,如果单例没有执行完,也不能生成新的对象。
-
单例模式的功能代码通常只写在一个类中,如果设计不合理,则很容易违背单一职责原则。
4、单例模式的应用场景:
对于Java来说,单例模式可以保存在一个JVM中只存在单一实例,应用场景如下:
-
需要频繁的创建一些类,使用单例可以降低系统的内存压力,减少GC。
-
某类只要求生成一个对象的时候。
-
某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
-
某类需要频繁的实例化,而创建的对象又被频繁的销毁的时候,如多线程的线程池、网络连接池等。
-
频繁访问数据库或文件的对象。
-
对于一些控制硬件级别的操作,或者从系统上来讲是单一控制逻辑的操作,如果是多个实例,则系统会完全乱套。
-
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象的访问速度。如Web中的配置对象、数据库的连接池等。
13、原型模式(Prototype)
在有些系统中,存在大量相同或者相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成的对象就很高效。
1、原型模式的定义:
原型模式的定义如下:用一个已经创建好的实例作为原型,通过复制该原型对象来创建一个和原型相同或者相似的新对象。用这种方式创建对象非常高效,根本无须知道对象创建的具体细节。
2、原型模式的优缺点:
优点:
-
Java自带的原型模式基于内存二进制的复制,在性能上比直接New一个对象更加优良。
-
可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要使用时使用(例如恢复到历史的某一状态),可辅助实现撤销操作。
缺点:
-
需要每一个类都配置一个clone方法。
-
clone方法位于类的内部,当对已有的类进行改造时,需要修改代码,违背了开闭原则。
-
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深度克隆,实现起来比较麻烦。
14、简单工厂模式(Simple Factory)
1、工厂的定义:
工厂模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。
我们把创建的对象称为“产品”,把创建产品的对象称为“工厂”,如果创建的产品不多,只要一个工厂类就可以完成,这种模式叫做“简单工厂模式”。在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫静态工厂方法模式。
2、简单工厂的优缺点:
优点:
-
工厂类包含必要的逻辑判断,可以决定什么时候创建哪一个产品实例。客户端可以免除直接创建产品的职责,很方便的创建出相应的产品,工厂和产品的职责区分明确。
-
客户端无需知道所创建具体产品的类名,只需知道参数即可。
-
可以引入具体的配置文件,在不修改客户端代码的基础上更换和添加新的产品类
缺点:
-
简单工厂模式的工厂类型单一,负责所有的产品创建,职责过重,一旦异常,整个系统将受影响,且工厂类的代码非常臃肿,违背高聚合原则。
-
使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度。
-
系统扩展困难,一旦增加新的产品不得不修改工厂逻辑,在产品类型较多时,可能会造成逻辑过于复杂。
-
简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构。
3、应用场景:
对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。
15、工厂方法模式(Factory Method)
1、定义:工厂方法模式是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来的代码的情况下引进新的产品,即满足开闭原则。
2、工厂方法的优缺点:
优点:
-
用户只需要知道具体工厂的名称就可以得到所需要的产品,无须知道产品的具体创建过程。
-
灵活性增高,对于新产品的创建,只需多写一个相应的工厂类。
-
典型的解耦框架,高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
-
类的个数容易过多,增加复杂度。
-
增加了系统的抽象性和理解难度。
-
抽象产品只能生产一种产品,只关心产品的品牌。
3、应用场景
-
客户只知道创建产品的工厂名,而不知道具体的产品名,如TCL电视工厂等
-
创建对象的任务有多个具体的子工厂中的某一个完成,而抽象工厂只是提供创建产品的接口。
-
客户不关心创建产品的细节,只关心产品的品牌。
16、抽象工厂模式(Abstract Factory)
1、产品模式的定义和特点:
抽象(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模板的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
使用抽象工厂模式一般要满足以下条件:
-
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
-
系统一次只能消费其中某一族产品,即同族的产品一起使用。
2、抽象工厂的优缺点
优点:除了具有工厂模式的优点外,抽象工厂的其他优点主要为:
-
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
-
当需要产品族时,抽象工厂可以保证客户端始终使用同一个产品的产品组。
-
抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改源代码,满足开闭原则。
缺点:当产品族中需要增加一个新的产品时,所有工厂类都需要进行修改。增加了系统的抽象性和理解难度。
3、抽象工厂模式的拓展通常适用以下场景:
-
当需要创建的对象是一系列相互关联或互相依赖的产品族时,如电器工厂的电视机、洗衣机、空调等。
-
系统中有多个产品族,但每次只使用其中的某一产品时,如有人只喜欢穿某一个品牌的衣服和鞋。
-
系统中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。
17、建造者模式(Builder)
1、建造者模式的定义和特点
建造者模式(Builder)模式的定义:指将一个复杂的对象构造和他的表示分离,使同样的构造过程可以创建不同的表示,这样的设计模式成为建造者模式。它是将一个复杂的对分解为多个简单的对象,然后一步步构建而成。他将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
2、建造者模式的优缺点
建造者模式的优点:
-
封装性好,构建和表示分离。
-
拓展性好,各个具体的建造者相互独立,有利于系统的解耦。
-
客户端不必知道产品内部组成的具体细节,建造者可以对创建过程逐步细化,而不对其他模块产生任何影响,便于控制细节风险。
建造者模式的缺点:
-
产品的组成部分必须相同,这限制了其使用范围。
-
如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
建造者(Builder)模式和工厂模式的关注点不同,建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。
3、模式的应用场景
建造者模式唯一区别于工厂模式的是针对复杂对象的构建。也就是说,如果创建简单的对象使用工厂模式进行创建,如果创建复杂的对象则使用创建者模式。
当需要创建的产品具备复杂创建过程时,可以抽取出共性创建过程,然后交由具体实现类自定义创建流程,使得同样的创建行为可以生产出不同的产品,分离了创建与展示,使创建产品的灵活性大大增加。
建造者模式的主要应用场景:
-
建造者模式更注重方法的调用顺序,工厂模式注重创建对象。
-
创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样
-
关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建对象,还要知道对象由哪些部分组成。
-
建造者模式根据建造过程中的顺序不一样,最终对象部件的组成也不一样。
18、结构型模式概述
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构性模式,前者采用
继承机制来组织接口和类,后者采用组合或者聚合来组合对象。
结构型模式分为以下7种:
-
代理模式(Proxy):为某对象提供一种代理以控制该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些属性。
-
适配器(Adapter)模式:将一个类的接口转换成客户端需要的另外一种接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
-
桥接模式(Bridge):将抽象与实现分离,使他们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象与实现这两个可变维度的耦合度。
-
装饰模式(Decorator):动态的给对象增加一些职责,即增加其额外的功能
-
外观模式(Facade):为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
-
享元模式(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
-
组合模式(Composite):将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
19、代理模式(Proxy)
1、代理模式的定义:
代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。
2、代理模式的优缺点
优点:
-
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
-
代理对象可以扩展目标对象的功能
-
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了代码的可扩展性。
缺点:
-
代理模式会造成系统中类数量的增加
-
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢。
-
增加了系统复杂度。
3、代理模式的应用场景
使用代理模式主要有以下两个目的:一是保护目标对象,二是增强目标对象。
主要的应用场景:
-
远程代理:这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。
-
虚拟代理:这种方式主要是用于创建的目标对象很大时。
-
安全代理:这种方式通常用于控制不同种类客户对真实对象的访问权限。
-
智能指引:只要用于调用目标对象时,代理附加一些额外的处理功能
-
延迟加载:指为了提高系统的性能,延迟对目标的加载。
20、适配器模式(Adapter)
在现实生活中,经常出现两个对象因接口不兼容而不能在一起的工作的实例,这时需要第三者进行适配。例如,讲中文的人同讲英文的人对话时需要一个翻译,用直流电的笔记本电脑接交流电时也需要一个适配器,用计算机访问照相机SD卡内存时需要一个读卡器等。
在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但他们与当前系统接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好的解决这些问题。
1、适配器模式的定义与特点
适配器模式的定义:将一个类的接口转换为客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。适配器模式分为类结构模型和对象结构模型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对少些。
2、适配器模式的优缺点
优点:
-
客户端通过适配器可以通明地调用目标接口。
-
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
-
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
-
在很多业务场景中符合开闭原则。
缺点:
-
适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
-
增加代码阅读难度,降低代码的可读性,过多的适配器会使系统代码变得凌乱。
21、桥接模式(Bridge)
1、桥接模式的定义:将抽象与实现分离,使他们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象与实现这两个可变维度的耦合度。
通过上述的讲解,我们可以很好的感觉到桥接模式是遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则,对修改关闭,对拓展开放,这里的桥接模式的优缺点如下:
桥接模式的优点:
-
抽象与实现相分离,拓展能力强。
-
符合开闭原则。
-
符合合成复用原则。
-
其实现细节对客户透明。
缺点是:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计和编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解和设计难度。
总结:将类的功能层次结构与类的实现层次结构分离
2、桥接模式的应用场景
当一类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。
桥接模式通常适用以下场景;
-
当一个类存在两种独立变化的维度,且这两个维度都需要进行扩展时。
-
当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
-
当一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性时。
22、装饰模式(Decorator)
1、装饰器模式的定义与特点
装饰模式的定义:指在不改变现有对象结构的情况下,动态地给对象增加一些职责(即增加其额外的功能),它属于对象结构型模式。
2、装饰器模式的优缺点
优点:
-
装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
-
通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
-
装饰器模式完全遵守开闭原则
缺点:
-
装饰模式会增加许多子类,过度使用会增加程序得复杂性。
3、装饰器模式的应用场景
-
当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
-
当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。
-
当对象的功能要求可以动态地添加,也可以再动态地撤销时。
23、外观模式(Facade)
1、外观模式的定义与特点:
外观模式又叫门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而是这些子系统更加容易被访问的模式。该模式对外有一个统一的接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
外观(Facade)模式是“迪米特法则”的典型应用,他有以下几个优点:
-
降低了子系统与客户端之间的耦合度,使得子系统变化不会影响他的客户端。
-
对客户屏蔽了子系统组件,减少了客户处理的对象项目,并使得子系统使用起来更加容易。
-
降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
外观(Facade)模式主要缺点如下:
-
不能很好地限制客户使用子系统类,很容易带来未知风险。
-
增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
2、外观模式的应用场景
通常以下情况可以考虑外观模式:
-
对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
-
当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
-
当客户端与多个子系统之间存在很大的联系时,引入外观模式可将他们进行分离,从而提高子系统的独立性和可移植性。
24、享元模式(Flyweight)
1、享元模式的定义与特点
享元(Flyweight)模式的定义:应用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式的主要优点:相同对象只要保存一份即可,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
主要缺点:
-
为了使对象可以共享,需要对一些不能共享的状态外部化,这将增加程序的复杂性。
-
读取享元模式的外部状态会使得运行状态时间变长。
享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两部分:内部状态和外部状态。
-
内部状态指对象共享出来的信息,存储在享元信息的内部,并且不会随环境的变化而变化。
-
外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。
享元模式的本质是缓存共享对象,降低内存消耗。
25、组合模式(Composite Pattren)
1、组合模式的定义与特点
组合模式的定义:有时又叫整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来展示“部分-整体”的关系,使用户对单个对象和组合对象具有一致性的访问性,属于结构型设计模式。
在组合模式中,整个树形结构中的对象都属于同一种数据类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户带来极大的便利。
2、组合模式的主要优点有:
-
组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象还是组合对象,这简化了客户端代码。
-
更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足开闭原则。
主要缺点是:
-
设计较为复杂,客户端需要花费更多的时间清理类之间的关秀
-
不同意限制容器的构件
-
不容易用继承的方法来增加构件的新功能
3、组合模式的应用场景
-
在需要表示一个对象整体与部分的层次结构的场合
-
要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
26、行为型模式概述
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法完成的任务,它涉及算法与对象间的职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
行为型模式是 GoF 设计模式中最为庞大的一类,它包含以下 11 种模式。
-
模板方法(Template Method)模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
-
策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
-
命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
-
职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
-
状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
-
观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
-
中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
-
迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
-
访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
-
备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
-
解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
27、模板方法模式(Template Method)
模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子中,使得子类可以不改变算法结构的情况下重定义该算法的某些特定步骤,它是一种行为型模式。
该模式的主要优点如下:
-
它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
-
它在父类中提取了公共的代码部分,便于代码复用。
-
部分方法是由子类实现的,因此子类可以通过拓展方式增加相应的功能,符合开闭原则
该模式的缺点如下:
-
对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
-
父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
-
由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
模板方法的应用场景:
-
算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
-
当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
-
当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。
28、策略模式(Strategy)
1、策略模式的定义和特点
策略模式(Strategy)的定义:该模式定义了一系类算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响使用算法的用户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和实现分割开来,并委派给不同的对象对这些算法进行管理。
2、策略模式的优缺点
优点:
-
多重条件语句不易维护,使用策略模式可以避免使用多重条件语句。
-
策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
-
策略模式可以提供相同行为的不同实现,客户可以根据不同时间或者空间要求选择不同的。
-
策略模式提供了对开闭原则的完美支持,可以在不修改源码的基础上,灵活的增加算法。
-
策略模式把算法的使用放到环境类中,而算法的实现移到具体的策略类中,实现了二者的分离。
缺点:
-
客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
-
策略模式造成很多的策略类。
策略模式在很多地方用到,如 Java SE 中的容器布局管理就是一个典型的实例,Java SE 中的每个容器都存在多种布局供用户选择。在程序设计中,通常在以下几种情况中使用策略模式较多。
-
一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
-
一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
-
系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
-
系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
-
多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。
29、命令模式(Command)
1、命令模式的定义与特点
命令模式的定义如下:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
命令模式的主要优点如下。
-
通过引入中间件(抽象接口)降低系统的耦合度。
-
扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。
-
可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
-
方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
-
可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。
其缺点是:
-
可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
-
命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。
2、命令模式的应用场景:
当系统的某项操作具备命令语义,且命令实现不稳定(变化)时,可以通过命令模式解耦请求与实现。使用抽象命令接口使请求方的代码架构稳定,封装接收方具体命令的实现细节。接收方与抽象命令呈现弱耦合(内部方法无需一致),具备良好的扩展性。
命令模式通常适用于以下场景。
-
请求调用者需要与请求接收者解耦时,命令模式可以使调用者和接收者不直接交互。
-
系统随机请求命令或经常增加、删除命令时,命令模式可以方便地实现这些功能。
-
当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
-
当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。
30、责任链模式(Chain of Responsibility)
1、责任链模式的定义与特点
责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
责任链模式也叫职责链模式
责任链模式是一种对象行为型模式,其主要优点如下。
-
降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
-
增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
-
增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
-
责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
-
责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
其主要缺点如下。
-
不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
-
对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
-
职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
2、模式的应用场景:
前边已经讲述了关于责任链模式的结构与特点,下面介绍其应用场景,责任链模式通常在以下几种情况使用。
-
多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
-
可动态指定一组对象处理请求,或添加新的处理者。
-
需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
31、状态模式(State)
1、状态模式的定义和特点:
状态模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态行为是一种对象行为型模式,其主要的优点如下:
-
结构清晰,状态模式将于特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“ 单一职责原则“。
-
将状态装换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换更加明确,且减少对象间的相互依赖。
-
状态类职责明确,有利于程序的拓展,通过定义新的子类很容易增加新的状态和转换。
状态模式的主要缺点如下:
-
状态模式的使用必然会增加系统的类与对象的个数。
-
状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
-
状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
2、状态模式的应用场景
-
当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
-
一个操作中包含庞大的分支结构,并且这些分支决定于对象的状态时。
32、观察者模式(Observer)
1、观察者模式的定义与特点
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 这种模式有时又称为发布-订阅模式、模式-视图模式。它是对象行为型模式。
观察者模式是一种对象行为型模式,其主要的优点如下;
-
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系,符合依赖倒置原则。
-
目标与观察者之间建立了一套触发机制。
主要缺点如下:
-
目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
-
当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
2、通过前面的分析与应用实例可知观察者模式适合以下几种情形。
-
对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
-
当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
-
实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。
-
多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。
33、中介者模式(Mediator)
1、中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
中介者模式是一种对象行为型模式,其主要优点如下。
-
降低了对象之间的耦合性,使得对象易于独立地被复用。
-
将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
其主要缺点是:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。
2、模式的应用场景:
在实际开发中,通常采用以下两种方法来简化中介者模式,使开发变得更简单。
-
不定义中介者接口,把具体中介者对象实现成为单例。
-
同事对象不持有中介者,而是在需要的时f矣直接获取中介者对象并调用。
34、迭代器模式(Iterator)
1、迭代器模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式,其主要优点如下。
-
访问一个聚合对象的内容而无须暴露它的内部表示。
-
遍历任务交由迭代器完成,这简化了聚合类。
-
它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
-
增加新的聚合类和迭代器类都很方便,无须修改原有代码。
-
封装性良好,为遍历不同的聚合结构提供一个统一的接口。
其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。
2、迭代器模式的主要应用场景:
前面介绍了关于迭代器模式的结构与特点,下面介绍其应用场景,迭代器模式通常在以下几种情况使用。
-
当需要为聚合对象提供多种遍历方式时。
-
当需要为遍历不同的聚合结构提供一个统一的接口时。
-
当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
35、访问者模式(Visitor)
1、访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
访问者(Visitor)模式是一种对象行为型模式,其主要优点如下。
-
扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
-
复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
-
灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
-
符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
访问者(Visitor)模式的主要缺点如下。
-
增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
-
破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
-
违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
36、备忘录模式(Memento)
1、备忘录模式的定义与特点:
备忘录(Memento)模式的定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
备忘录模式是一种对象行为型模式,其主要优点如下。
-
提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
-
实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
-
简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
其主要缺点是:资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
2、模式的以下应用场景。
-
需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
-
需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。
37、解释器模式(Interpreter)
1、解释器模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
解释器模式是一种类行为型模式,其主要优点如下。
-
扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
-
容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
解释器模式的主要缺点如下。
-
执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
-
会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
-
可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
前面介绍了解释器模式的结构与特点,下面分析它的应用场景。
-
当语言的文法较为简单,且执行效率不是关键问题时。
-
当问题重复出现,且可以用一种简单的语言来进行表达时。
-
当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。
注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。