设计模式之结构型模式

结构型模式

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

结构模式可以分为类型结构和对象结构模式:

  • 类型结构模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构模式中一般只存在继承关系和实现关系。
  • 对象结构模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。根据合成复用原则,在系统中尽量使用关联关系来替代继承关系,因此大部分结构模式都是对象结构模式

合成/聚合复用原则
- 尽量使用合成/聚合,尽量不要使用类继承
合成和聚合都是关联的特殊种类。聚合表示一种弱的拥有关系,体现的是A对象可以包含B对象,但B对象不是A对象 的一部分;合成则是一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样.
比方说,大雅有俩个翅膀,翅膀和大雁是部分和整体,并且它们的生命周期是相同的,于是大雁和翅膀就是合成关系,而大雁是群居动物,所有每只大雁都是属于一个雁群,一个雁群可以有多个大雁,所以大雁和雁群是聚合关系.
合成/聚合复用原则好处是:优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不可能增长为不可控制的庞然大物

适配器模式

1.1.模式动机
在软件开发中采用类似于电源适配器的设计和编码技巧被称为适配器模式。
通常情况下,客户端可以通过目标类的接口访问它所提供的服务。有时,现有的类可以满足客户类的功能需要,但是它所提供的接口不一定是客户类所期望的,这可能是因为现有类中方法名与目标类中定义的方法名不一致等原因所导致的。
在这种情况下,现有的接口需要转化为客户类期望的接口,这样保证了对现有类的重用。如果不进行这样的转化,客户类就不能利用现有类所提供的功能,适配器模式可以完成这样的转化。
在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所包装的对象就是适配者(Adaptee),即被适配的类。
适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机

1.2.模式定义
适配器模式:将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,起别名为包装器。适配器模式既可以作为类结构模式,也可以作为对象结构模式
1.3.模式结构

  • 目标抽象类
  • 适配器类
  • 适配者类
  • 客户类
    1.3.模式分析
  • 系统的数据和行为都是正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,单是接口又与复用环境要求不一致的1.4.优点
    • 将目标类和适配类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码
    • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性
    • 灵活性和拓展性都非常好,通过使用配置文件,可以很方便的更好适配器,可以在不修改原有代码的基础上增加新的适配类,完全符合开闭原则
      1.5.缺点
    • 对于java c#等不支持多继承的语言,一次最多只能适配一个是适配者类,而且目标类抽象只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口
    • 如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行匹配,实现过程较为复杂。

1.6.适用环境

  • 系统需要使用现有的类,而这些类的接口不符合系统的需要
  • 想要建立一个可以重复的类,用于与彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

桥接模式

2.1.模式动机
设想如果要绘制矩形、圆形、椭圆、正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色,如红色、绿色、蓝色等,此时至少有如下两种设计方案:

第一种设计方案是为每一种形状都提供一套各种颜色的版本。
第二种设计方案是根据实际需要对形状和颜色进行组合
对于有两个变化维度(即两个变化的原因)的系统,采用方案二来进行设计系统中类的个数更少,且系统扩展更为方便。设计方案二即是桥接模式的应用。桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量

2.2.模式定义
桥接模式: 将抽象部分与实现部分分离,使它们都可以独立地变化。它是一种对象结构模式,又称接口模式
2.3.模式结构

  • Abstraction:抽象基类
  • RefinedAbstraction:扩充抽象类
  • Implementor:实现类接口
  • Concretelementor:具体实现类
    2.3.代码分析
    #include
    #include “ConcreteImplementorA.h”
    #include “ConcreteImplementorB.h”
    #include “RefinedAbstraction.h”
    #include “Abstraction.h”

using namespace std;

int main(int argc, char *argv[])
{

Implementor * pImp = new ConcreteImplementorA();
Abstraction * pa = new RefinedAbstraction(pImp);
pa->operation();

Abstraction * pb = new RefinedAbstraction(new ConcreteImplementorB());
pb->operation();		

delete pa;
delete pb;

return 0;

}
///
// RefinedAbstraction.h
// Implementation of the Class RefinedAbstraction
// Created on: 03-十月-2014 18:12:43
// Original author: colin
///

#if !defined(EA_4BA5BE7C_DED5_4236_8362_F2988921CFA7__INCLUDED_)
#define EA_4BA5BE7C_DED5_4236_8362_F2988921CFA7__INCLUDED_

#include “Abstraction.h”

