设计模式——结构型模式

前言:本文为学习《大话数据结构》过程的总结, 同时也参考了如下内容:
https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/structural.html

总结:结构型模式,共七种,适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

一、适配器模式

将一个类的接口换成客户希望的另一个接口。 使得原本接口互不兼容的类可以一起工作。
类适配器模式:需要通过多重继承对一个接口与另一个接口进行匹配, C++支持。 而C#,JAVA等语言一个类只有一个父类。
在这里插入图片描述

对象适配器模式:
在这里插入图片描述

二、桥接模式

将抽象部分与它的实现部分分离,使用它们每个部分都可以独立变化。
(例如:将手机品牌和手机软件分离为两个不同的大类。品牌和软件的扩展各自不受干扰,手机通过安装相应的软件来扩展自己的功能 。)

设想如果要绘制矩形、圆形、椭圆、正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色,如红色、绿色、蓝色等,此时至少有如下两种设计方案:
第一种设计方案是为每一种形状都提供一套各种颜色的版本。
第二种设计方案是根据实际需要对形状和颜色进行组合
对于有两个变化维度(即两个变化的原因)的系统,采用方案二来进行设计系统中类的个数更少,且系统扩展更为方便。设计方案二即是桥接模式的应用。桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量。

当需要多角度分类实现对象,而只用继承会造成大量的类增加,不能满足开放——封闭原则,就要考虑桥接模式。

• Abstraction:抽象类
• RefinedAbstraction:扩充抽象类
• Implementor:实现类接口
• ConcreteImplementor:具体实现类
在这里插入图片描述

抽象部分通过接口调用其具体实现

脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意

实例:
如果需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Linux、Unix等)上播放多种格式的视频文件,常见的视频格式包括MPEG、RMVB、AVI、WMV等。现使用桥接模式设计该播放器。

三、装饰模式

装饰模式:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更加灵活。

一般有两种方式可以实现给一个类或对象增加行为:
继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)。

不能采用类继承的情况:有大量独立的扩展,为支持每一组合将产生大量的子类; 类定义不能继承。
在这里插入图片描述

如果只有一个元件类和一个装饰器类,就只让一个Decorator类继承Component类即可完成装饰。
在这里插入图片描述

由于关联关系的松耦合性,系统更加容易维护。 但它的缺点是比继承关系要创建更多的小对象 ,调试和寻找错误较困难。

客户端使用示例:

ConcreateComponent c = new ConcreateComponent();
ConcreateDecoratorA d1 = new ConcreateDecoratorA();
ConcreateDecoratorB d2 = new ConcreateDecoratorB();

d1.setComponent( c );
d2.setComponent( d1 ); //此处ComcreateComponent 与 ConcreateDecorator的祖先均为Component.
d2.opetation(); //所以setComponent是可以依次将它们作为参数对象进行包装的。最终结果是均以C为基础进行层层包装,扩展功能 。

一个装饰类的接口必须与被装饰类的接口保持相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以一致对待。
尽量保持具体构件类Component作为一个“轻”类,也就是说不要把太多的逻辑和状态放在具体构件类中,可以通过装饰类对其进行扩展

四、代理模式Proxy

为某对象提供一种代理,由代理控制对这个对象的访问与使用。
A 与 C 通过代理B 通信, A 知道C ,A 、C都认识B,但C不认识A。 A 可以通过代理B传达消息给C。
在这里插入图片描述
在这里插入图片描述

代理模式应用:
1.远程代理:为一个对象在不同的地址空间提供局部代理。 可以隐藏一个对象存在于不同地址空间的事实。(例如网络通信)
2.虚拟代理:根据需要创建开销很大 的对象,通过它来实例化需要很长时间的真实对象。(内存节省技术,将占用大量内存或处理复杂对象推迟到使用它的时候)
比如打开一个很大的HTML网页,一些未打开的图片框就是通过虚拟代理替代了真实的图片。 代理存储了 真实图片的路径和尺寸。
3.安全代理: 控制真实对象访问时的权限。
4.智能指引:当引用真实对象时,代理处理另外一些事情。比如将此对象引用次数记录下来等。

五、外观模式

外观模式:为子系统的一组接口提供一个一致的界面。 定义一个高层接口,封装子系统的功能组合。 客户端通过这一接口使用子系统的多项功能。

它完美地体现了依赖——倒转原则和迪米特法则 。 (针对接口编程, 实现松耦合)

迪米特法则(最少知识原则):如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,应该通过第三方转发这个调用。

使用场景: 三层架构 , 将数据访问层和业务逻辑层, 业务逻辑层和表示层的层与层之间建立外观Façade
维护遗留的大型系统,将复杂难维护部分通过 外观Facade类 提供一个清晰简单的接口。

在这里插入图片描述
在这里插入图片描述
抽象外观类的引入
外观模式最大的缺点在于违背了“开闭原则”,当增加新的子系统或者移除子系统时需要修改外观类,可以通过引入抽象外观类在一定程度上解决该问题,客户端针对抽象外观类进行编程。对于新的业务需求,不修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。

六、享元模式

运用共享技术有效地支持大量细粒度的对象。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。

面向对象技术可以很好地解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致运行代价过高,带来性能下降等问题。

享元模式正是为解决这一类问题而诞生的。享元模式通过共享技术实现相同或相似对象的重用。
在享元模式中可以共享的相同内容称为内部状态(IntrinsicState),而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State),由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。
在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)用于存储具有相同内部状态的享元对象。
在享元模式中共享的是享元对象的内部状态,外部状态需要通过环境来设置。在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为细粒度对象。享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。

Flyweight: 抽象享元类
ConcreteFlyweight: 具体享元类
UnsharedConcreteFlyweight: 非共享具体享元类
FlyweightFactory: 享元工厂类
在这里插入图片描述
在这里插入图片描述

Flyweight* FlyweightFactory::getFlyweight(string str){
map<string,Flyweight*>::iterator itr = m_mpFlyweight.find(str);
if(itr == m_mpFlyweight.end())
{
Flyweight * fw = new ConcreteFlyweight(str);
m_mpFlyweight.insert(make_pair(str,fw));
return fw;
}
else
{
cout << “aready in the pool,use the exist one:” << endl;
return itr->second;
}
}

享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。

享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。

内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享。
外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的

享元模式与其他模式的联用

在享元模式的享元工厂类中通常提供一个静态的工厂方法用于返回享元对象,使用简单工厂模式来生成享元对象。
在一个系统中,通常只有唯一一个享元工厂,因此享元工厂类可以使用单例模式进行设计。
享元模式可以结合组合模式形成复合享元模式,统一对享元对象设置外部状态。

七、组合模式

将对象组合成树形结构以表示部分——整体的层次结构。 组合模式使得用户对单个对象和组合对象的使用具有一致性。
(举例: 公司的组织架构图, 树状结构 , 总公司的组织架构和分公司、办事处的基本组织架构是一致的)
在这里插入图片描述

透明方式: 在Component中声明所有用来管理子对象的方法,实现Component接口的所有子类都具备了接口,这样的好处是叶结点和枝结点对于外界没有区别 ,具备完全一致的接口。 但叶结点的其它方法是没有意义的。
安全方式: 在接口中不声明具体的add remove方法, 子类leaf 就不需要实现。 但这样树叶和树枝将具有不相同的接口,客户端调用时需要判断。

使用场景: 当需求体现的是部分与整体的层次结构时,希望用户可以忽略组合对象与单个对象的不同,统一使用组合结构中所有对象时,可使用组合模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值