第四章 设计模式之结构型模式

设计模式说明
代理(Proxy)模式为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
适配器(Adapter)模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
桥接(Bridge)模式将抽象与现实分离,便它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
装饰(Decorator)模式动态的给对象增加一些职责,即增加其额外的功能。
外观(Facade)模式为多个复杂的子系列提供一个一致的接口,使这些子系统更加容易被访问。
享元(Flyweight)模式运用共享技术来有效地支持大量细粒度对象的复用。
组合(Composite)模式将对象组合成树桩层次结构,使用户对单个对象和组合对象具有一致的访问性。

代理模式

生活例子

① 在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。
② 购买火车票不一定要去火车站买,可以通过 12306网站或者太火车票代售点买。
③ 找女朋友、找保姆、批工作等都可以通过战中介完成。

在软件设计中例子

① 使用代理模式的例子也很多,例如,要访问的远程对象H较大(如D视频或大图像等),其下载要花很多时间。
② 还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。

代理模式的定义与特点

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。
这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和访问对象之间的中介。

代理模式的主要优点

① 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
② 代理对象可以扩展目标标对象的功能;
③ 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

代理模式的结构与实现

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。

模式的结构

代理模式的主要角色如下。
① 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
② 真实主题(Real Subject))类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
③ 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能
请添加图片描述
在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知.
根据代理的创建时期,代理模式分为静态代理和动态代理。
① 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class文件就已经存在了。
② 动态:在程序运行时,运用反射机制动态创建而成。

【例1】韶关“天街e角"公司是一家婺源特产公司的代理公司,用代理模式实现。
分析:本实例中的"婺源特产公司经营许多婺源特产,它是真实主题,提供了显示特产的display)方法,可以用窗体程序实现。而韶关天街e角"公司是婺源特产公司特产的代理,通过调用婺源特产公司的display()方法显示代理产品,当然它可以增加一些额外的处理,如包装或加价等。客户可通过"天街e角"代理公司间接访可"婺源特产公司"的产品,图2所示是公司的结构图。
在这里插入图片描述

适配器模式(Adapter模式)

生活例子

① 在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。
② 例如,讲中文的人同讲英文的人对话时需要一个翻译,
③ 用直流电的笔记本电脑接交流电源时需要一个电源适配器,
④ 用计算机访问照相机的SD内存卡时需要一个读卡器等。

在软件设计中例子

需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但他们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题。

模式的定义与特点

适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为:
① 类结构型模式
② 对象结构型模式
前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

优点和缺点

优点
① 客户端通过适配器可以透明地调用目标接口。
② 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
③ 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。·在很多业务场景中符合开闭原则。

模式的结构与实现

模式的结构
适配器模式(Adapter)包含以下主要角色。
① 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口.
② 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口.
③ 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
请添加图片描述
请添加图片描述

实现
【例1】用适配器模式(Adapter)模拟新能源汽车的发动机。
分析:新能源汽车的发动机有电能发动机(Electric Motor)和光能发动机(Optical Motor)等,各种发动机的驱动方法不同,例如,电能发动机的驱动方法 electricDrive()是用电能驱动,而光能发动机的驱动方法opticalDrive()是用光箭汉动,它们是适配器模式中被访问的适配者。
客户端希望用统―的发动机驱动方法drive()访问这两种发动机,所以必须定义一个统一的目标接口Motor,然后再定义电能适配器(Electric Adapter)和光能适配器(Optical Adapter)去适配这两种发动机。
请添加图片描述

桥接模式(Bridge模式)

生活例子

在现实生活中,某些类具有两个或多个维度的变化,如图形既可按形状分,又可按颜色分。
① 如何设计类似手Photoshop这样的软件,能画不同形状和不同颜色的图形呢?如果用继承方式, m种形状和n种颜色的图形就有m*n种,不但对应的子类很多,而且扩展困难。
当然,这样的例子还有很多,如不同颜色和字体的文字、不同品牌和功率的汽车、不同性别和职业的男女、支持不同平台和不同文件格式的媒体播放器等。如果用桥接模式就能很好地解决这些问题。

桥接模式的定义与特点

桥接(Brige)模式的定义如下:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
我们能很好的感觉到桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则,对修改关闭,对扩展开放。

优点和缺点

优点
① 抽象与实现分离,扩展能力强
② 符合开闭原则
③ 符合合成复用原则
④ 其实现细节对客户透明

