设计模式复习

一、模式所采用的关系(e.g.继承…)

  1. UML图例

二、各模式的特点、优缺点

1.创建型(5种创建型口诀:

抽象工厂 按照 工厂方法建造 单例 原型

        将对象的使用创建分离,使用对象时无需知道对象的创建细节,使得创建过程可以多次复用,且修改两者中的一个对另一个影响为0或很少

       软件系统运行时,类将实例化为对象,并由对象来协作完成各项业务功能。创建型模式对类的实例化过程进行了抽象,能够将软件模块中对象的创建和使用进行分离。

        外界只需知道他们的共同接口而无需知道具体的细节,符合单一职责原则

1.简单工厂(依赖、继承)

优点:

(1)实现对责任的分割

(2)客户端无需知道具体产品类的类名,只需知道对应参数即可,对于一些复杂的类名,可以减少使用者的记忆量

(3)通过引入配置文件,可以在不修改任何客户端代码的情况下更换,增加新的具体的产品类,一定程度上提高了系统的灵活性

缺点:

(1)工厂类集中了所有产品的创建逻辑,一旦不能正常工作,整个系统都要受影响

(2)会增加系统中类的个数,一定程度上增加了系统的复杂度和理解难度

(3)系统拓展困难,一旦增加新产品就不得不修改工厂逻辑,产品类型较多时,有可能会使工厂逻辑过于复杂,不利于系统的拓展和维护

(4)由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构

适用环境:

(1)创建对象少

(2)客户端对创建对象不关心

2.工厂方法(依赖、继承)

优点:

(1)用户无需关心细节,无需知道具体类名

(2)多态性设计,工厂可以自主确定创建何种对象,而细节完全封装在具体工厂内部,又被称为多态工厂模式,所有的工厂类都具有同一抽象父类

(3)在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品即可。系统的可扩展性也就变得非常好,符合“开闭原则”。

缺点:
(1)在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销

(2)由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到 DOM、反射等技术,增加了系统的实现难度。

适用情况:

(1)一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。

(2)一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

(3)将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

3.抽象工厂方法(依赖、继承)

优点:

(1)抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。

(2)当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。

(3)增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”

缺点:

在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。

适用环境

(1)一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。用户无须关心对象的创建过程,将对象的创建和使用解耦。

(2)系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。

(3)属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来,同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类型。

(4)系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。对于这些产品,用户只需要知道它们提供了哪些具体的业务方法,而不需要知道这些对象的创建过程,在客户端代码中针对抽象编程,而将具体类写人配置文件中。

4.建造者模式(聚合、依赖、继承)

优点

(1)在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

(2)每一个具体建造者都相对独立,与其他的具体建造者无关,因此可以很方便替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。

(3)可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

(4)增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程系统扩展方便,符合“开闭原则”

缺点

(1)建造者模式所创建的产品一般具有较多的共同点,其组成部分相似。如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制

(2)如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

适用环境

(1)需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。

(2)需要生成的产品对象的属性相互依赖,需要指定其生成顺序。

(3)对象的创建过程独立于创建该对象的类。在建造者模式中引人了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。

(4)隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

5.原型模式(关联、依赖、继承)

优点

(1)当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例的创建效率

(2)可以动态增加或减少产品类。由于创建产品类实例的方法是产品类(具体原型类)内部具有的,因此增加新产品对整个结构没有影响。在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。

(3)原型模式提供了简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的 clone()方法实现的,无须专门的工厂类来创建产品。

(4)可以使用深克隆的方式保存对象的状态。使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态)。

缺点

(1)需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,一定是件容易的事,必须修改其源代码,违背了“开闭原则”。

(2)在实现深克隆时需要编写较为复杂的代码

适用环境

