设计模式
文章目录
简要说明
这份文档是针对设计模式的一次期末总结,其中内容引用了C语言中文网的内容,大家也可以点击链接进行查看。
由于图片上传至github
,大家看不见图的需要挂下代理,或者使用手机热点尝试解决。这方面问题大家请自行解决。
内容可能有一些不对的地方请见谅。
面向对象设计原则
- 单一职责原则
- 定义:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中
- 命令模式体现了单一职责原则,大多数类都是一组方法和相应的一组变量的结合,而该模式只是封装了一个没有任何变量的函数,它对函数的关注超过了类,将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化。
- 职责链模式、备忘录模式、状态模式、访问者模式体现该原则
- 开闭原则
- 定义:一个软件实体应当对扩展开放,对修改关闭。在设计一个模块时候,应当使这个模块可以在不被修改地前提下被扩展。
- 抽象工厂体现了这个原则,如果想增加一个新的products,只需在product体系中增加一个products,然后在factory类体系结构中增加一个concrete factory就可以了,而不需要对现有类做任何修改。
- 里氏代换原则
- 定义:在软件中如果能过够使用基类(父类)对象,那么一定能够使用其子类对象。把基类都替换成子类,程序不会产生任何错误和异常,反过来则不成立。
- 里氏代换原则使实现开闭原则地重要方式之一。抽象工厂。
- 依赖倒转原则
- 定义:代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。
- Template method就体现了这个原则,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中,template method使得子类不改变一个算法的结构,即可重定义该算法的某些特定步骤。
- 观察者模式体现该原则
- 接口隔离原则
- 定义:一旦一个接口太大,则需要将塌分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。
- 合成复用原则
- 定义:在一个新的对象里通过关联关系来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之,要尽量使用组合/聚合关系,少用继承
- 桥接模式体现了合成复用原则
- 迪米特法则
- 定义:一个软件实体应当尽可能少地于其他实体类发生相互作用,这样当一个模块修改时,就会尽量少地影响其他地模块,扩展会相对容易。
- 外观模式、中介者模式体现该原则
GoF23设计模式简要说明
看书P55
创建型
工厂方法模式
-
优点:
-
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
-
灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
-
典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,
满足迪米特法则、依赖倒置原则和里氏替换原则
。
-
-
缺点:
-
类的个数容易过多,增加复杂度
-
增加了系统的抽象性和理解难度
-
抽象产品只能生产一种产品,此弊端可使用
抽象工厂模式
解决。
-
-
应用场景
-
客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
-
创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
-
客户不关心创建产品的细节,只关心产品的品牌
-
-
现需要设计一个程序来读取多种不同类型的图片格式,针对每一种图片格式都设计一个图片读取器(ImageReader),如GIF图片读取器(GifReader)用于读取GIF格式的图片、JPG图片读取器(JpgReader)用于读取JPG格式的图片。图片读取器对象通过图片读取器工厂ImageReaderFactory来创建,ImageReaderFactory是一个抽象类,用于定义创建图片读取器的工厂方法,其子类GifReaderFactory和JpgReaderFactory用于创建具体的图片读取器对象。使用工厂方法模式实现该程序的设计,要求绘制相应的类图并用Java语言编程实现。
-
-
如要在系统中增加一个BMP格式读取器:绘制类图并修改代码。
-
-
抽象工厂模式
-
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下:
-
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
-
当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
-
抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
-
-
缺点:
- 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
-
应用场景
-
- 某单据管理系统中要求实现对采购、销售、库存的单据管理,系统的采购类的单据中有如采购单、收货单、应付帐单、付款单和退货单等,现使用抽象工厂方法设计模式设计该系统。
-
-
建造者模式
建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
-
优点:
-
封装性好,构建和表示分离。
-
扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
-
客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
-
-
缺点:
- 产品的组成部分必须相同,这限制了其使用范围。
- 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
-
应用场景
-
- 某软件公司欲开发一个音频与视频播放软件,为了给用户使用提供方便,该播放软件提供了多种界面显示模式,如完整模式、精简模式、记忆模式、网络模式等。在不同的显示模式下主界面的组成元素有所差异,如在完整模式下将显示菜单、播放列表、主窗口、控制条等;在精简模式下只显示主窗口和控制条,而在记忆模式下将显示主窗口、控制条、收藏列表等。现要求使用建造者设计模式设计该软件。
-
-
原形模式
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。
-
优点:
-
Java自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
-
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
-
-
缺点:
-
需要为每一个类都配置一个 clone 方法
-
clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,
违背了开闭原则
。 -
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
-
-
原型模式的克隆分为浅克隆和深克隆。
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。需要实现
Cloneable接口
。 - 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。需要实现
Serialization接口
。
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。需要实现
-
应用场景
- 某数据处理软件要增加一个图表复制功能,在图表对象中包含一个数据集对象,用于封闭待显示的数据,可以通过界面的“复制”按钮将该图表复制一份,复制后可以得到新的图表对象,用户可以修改新图表的编号、颜色和数据。现要求使用原型设计模式设计该软件。
单例模式
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
单例模式有 3 个特点:
- 单例类只有
一个实例对象
; - 该单例对象
必须由单例类自行创建
; - 单例类对外提供
一个访问该单例的全局访问点
。
-
优点:
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
-
缺点:
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,
违背开闭原则
。 - 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,
-
扩展
- 饿汉式单例:在定义静态变量的时候实例化单例类
- 懒汉式单例:在第一次被引用时将自己实例化
-
应用场景
-
世界上只有一个月亮,月亮的直径是3476.28km,无论在中国还是在美国,我们所看到的都是同一个月亮。使用单例模式实现无论我们在哪所看到的月亮是同一个月亮(饿汉单例模式、懒汉单例模式),绘制类图并用Java语言编程实现。
-
-
-
结构型模式
适配器模式
适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式
和对象结构型模式
两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
-
优点:
-
客户端通过适配器可以透明地调用目标接口。
-
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
-
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
-
在很多业务场景中
符合开闭原则
。
-
-
缺点:
- 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
-
应用场景
- 现有一个接口DataOperation定义了排序方法Sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序,类BinarySearch的binarySearch(int[],int)方法实现了二分查找算法,现使用适配器模式设计一个系统,在不修改源码的情况下将类QuickSort和类BinarySearch的方法适配到DataOperation接口中。绘制类图并编程实现。
- 修改实例仿生机器人,使得机器人可以像鸟一样叫,并像狗一样的跑,请绘制类图并编程实现
桥接模式
桥接(Bridge)模式的定义如下:将抽象与实现分离,使它们可以独立变化
。它是用组合关系代替继承关系
来实现,从而降低了抽象和实现这两个可变维度的耦合度。
-
优点:
-
抽象与实现分离,扩展能力强
-
符合开闭原则
-
符合合成复用原则
-
其实现细节对客户透明
-
-
缺点:
- 由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。
-
应用场景
- 如果系统中某对象有三个维度,如某日志记录器既可以支持不同的操作系统,还可以支持多种编程语言,并且可以使用不同的输出方式。请使用桥接模式设计该系统。
-
组合模式
组合(Composite Pattern)模式的定义:有时又叫作整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式。
组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,树形结构图如下。
![组合模式树形结构图](https://i-blog.csdnimg.cn/blog_migrate/8e82f23426a57530e57ea26426d173ff.png)
- 优点:
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足
开闭原则
;
- 缺点:
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
- 不容易限制容器中的构件;
- 不容易用继承的方法来增加构件的新功能;
- 应用场景
- 使用组合模式设计一个杀毒软件框架,该软件既可以对一个文件夹杀毒,也可以对一个文件杀毒,文件种类包括文本文件、图片文件、视频文件。绘制类图并编程实现。
-
装饰模式
装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
-
优点:
-
装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
-
通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
-
装饰器模式完全
遵守开闭原则
-
-
缺点:
- 装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
-
应用场景
-
- 某系统提供了一个数据加密功能,可以对字符串进行加密。最简单的加密算法通过对字母进行移位来实现,同时还提供了稍复杂的逆向输出加密,还提供了更为高级的求模加密。用户先使用最简单的加密算法对字符串进行加密,如果觉得还不够可以对加密之后的结果使用其他加密算法进行二次加密,当然也可以进行第三次加密。现使用装饰模式设计该多重加密系统。
-
外观模式
外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
在日常编码工作中,我们都在有意无意的大量使用外观模式。只要是高层模块需要调度多个子系统(2个以上的类对象),我们都会自觉地创建一个新的类封装这些子系统,提供精简的接口,让高层模块可以更加容易地间接调用这些子系统的功能。尤其是现阶段各种第三方SDK、开源类库,很大概率都会使用外观模式。
-
外观(Facade)模式是
迪米特法则
的典型应用,它有以下主要优点。-
降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
-
对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
-
降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
-
-
缺点
- 不能很好地限制客户使用子系统类,很容易带来未知风险。
- 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
-
应用场景
- 某系统需要提供一个文件加密模块,加密流程包括三个操作,分别是读取源文件、加密、保存加密之后的文件。读取文件和保存文件使用流来实现,这三个操作相对独立,其业务代码封装在三个不同的类中。现在需要提供一个统一的加密外观类,用户可以直接使用该加密外观类完成文件的读取、加密和保存三个操作,而不需要与每一个类进行交互,使用外观模式设计该加密模块。
享元模式
享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
-
享元模式的主要优点是:
- 相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
-
其主要缺点是:
-
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
-
读取享元模式的外部状态会使得运行时间稍微变长。
-
-
应用场景
代理模式
代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
-
代理模式的主要优点有:
-
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
-
代理对象可以扩展目标对象的功能;
-
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
-
-
缺点:
- 代理模式会造成系统设计中类的数量增加
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
-
应用场景
-
-
某信息咨询公司推出收费的在线商业信息查询模块,需要对查询用户进行身份验证,并记录查询日志,以便根据查询次数收取查询费用,现使用代理模式设计该系统。
-
-
行为型模式
职责链模式
责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
注意:责任链模式也叫职责链模式。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程
,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。
-
优点:
-
降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
-
增强了系统的可扩展性。可以根据需要增加新的请求处理类,
满足开闭原则
。 -
增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
-
责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
-
责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的
单一职责原则
。
-
-
缺点:
-
不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
-
对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
-
职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
-
-
应用场景
- 某物质管理系统中物质采购需要分批审批,主任可以审批一万元及以下的采购单,部门经理可以审批5万及以下的采购单,副总经理可以审批10万及以下的采购单,总经理可以审批20万元及以下的采购单,20万以上的需要会议审批,现采用职责链模式来设计该模拟系统。
命令模式
命令(Command)模式的定义如下:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
-
优点:
-
通过引入中间件(抽象接口)降低系统的耦合度。
-
扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且
满足开闭原则
。 -
可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
-
方便实现Undo和Redo操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
-
可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。
-
-
缺点:
-
可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
-
命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。
-
-
应用场景
-
-
实现一个通讯录程序,命令模式下的JAVA程序,该程序具备添加,删除,和查看通讯录信息的功能
界面:
1.添加联系人信息
2.删除联系人信息
3.查看联系人信息
当点击按钮添加时,以进入添加联系人信息,添加的信息包括姓名和电话号码
当点击按钮删除时,可进入删除联系人信息,通过输入联系人姓名完成删除。
当点击按钮查看时,可进行查看,通过输入联系人姓名,查看其他电话号码。
现使用命令模式来设计该系统,绘制类图并编程实现。
-
-
解释器模式
解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。
-
优点:
-
扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
-
容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
-
-
缺点:
-
执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
-
会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
-
可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式
很少被使用到
。
-
-
结构图
-
迭代器模式
迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型
模式。
-
优点:
-
访问一个聚合对象的内容而无须暴露它的内部表示。
-
遍历任务交由迭代器完成,这简化了聚合类。
-
它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
-
增加新的聚合类和迭代器类都很方便,无须修改原有代码。
-
封装性良好,为遍历不同的聚合结构提供一个统一的接口。
-
-
缺点:
- 增加了类的个数,这在一定程度上增加了系统的复杂性。
在日常开发中,我们几乎不会自己写迭代器。除非需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供的 API 完全够用。
- 应用场景
-
- 某商品管理系统中的商品名称存储在一个字符串数组中,现需要定义一个双向迭代器,实现对该商品名称数组的双向遍历(前向和后向)。要求采用双向迭代器形式实现该模式。绘制该模式的UML图。
-
中介者模式
中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则
的典型应用。
中介者模式是一种对象行为型模式。
-
优点:
-
类之间各司其职,符合
迪米特法则
。 -
降低了对象之间的耦合性,使得对象易于独立地被复用。
-
将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
-
-
缺点:
- 中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。
-
应用场景
- 采用中介模式来说明联合国的作用,联合国实际上是一个协调性组织,各个成员国之间有下属机构WTO、WFC、WHO,他们作为各个成员国家之间的事务协调者,采用中介模式来设计该模拟系统。
备忘录模式
备忘录(Memento)模式的定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
-
优点:
-
提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
-
实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
-
简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合
单一职责原则
。
-
-
缺点:
- 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
-
应用场景
- 某软件公司正在开发一款网游,为了给玩家提供更多方便,在游戏过程中可以设置一个恢复点,用于保存当前的游戏场景,如果在后续游戏过程中,玩家角色“不幸牺牲”,玩家可以返回到先前保存的场景,从恢复点开始重新游戏,试用备忘录模式实现。
观察者模式
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
- 优点:
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
符合依赖倒转原则
。 - 目标与观察者之间建立了一套触发机制。
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 缺点:
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
- 应用场景
- 某公司欲开发一套机房监控系统,如果机房达到某一指定温度,传感器将作出反应,将信号传递给响应设备,如警示灯将闪烁、报警器将发出报警、安全逃生门将自动开启、隔热门将自动关闭等,每一种响应设备的行为由专门的程序来控制,采用观察者模式来设计该系统。
状态模式
状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
-
优点:
-
结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足
单一职责原则
。 -
将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
-
状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
-
-
缺点:
-
状态模式的使用必然会增加系统的类与对象的个数。
-
状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
-
状态模式对开闭原则的支持并不太好
,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
-
-
应用场景
- 某学生成绩管理系统中学生成绩需要状态转换,,现采用用“状态模式”设计一个学生成绩的状态转换程序模拟该系统。本系统包含了学生成绩“不及格”“中等”和“优秀” 3 种状态,当学生的分数小于 60 分时为“不及格”状态,当分数大于等于 60 分且小于 90 分时为“中等”状态,当分数大于等于 90 分时为“优秀”状态,我们用状态模式来实现这个程序。
-
策略模式
策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
-
优点
- 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if…else 语句、switch…case 语句。
- 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
- 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
- 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
- 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
-
缺点
- 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
- 策略模式造成很多的策略类,增加维护难度
-
应用场景
-
-
某公司专门销售各种打印机,销售打印机时都有一定的折扣让利给顾客,但折扣计算的方法有很多种,如:不打折;每台减扣固定的金额;按售价的5%打折等等,且折扣计算方法可能发生变化。现在要开发该公司的销售系统,实现打印机销售时的价格计算。
要求:请用strategy模式设计解决方案。方案要能够使得在销售打印机(即使是同一种打印机)时可以灵活的选用价格计算方法,并且可以很容易地增加或修改价格计算方法而不至于对整个系统的维护造成困难。实验报告中要求包含设计方案的类图,并给出相应的
Java源程序。
-
-
模板方法模式
模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中
,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。
- 该模式的主要优点如下。
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,
符合开闭原则
。
- 该模式的主要缺点如下。
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
- 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
- 应用场景
-
- 某银行软件的利息计算流程如下:系统根据帐号查询用户信息;根据用户信息判断用户类型,不同类型的用户采用不同的利息计算方式计算利息(如活期帐户CurrentAccount和定期帐户SavingAccount具有不同的利息计算方式);显示利息。现使用模板方法模式来设计该系统,绘制类图并编程实现。
-
访问者模式
访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
-
优点
- 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
- 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
- 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
符合单一职责原则
。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
-
缺点
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这
违背了开闭原则
。 - 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
违反了依赖倒置原则
。访问者模式依赖了具体类,而没有依赖抽象类。
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这
-
应用场景
-
-
某公司OA系统中包含一个员工信息管理子系统,该公司员工包括正式员工和临时工,每周人力资源部和财务部等部门需要对员工数据进行汇总,汇总数据包括员工工作时间、员工工资等。该公司基本制度如下:
(1) 正式员工每周工作时间为40小时,不同级别、不同部门的员工每周基本工资不同;如果超过40小时,超出部分按照100元/小时作为加班费;如果少于40小时,所缺时间按照请假处理,请假所扣工资以80元/小时计算,直到基本工资扣除到零为止。除了记录实际工作时间外,人力资源部需记录加班时长或请假时长,作为员工平时表现的一项依据。
(2) 临时工每周工作时间不固定,基本工资按小时计算,不同岗位的临时工小时工资不同。人力资源部只需记录实际工作时间。
人力资源部和财务部工作人员可以根据各自的需要对员工数据进行汇总处理,人力资源部负责汇总每周员工工作时间,而财务部负责计算每周员工工资。
现使用访问者模式设计该员工信息管理子系统。
-
-