桥接模式的结构与实现

可以将抽象化部分与实现化部分分开,取消二者的继承关系,改用组合关系。
桥接的结构
桥接(Bridge)模式包含以下主要角色。
① 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
② 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
③ 实现化(lmplementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
④ 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现…
请添加图片描述
实现
【例1】用桥接(Bridge)模式模拟女士皮包的选购。
分析:女士皮包有很多种,可以按用途分、按皮质分、按品牌分、按颜色分、按大小分等,存在多个维度的变化,所以采用桥接模式来实现女士皮包的选购比较合适。
本实例按用途分可选钱包((Wallet)和挎包(HandBag),按颜色分可选黄色(Yellw)和红色(Red)。可以按两个维度定义为颜色类和包类。
颜色类(Color))是一个维度,定义为实现化角色,它有两个具体实现化角色:黄色和红色,通过getColor()方法可以选择颜色;
包类(Bag)是另一个维度,定义为抽象化角色,它有两个扩展抽象化角色:挎包和钱包,它包含了颜色类对象,通过getName()方法可以选择相关颜色的挎包和钱包。
请添加图片描述

装饰者模式

生活案例

上班族大多都有睡懒觉的习惯,每天早上上班时间都很紧张,于是很多人为了多睡一会,就会用方便的方式解决早餐问题。有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么"加码”,都还是一个煎饼。
在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式。

在软件设计中例子

有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以采用装饰器模式来实现。

装饰器模式的定义与特点

装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

优点和缺点

① 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用。
② 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果。
③ 装饰器模式完全遵守开闭原则。

装饰者模式的结构与实现

通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。
如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标。下面来分析其基本结构和实现方法。

结构
装饰器模式主要包含以下角色。
1.抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
2.具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
3.抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其了类扩展具体构件的功能。
4.具体装饰(ConcreteDecorator))角色:实现抽象装饰的相关方法,并给具休构件对象添加附加的责任.
装饰器模式的结构图如图1所示。
请添加图片描述

实现
【例1】用装饰器模式实现游戏角色“莫莉卡·安斯兰的变身。
分析:在《恶魔战士》中,游戏角色"莫莉卡·安斯兰"的原身是一个可爱少女,但当她变身时,会变成头顶及背部延伸出蝙蝠状飞理的女妖,当然她还可以变为穿着漂亮外衣的少女。这些都可用装饰器模式来实现,在本实例中的莫莉卡"原身有setlmage(Sitring t)方法决定其显示方式,而其变身"编蝠状女妖"和"着装少女"可以用setChanger()方法来改娈其外观,原身与变身后的效果用display()方法来显示,图2所示是其结构图。
请添加图片描述

外观模式

生活案例

在现实生活中,常常存在办事较复杂的例子,如办房产证或注册一家公司,有时要同多个部门联系,这时要是有一个综合部门能解决一切手续问题就好了。
图1给出了客户去当地房产局办理房产证过户要遇到的相关部门。
在这里插入图片描述

在软件设计中例子

软件设计也是这样,当一个系统的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越复杂。这时如果系统内部发生改变,客户端也要跟着改变,这违背了"开闭原则”,也违背了““迪米待法则”,所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标。

外观模式的定义与特点

外观(Facade)模式:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具休细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
在日常编码工作中,我们都在有意无意的大量使用外观模式。只要是高层模块需要调度多个子系统(2个以上的类对象),我们都会自觉地创建一个新的类封装这些子系统,提供精简的接口,让高层模块可以更加容易地问接调用这些子系统的功能。尤其显现阶段各种第三方SDK、开源类库,很大概率都会使用外观模式。

优点和缺点

外观(Facade)模式是"迪米特法则的典型应用,它有以下主要优点。
① 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类.
② 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
③ 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

外观模式的结构与实现

外观(Facade)模式,主要是定义了一个高层接口。它包含了对各个了系统的引用,客户端可以通过它访问各个子系统的功能。