class RefinedAbstraction : public Abstraction
{

public:
RefinedAbstraction();
RefinedAbstraction(Implementor* imp);
virtual ~RefinedAbstraction();

virtual void operation();

};
#endif // !defined(EA_4BA5BE7C_DED5_4236_8362_F2988921CFA7__INCLUDED_)
///
// RefinedAbstraction.cpp
// Implementation of the Class RefinedAbstraction
// Created on: 03-十月-2014 18:12:43
// Original author: colin
///

#include “RefinedAbstraction.h”
#include
using namespace std;

RefinedAbstraction::RefinedAbstraction(){

}

RefinedAbstraction::RefinedAbstraction(Implementor* imp)
:Abstraction(imp)
{
}

RefinedAbstraction::~RefinedAbstraction(){

}

void RefinedAbstraction::operation(){
cout << "do something else ,and then " << endl;
m_pImp->operationImp();
}

2.4.模式分析
理解桥接模式,重点需要理解如果将抽象化与实现化脱耦,是的二者可以独立地变化

  • 抽象化:抽象化就是忽略一些信息,把不同的实体当作同样的实体对待。在面向对象中,将对象的共同性质抽取出来形成类的过程即为抽象化的过程
  • 实现化:针对抽象化给出的具体实现,就是实现化,抽象化与实现化是一对互逆的概念,实现化产生对象bi抽象化更具体,是对抽象化事物的进一步具体化的产物
    2.5.优点
    桥接模式的有点:
  • 分离抽象接口及其实现部分
  • 桥接模式有时类似于多继承方案,但是多继承方案违背了单一职责原则,复用性比较差,而且多继承中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法
  • 桥接模式提高了系统的可扩展性,在俩个变化维度中任意扩展一个维度,都不需要修改原有系统
  • 实现细节对客户透明,可以对用户隐藏实现细节
    2.6.缺点
  • 引入次模式增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
  • 桥接模式要求正取的识别出系统中俩个独立的变化维度,因此使用范围具有一定的局限性

装饰模式

3.1.模式动机
一般有俩种方式可以实现给一个类或对象增加行为:

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

装饰模式以对客户透明的方式动态地给一个对象附加上更多责任,换言之,客户端并不会觉得对象在装饰前后有什么不同。装饰模式可以在不需要创造更多更多子类的情况下,将对象的功能增加以扩展。这就是装饰模式的动机
3.2.模式定义
装饰模式:动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类更为灵活。其别名也可以成为包装器,与适配器模式的别名相同,但它们适用于不同的场合,根据翻译不同装饰模式也有人称之为油漆工模式。它是一种对象结构模式
3.3.模式结构
- Component:抽象构建
- ConcreteComponent:具体构件
- Decorator:抽象装饰类
- ConcreteDecorator:具体装饰类
3.4.代码分析
///
// ConcreteComponent.cpp
// Implementation of the Class ConcreteComponent
// Created on: 03-十月-2014 18:53:00
// Original author: colin
///

#include “ConcreteComponent.h”
#include
using namespace std;

ConcreteComponent::ConcreteComponent(){

}

ConcreteComponent::~ConcreteComponent(){

}

void ConcreteComponent::operation(){
cout << “ConcreteComponent’s normal operation!” << endl;
}
///
// ConcreteDecoratorA.h
// Implementation of the Class ConcreteDecoratorA
// Created on: 03-十月-2014 18:53:00
// Original author: colin
///

#if !defined(EA_6786B68E_DCE4_44c4_B26D_812F0B3C0382__INCLUDED_)
#define EA_6786B68E_DCE4_44c4_B26D_812F0B3C0382__INCLUDED_

#include “Decorator.h”
#include “Component.h”

class ConcreteDecoratorA : public Decorator
{

public:
ConcreteDecoratorA(Component* pcmp);
virtual ~ConcreteDecoratorA();

void addBehavior();
virtual void operation();

};
#endif // !defined(EA_6786B68E_DCE4_44c4_B26D_812F0B3C0382__INCLUDED_)
///
// ConcreteDecoratorA.cpp
// Implementation of the Class ConcreteDecoratorA
// Created on: 03-十月-2014 18:53:00
// Original author: colin
///

#include “ConcreteDecoratorA.h”
#include
using namespace std;

ConcreteDecoratorA::ConcreteDecoratorA(Component* pcmp)
:Decorator(pcmp)
{

}

ConcreteDecoratorA::~ConcreteDecoratorA(){

}

void ConcreteDecoratorA::addBehavior(){
cout << “addBehavior AAAA” << endl;
}