(1)创建新对象成本较大(如初始化需要占用较长的时间,占用太多的 CPU 资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以
对其属性稍作修改。

(2)如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占内存不大的时候,也可以使用原型模式配合备忘录模式(第22章将介绍备忘录模式)来应用。相反,如果对象的状态变化很大,或者对象占用的内存很大,那么采用状态模式会比原型模式更好。

(3)需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。

6.单例模式(聚合、依赖)

优点

(1)提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。

(2)由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。

(3)允许可变数目的实例。基于单例模式我们可以进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。

缺点

(1)由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

(2)单例类的职责过重,一定程度上违背了"单一职责原则”。因为单例类既充当了工.厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品本身的功能融合到一起。

(3)滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。

适用情况

(1)系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。

(2)客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

使用单例模式有一个必要条件:在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式。

在使用的过程中我们还需要注意以下两个问题

(1)不要使用单例模式存取全局变量,因为这违背了单例模式的用意,最好将全局变量放到对应类的静态成员中。

(2)不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。单例模式由于使用静态成员存储类的实例,所以可能会造成资源无法及时释放,带来一些问题。

2.结构型(7种结构型口诀:

外观  装饰 按照 适配 的方式 桥接 组合 亨元 代理

结构型模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构.

结构型模式可以描述两种不同的东西:类与类的实例(即对象)
根据这一点,结构型模式可以分为类结构型模式对象结构型模式
类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系;
而对象结构型模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。根据“合成复用原则”,在系统中应当尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。


7.适配器模式

类适配器(依赖、泛化与接口实现)

对象适配器(依赖、实现和关联)

无论是类适配器模式还是对象适配器模式,优点
(1)将目标类和适配者类解耦,通过引人一个适配器类来重用现有的适配者类,而无须修改原有代码。
(2)增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
(3)灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
具体地说,类适配器模式的优点还有:由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强,

类适配器模式的缺点有:对于 Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。
对象适配器模式的优点还有:对象适配器可以把多个不同的适配者适配到同一个目
标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
对象适配器模式的缺点有:与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。需要注意的是,在使用适配器模式的系统中,客户端一定要针对抽象目标类进行编程,否则适配器模式的使用将导致系统发生一定的改动。

在以下情况下可以使用适配器模式:
(1)系统需要使用现有的类,而这些类的接口不符合系统的需要
(2)想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

8.桥接模式(聚合、泛化与接口实现)

优点
(1)分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系"解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任意组合子类,从而获得多维度组合对象。
(2)桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
(3)桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
(4)实现细节对客户透明,可以对用户隐藏实现细节。用户在使用时不需要关心实现在抽象层通过聚合关联关系完成封装与对象的组合
缺点
(1)桥接模式的引人会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层要求开发者针对抽象进行设计与编程。
(2)桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

在以下情况下可以使用桥接模式。
(1)如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
(2)抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
(3)一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
(4)虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
(5)对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

9.组合模式(依赖、聚合、继承)

优点
(1)组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易,因为它让客户忽略了层次的差异,而它的结构又是动态的,提供了对象管理的灵活接口,因此组合模式可以方便地对层次结构进行控制
(2)客户端调用简单,客户端可以一致地使用组合结构或其中单个对象,用户就不必关心自己处理的是单个对象还是整个组合结构,简化了客户端代码。
(3)定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。
(4)更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码。
缺点
(1)使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。
(2)增加新构件时可能会产生一些问题,很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。

10.装饰模式(继承、聚合)

优点
(1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
(2)可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
(3)通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。(4)具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”
缺点如下:
(1)使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
(2)这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

11.外观模式(关联、依赖)

外观模式并不给系统增加任何新功能,它仅仅是增加一些简单化的接口。外观模式的优点如下:
(1)对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引人外观模式,客户代码将变得很简单,与之关联的对象也很少。
(2)实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。
(3)降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程。因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
(4)只是提供了一个访问子系统的统一人口,并不影响用户直接使用子系统类
外观模式的缺点如下:
(1)不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
(2)在不引人抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”

12.代理模式(关联、继承和依赖)

1.代理模式的优点
(1)代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度(2)远程代理使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
(3)虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
(4)保护代理可以控制对真实对象的使用权限。
2.代理模式的缺点
(1)由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
(2)实现代理模式需要额外的工作,有些代理模式的实现非常复杂,

根据代理模式的使用目的,常见的代理模式有以下几种类型。
(1)远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做
大使(Ambassador)。
(2)虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。

(3)Copy-on-Write 代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write 代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
(4)保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
(5)缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
(6)防火墙(Firewall)代理:保护目标不让恶意用户接近。(7)同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。(8)智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。
在这些种类的代理中,虚拟代理,远程代理和保护代理是最常见的代理模式。不同类型的代理模式有不同的优缺点,它们应用于不同的场合。

在以下情况下可以使用策略模式。
(1)如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略
模式可以动态地让一个对象在许多行为中选择一种行为。(2)一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多态性原则,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。
(3)如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并且体现面向对象设计思想
(4)不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

3.行为型模式

创建型模式关注对象的创建过程,结构型模式关注对象与类的组织,而行为型模式关注对象之间的交互。相对创建型模式和结构型模式,行为型模式定义了系统中对象之间的交互与通信,包括对系统中较为复杂的流程的控制。
行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象之间的交互。在系统运行时,对象并不是孤立的,它们可以通过相互通信与协作完成某些复杂功能,一个对象在运行时也将影响到其他对象的运行。在现实生活中,对象之间的这种通信与交互也普遍存在。

13.命令模式(关联依赖、聚合继承)

1.命令模式的优点
(1)降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者具有良好的独立性。
(2)新的命令可以很容易地加入到系统中。增加新的具体命令类不影响其他的类,因此增加新的具体命令类很容易,增加新的具体命令无须修改原有系统源代码,包括客户类代码,满足“开闭原则”,使得系统具有良好的灵活性和可扩展性。
(3)可以比较容易地设计一个命令队列和宏命令(组合命令)。可以将多个命令组合在一起批量执行,实现批处理操作,在实现时可以结合组合模式。在本章模式扩展部分将进一步讨论宏命令的实现。
(4)可以方便地实现对请求的 Undo 和 Redo。对于有些命令可以提供一个对应的逆操作命令,并将命令对象存储在集合中,从而实现对请求操作的 Undo 和 Redo 操作。在本章模式扩展部分将进一步讨论 Undo 和 Redo 的实现。
2.命令模式的缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,所以某些系统可能需要大量具体命令类,这将影响命令模式的使用。

14.迭代器模式(继承、关联、依赖)

1.迭代器模式的优点
(1)它支持以不同的方式遍历一个聚合对象。对于复杂的聚合对象可用多种方法来进行遍历,在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法也可以自己定义迭代器的子类以支持新的遍历方式。
(2)迭代器简化了聚合类。因为引人了迭代器,在原有的聚合对象中不需要再自行提供遍历等数据操作方法,这样可以简化聚合类的设计。
(3)在同一个聚合上可以有多个遍历。由于每个迭代器都保持自己的遍历状态,因此可以同时对一个聚合对象进行多个遍历操作。
(4)在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求。
2.迭代器模式的缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
模式适用环境
在以下情况下可以使用迭代器模式:
(1)访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节
(2)需要为聚合对象提供多种遍历方式。
(3)为遍历不同的聚合结构提供一个统一的接口。当需要扩展聚合结构或者给聚合结构增加新的遍历方式时可以使用迭代器模式,它提供了聚合结构和迭代器的抽象定义。

15.观察者模式(关联继承接口)

1.观察者模式的优点
(1)观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
(2)观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持
一个抽象观察者的集合,每一个具体观察者都符合抽象观察者的定义。观察目标不需要了解其具体观察者,只需知道它们都有一个共同的接口即可。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
(3)观察者模式支持广播通信,观察目标会向所有注册的观察者发出通知,简化了一对多系统设计的难度。
(4)观察者模式符合“开闭原则"的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便
2.观察者模式的缺点
(1)如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(2)如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
(3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化,
在以下情况下可以使用观察者模式:
(1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
(2)一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
(3)一个对象必须通知其他对象,而并不知道这些对象是谁。
(4)需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……可以使用观察者模式创建一种链式触发机制。

16.状态模式(聚合、继承)

1.状态模式的优点
(1)封装了转换规则。在状态模式中无须使用冗长的条件语句来进行状态的判断和转移,将不同状态之间的转换封装在状态类中,提高了代码的可维护性。
(2)枚举可能的状态,在枚举状态之前需要确定状态种类。
(3)将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
(4)允许状态转换逻辑与状态对象合成一体,而不是一个巨大的条件语句块(5)可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数
2.状态模式的缺点
(1)状态模式的使用必然会增加系统类和对象的个数
(2)状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码混乱
(3)状态模式对“开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
在以下情况下可以使用状态模式:
(1)对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为,如银行账号,具有不同的状态时其行为有所差异(有些状态既能存款又能取款,有些状态能存款但是不能取款)。
(2)代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。

17.策略模式(聚合、继承)

1.策略模式的优点
(1)策略模式提供了对“开闭原则"的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
(2)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到父类里面,从而避免重复的代码
(3)策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为,如果不使用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用就和算法或行为本身混在一起,不符合“单一职责原则”,决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为本身的逻辑混合在一起,从而不可能再独立演化,而且使用继承无法实现算法或行为的动态改变。
(4)使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件转移语句里面,比使用继承的办法还要原始和落后。
2.策略模式的缺点
(1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
(2)策略模式将造成产生很多策略类和对象,可以通过使用享元模式在一定程度上减少对象的数量。

在以下情况下可以使用策略模式。
(1)如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略
模式可以动态地让一个对象在许多行为中选择一种行为。(2)一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多态性原则,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。
(3)如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并且体现面向对象设计思想
(4)不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

三、工厂模式的核心结构

工厂模式的核心结构通常包括以下要素:

产品(Product):定义了工厂要创建的对象的抽象接口或抽象类。

具体产品(Concrete Product):实现产品接口或继承产品抽象类的具体对象。

工厂(Factory):负责创建产品对象的类,可以是抽象工厂或具体工厂。

抽象工厂(Abstract Factory)(在抽象工厂模式中):声明创建一系列相关或相互依赖产品的方法。

具体工厂(Concrete Factory)(在抽象工厂模式中):实现抽象工厂中声明的方法,创建具体的产品。

通过这样的结构,实现了将产品的创建与使用分离,提高了代码的灵活性和可维护性。

通过这种结构,工厂模式将对象的创建与使用分离,客户端只需要通过工厂接口来创建产品对象,而不需要关心具体产品对象的创建

四、面向对象设计原则,包括其定义以及为什么要遵循,最好熟悉一些遵循各原则的例子

 

五、模式分类

  1. 5种创建型口诀:抽象工厂 按照 工厂方法生成 单例 原型
    7种结构型口诀:外观  装饰 按照 适配 的方式 桥接 组合, 亨元 代理
    记住前面的12种,剩下的都是行为型

  2. 创建型模式

    1-11.png

  3. 结构型模式

    1-12.png

  4. 行为型模式(1)

    1-13.png

  5. 行为型模式(2)

    1-14.png

六、不同类型的单例模式

1.普通版本

题目1

题目2

2.懒汉式

 

3.饿汉式

区别

懒汉式单例类实现静态工厂方法时使用了同步化机制,以处理多线程环境。同样,由于构造函数是私有的,因此,此类不能被继承。
饿汉式单例类在自己被加载时就将自己实例化。单从资源利用效率角度来讲,这个比懒汉式单例类稍差些;从速度和反应时间角度来讲,则比懒汉式单例类稍好些。然而,懒汉式单例类在实例化时,必须处理好在多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着多个线程同时首次引用此类的几率变得较大,需要通过同步化机制进行控制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值