结构
外观(Facade)模式包含以下主要角色。
① 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
② 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
③ 客户(Client)角色:通过一个外观角色访问各个子系统的功能。
请添加图片描述
实现
【例1】用“外观模式"设计一个婺源特产的选购界面。
分析:本实例的外观角色WySpecialty是JPanel的子类,它拥有8个子系统角色 Specialty1~Specialty8,它们是图标类(Imagelcon)的子类对象,用来保存该婺源特产的图)。
外观类(WySpecialty)用JTree组件来管理婺源特产的名称,并定义一个事件处理方法valueChanged(TreeSelectionEvent e),当用户从树中选择特产时,该特产的图标对象保存在标签(JLabd)对象中。
客户窗体对象用分割面板来实现,左边放外观角色的目录树,右边放显示所选特产图像的标签。其结构图如图3所示
请添加图片描述

享元模式

生活案例

在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。
例如,围棋和五子棋中的黑白棋子,图像中的坐标点或颜色,局域网中的路由器、交换机和集线器,教室里的桌子和凳子等。这些对象有很多相似的地方,如果能把它们相同的部分提取出来共享,则能节省大量的系统资源,这就是享元模式的产生背景。

享元模式的定义与特点

享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

享元模式的结构与实现

享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。
① 内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变;
② 外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。

比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。
享元模式的本质是缓存共享对象,降低内存消耗。

模式的结构
享元模式的主要角色有如下。
① 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
② 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
③ 非享元 (Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
④ 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不在的话,则创建一个新的享元对象。
请添加图片描述

实现
【例1】享元模式在五子棋游戏中的应用。
分析:五子棋同围棋一样,包含多个“黑"或“"白"颜色的棋子,所以用享元模式比较好。
本实例中:
① 棋子(ChessPieces)类是抽象享元角色,它包含了一个落子的DownPieces(Graphics g.Point pt)方法;
② 白子(WhitePieces)和黑子(BlackPieces)类是具体享元角色,它实现了落子方法;

  • Point是非享元角色,它指定了落了的位置;
    .WeigiFaclory是享元工厂角色,它通过 ArrayList来管理棋子,并且提供了获取白子或者黑子的 getChessPieces(Sting type)方法;
    ·客户类(Chessboard)利用Graphics组件在框架窗体中绘制一个棋盘,并实现mouseClicked(MouseEvent e)事件处理方法,该方法根据用户的选择从享元工厂中获取白子或者黑了并落在棋盘上.
    请添加图片描述

组合模式

生活案例

在现实生活中,存在很多部分-整体的关系,例如,大学中的部门与学院、总公司中的部门与分公司、学习用品中的书与书包、生活用品中的衣服与衣柜、以及厨房中的锅碗瓢盆等。在软件开发中也是这样,例如,文件系统中的文件与文件夹、窗体程序中的简单控件与容器控件等。对这些简单对象与复合对象的处理,如果用组合模式来实现会很方便。

组合模式的定义与特点

组合(Composite Pattern)模式的定义:有时又叫作整体·部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示‘整体-部分的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式。
组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,树形结构图如下。
请添加图片描述
由上图可以看出,其实根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为。
这样,在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的使利。

优点和缺点

组合模式的主要优点有:
① 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
② 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“"开闭原则;

组合模式的结构与实现

结构
组合模式包含以下主要角色。
① 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
② 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
③ 树枝构件(Composite)角色/中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含Add()、Remove()、GetChild()等方法。
组合模式分为透明式的组合模式和安全式的组合模式。
Ⅰ透明方式
在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove()及GetChid()方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。其结构图如图1所示。
请添加图片描述
Ⅱ 安全方式
在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。其结构图如图2所示。
请添加图片描述
实现
加入要访问集合c0={leaf1,{leaf2,leaf3}}中的元素,其对象的树状图如图3所示
【例1】用组合模式实现当用户在商店购物后,显示其所选商品信息,并计算所选商品总价的功能。
说明:假如李先生到韶关"天街e角生活用品店购物,用1个红色小袋子装了2包婺源特产((单价7.9元)、1张婺源地图(单价9.9元);用1个白色小袋子装了2包韶关香菇(单价68元)和3包韶关红茶(单价180元);用1个中袋子装了前面的红色小袋子和1个景德镇瓷器(单价380元);用1个大袋子装了前面的中袋子、白色小袋子和1双李宁牌运动鞋(单价198元)。最后大袋子"中的内容有:{1双李宁牌运动鞋(单价198元)、
中袋子{1个景德镇瓷器(单价380元)、
红色小袋了{2包婺源特产(单价7.9元)、1张婺源地图(单价9.9元)册.
白色小袋子{2包留关香菇(单价68元)、3包留关红茶(单价180元)人
请添加图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-攀-登-者-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值