void ConcreteDecoratorA::operation(){
Decorator::operation();
addBehavior();
}

3.5.模式分析

  • 与继承关系比较,关联关系的主要优势在于不会破坏类的封装性,而且继承是一种耦合较大的静态关系,无法在程序运行时动态扩展。在软件开发阶段,关联关系虽然不会比继承关系减少编码量,但是到了维护阶段,由于关联关系是系统具有较好的松耦合性,因此使得系统更加容易维护。当然,关联关系的缺点就是比继承关系要创建更多的对象
  • 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
    3.6.优点
  • 装饰模式与继承关系的目的都是扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性
  • 可以通过一种动态的方式扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为
  • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一个对象,得到功能更为强大的对象
  • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对进行组合,原有代码无须改变,符合开闭原则
    3.7.缺点
  • 使用装饰模式进行系统设计时将产生很多小对象,这些小对象的区别在于它们之间互相连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生增加系统的复杂度,加大学习与理解难度
  • 这种比继承更加灵活的特性,也同时以为者装饰模式比继承更加容易出错,拍错也很困难,对于多次装饰对象,调试时寻找错误可能 需要逐级排查,较为繁琐
    3.8.使用环境
  • 在不影响其他对象的情况下,以动态,透明 的方式给单个对象添加职责
  • 需要动态的给一个对象增加功能,这些功能也可以动态的被撤销
  • 当不能采用继承的方式对系统进行扩充或采用继承不利于扩展和维护时,不能采用继续的情况主要有俩类:第一类是系统存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈现爆炸性增长;第二类是因为类定义不能继承(如final类)

外观模式

4.1模式动机
4.2.模式定义
外观模式:外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式它是一种对象结构模式
4.3.模式结构
- Facade:外观角色(外观类,需要了解所有子系统的方法或属性,进行组合,以备外界调用)
- SubSystem:子系统角色
4.4.模式分析
根据 单一职责原则,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和互相依赖关系到达最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口

  • 外观模式也是迪米特法则的体现,通过引入一个子系统的外部与内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性隔开,是的客户端只需要与外观对象打交道,而不需要与子系统内部很多对象打交道
  • 外观模式的目的在于降低系统的复杂度
  • 外观模式很大程度上提高了客户端使用的便捷性,是的客户端无须关系子系统的工作细节

4.5有点

  • 对客户端屏蔽系统组件,减少客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式,客户代码变的很简单,与之关联的对象也很少
  • 实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响调用它的客户类,只需要调整外观类即可
  • 降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统,一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象
  • 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类

4.6.缺点

  • 不能很好的限制客户使用子系统类,如果对客户访问子系统做太多限制则减少了可变性和灵活性
  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端源代码,违背了开闭原则

4.7.适用环境

  • 当要为一个复杂子系统提供一个简单接口时可以用外观模式。该接口可以满足大多数用户的需求,而且用户也可以越过外观类直接访问子系统
  • 客户程序与多个子系统之间存在很大的依赖性。引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性
  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度

4.8.模式扩展

  • 一个系统有多个外观类:在外观模式中,通常只需要一个外观类,并且次外观类只有一个实例,换言之它是一个单例模式。在很多情况下为了节约系统资源,一般将外观类设计为单例类。当然这并不意味着整个系统里只能有一个外观类,在一个系统中可以设计很多个外观类,每个外观类都负责和一些特定的子系统交互,向用户提供业务功能
  • 不要试图通过外观类为子系统添加新行为:不用通过继承一个外观类在子系统中加入新的行为,这种做法是错误的。外观模式的用意是为了子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为。新的行为的增加应该通过修改原有子系统类或增加新的子系统类来实现,不能通过外观类来实现
  • 外观模式与迪米特法则:外观模式创造出一个外观对象,将客户端所涉及的属于子系统的协作伙伴的数量减少到最少,使得客户端与子系统内部的对象的相互作用被外观对象所取代。外观类充当了客户类与子系统类之间的第三者,降低了客户类与子系统的之间的耦合度,外观模式就是实现代码重构以便达到迪米特法则要求的一个强有力的武器
  • 抽象外观类引入:外观类最大的缺点在于违背了开闭原则,当增加新的子系统或者移除子系统的时候需要修改外观类,可以通过引入抽象外观类在一定程度上解决该问题,客户端针对抽象外观类进行编程,对于新的业务需求,不修改原有的外观类,而增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类代码的目的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值