结构型模式

适配器模式

模式定义

适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。

模式结构

适配器模式包含以下角色:

  • Target:目标抽象类
  • Adapter:适配器类
  • Adaptee:适配者类
  • Client:客户类

适配器模式可分为对象适配器类适配器

示例代码

#include <iostream>
#include <memory>class Target {public:Target() = default;virtual ~Target() = default;virtual void request() = 0;
};class Adaptee {public:Adaptee() = default;~Adaptee() = default;void specific_request() {std::cout << "specific request" << std::endl;}
};
using AdapteePtr = std::shared_ptr<Adaptee>;class ObjectAdapter : public Target {public:explicit ObjectAdapter(AdapteePtr adaptee) : adaptee_(std::move(adaptee)) {}~ObjectAdapter() override = default;void request() override {adaptee_->specific_request();}private:AdapteePtr adaptee_;
};class ClassAdapter : public Target, private Adaptee {public:ClassAdapter() = default;~ClassAdapter() override = default;void request() override {specific_request();}
};
int main() {ClassAdapter class_adapter;class_adapter.request();ObjectAdapter object_adapter(std::make_shared<Adaptee>());object_adapter.request();
}

优点

  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
  • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
  • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

类适配器模式还有以下优点:

  • 由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

对象适配器还有以下有带你:

  • 一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。

缺点

类适配器的缺点:

  • 对于不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。

对象适配器的缺点:

  • 与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

适用场景

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

桥接模式

模式定义

桥接模式(Bridge Pattern):针对系统中有两个独立变化维度的类,将抽象部分与它的实现部分分离,使它们都可以独立地变化。又称为柄体(Handle and Body)模式或接口(Interface)模式。

模式结构

桥接模式包含以下角色:

  • Abstraction:抽象类
  • RefinedAbstraction:扩充抽象类
  • Implementor:实现类接口
  • ConcreteImplementor:具体实现类

示例代码

示例代码对应的结构图如下:

#include <iostream>
#include <memory>/* Implementor */
class Skin {public:Skin() = default;virtual ~Skin() = default;virtual void show() = 0;
};
using SkinPtr = std::shared_ptr<Skin>;/* ConcreteImplementor */
class CuteSkin : public Skin {public:CuteSkin() = default;~CuteSkin() override = default;void show() override {std::cout << "skin: cute skin" << std::endl;}
};/* ConcreteImplementor */
class CoolSkin : public Skin {public:CoolSkin() = default;~CoolSkin() override = default;void show() override {std::cout << "skin: cool skin" << std::endl;}
};/* Implementor */
class VideoFile {public:VideoFile() = default;virtual ~VideoFile() = default;virtual void decode() = 0;
};
using VideoFilePtr = std::shared_ptr<VideoFile>;/* ConcreteImplementor */
class MPGFile : public VideoFile {public:MPGFile() = default;~MPGFile() override = default;void decode() override {std::cout << "decode: MPG file" << std::endl;}
};/* ConcreteImplementor */
class AVIFile : public VideoFile {public:AVIFile() = default;~AVIFile() override = default;void decode() override {std::cout << "decode: AVI file" << std::endl;}
};/* Abstraction */
class OperationSystemVersion {public:OperationSystemVersion() = default;virtual ~OperationSystemVersion() = default;void setSkin(SkinPtr skin) { skin_ = std::move(skin); }void setVideoFile(VideoFilePtr video_file) { video_file_ = std::move(video_file); }virtual void play() = 0;protected:SkinPtr skin_;VideoFilePtr video_file_;
};
using OperationSystemVersionPtr = std::shared_ptr<OperationSystemVersion>;/* RefinedAbstraction */
class WindowsVersion : public OperationSystemVersion {public:WindowsVersion() = default;~WindowsVersion() override = default;void play() override {video_file_->decode();skin_->show();}
};/* RefinedAbstraction */
class LinuxVersion : public OperationSystemVersion {public:LinuxVersion() = default;~LinuxVersion() override = default;void play() override {video_file_->decode();skin_->show();}
};
int main() {OperationSystemVersionPtr player = std::make_shared<WindowsVersion>();player->setSkin(std::make_shared<CuteSkin>());player->setVideoFile(std::make_shared<MPGFile>());player->play();return 0;
}

优点

  • 分离抽象接口及其实现部分。
  • 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。

缺点

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

适用场景

  • 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
  • 抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
  • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

装饰模式

模式定义

装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责(Responsibility)。

其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。

模式结构

装饰模式包含如下角色:

  • Component: 抽象构件
  • ConcreteComponent: 具体构件
  • Decorator: 抽象装饰类
  • ConcreteDecorator: 具体装饰类

示例代码

#include <iostream>
#include <memory>class Component {public:Component() = default;virtual ~Component() = default;virtual void operation() = 0;
};
using ComponentPtr = std::shared_ptr<Component>;class ConcreteComponent : public Component {public:ConcreteComponent() = default;~ConcreteComponent() override = default;void operation() override {std::cout << "ConcreteComponent operation" << std::endl;}
};class Decorator {public:explicit Decorator(ComponentPtr component) : component_(std::move(component)) {}virtual ~Decorator() = default;virtual void operation() {component_->operation();}private:ComponentPtr component_;
};
using DecoratorPtr = std::shared_ptr<Decorator>;class ConcreteDecoratorA : public Decorator {public:explicit ConcreteDecoratorA(ComponentPtr component) : Decorator(std::move(component)) {}~ConcreteDecoratorA() override = default;void operation() override {Decorator::operation();addBehavior();}private:void addBehavior() {std::cout << "DecoratorA add behavior" << std::endl;}
};class ConcreteDecoratorB : public Decorator {public:explicit ConcreteDecoratorB(ComponentPtr component) : Decorator(std::move(component)) {}~ConcreteDecoratorB() override = default;void operation() override {Decorator::operation();addBehavior();}private:void addBehavior() {std::cout << "DecoratorB add behavior" << std::endl;}
};
int main() {ComponentPtr component = std::make_shared<ConcreteComponent>();DecoratorPtr decorator_a = std::make_shared<ConcreteDecoratorA>(component);DecoratorPtr decorator_b = std::make_shared<ConcreteDecoratorB>(component);decorator_a->operation(); // ConcreteComponent operation\nDecoratorA add behaviordecorator_b->operation(); // ConcreteComponent operation\nDecoratorB add behaviorreturn 0;
}

优点

  • 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
  • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
  • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

缺点

  • 使用装饰模式进行系统设计时将产生很多小对象,这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
  • 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

适用场景

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

外观模式

模式定义

外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式。

模式结构

外观模式包含以下角色:

  • Facade: 外观角色
  • SubSystem:子系统角色

示例代码

#include <iostream>
#include <memory>class SystemA {public:SystemA() = default;~SystemA() = default;void operationA() {std::cout << "operationA" << std::endl;}
};
using SystemAPtr = std::shared_ptr<SystemA>;class SystemB {public:SystemB() = default;~SystemB() = default;void operationB() {std::cout << "operationB" << std::endl;}
};
using SystemBPtr = std::shared_ptr<SystemB>;class SystemC {public:SystemC() = default;~SystemC() = default;void operationC() {std::cout << "operationC" << std::endl;}
};
using SystemCPtr = std::shared_ptr<SystemC>;class Facade {public:Facade(): system_a_(std::make_shared<SystemA>()),system_b_(std::make_shared<SystemB>()),system_c_(std::make_shared<SystemC>()) {}~Facade() = default;void wrapOperation() {system_a_->operationA();system_b_->operationB();system_c_->operationC();}private:SystemAPtr system_a_;SystemBPtr system_b_;SystemCPtr system_c_;
};
int main() {Facade facade;facade.wrapOperation();return 0;
}

优点

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

缺点

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

适用场景

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

享元模式

模式定义

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。又称为轻量级模式。

模式结构

享元模式包含如下角色:

  • Flyweight: 抽象享元类
  • ConcreteFlyweight: 具体享元类
  • UnsharedConcreteFlyweight: 非共享具体享元类
  • FlyweightFactory: 享元工厂类

示例代码

#include <iostream>
#include <memory>
#include <unordered_map>class Flyweight {public:Flyweight() = default;virtual ~Flyweight() = default;virtual void operation() = 0;
};
using FlyweightPtr = std::shared_ptr<Flyweight>;class ConcreteFlyweight : public Flyweight {public:explicit ConcreteFlyweight(int intrinsic_state) : intrinsic_state_(intrinsic_state) {}~ConcreteFlyweight() override = default;void operation() {std::cout << "intrinsic_state: " << intrinsic_state_ << std::endl;}private:int intrinsic_state_;
};class UnsharedConcreteFlyweight : public Flyweight {public:explicit UnsharedConcreteFlyweight(int all_state) : all_state_(all_state) {}~UnsharedConcreteFlyweight() override = default;void operation() {std::cout << "all_state: " << all_state_ << std::endl;}private:int all_state_;
};class FlyweightFactory {public:FlyweightFactory() = default;~FlyweightFactory() = default;FlyweightPtr getFlyweight(int key) {if (map_.find(key) != map_.end()) {return map_[key];}auto new_flyweight = std::make_shared<ConcreteFlyweight>(key);map_.emplace(key, new_flyweight);return new_flyweight;}private:std::unordered_map<int, FlyweightPtr> map_;
};
int main() {FlyweightFactory factory;factory.getFlyweight(1)->operation();factory.getFlyweight(2)->operation();factory.getFlyweight(1)->operation();return 0;
}

优点

  • 可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
  • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点

  • 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
  • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

适用场景

  • 一个系统有大量相同或者相似的对象。
  • 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
  • 使用享元模式需要维护一个存储享元对象的享元池,而这需要耗费资源,因此,应当在多次重复使用享元对象时才值得使用享元模式。

代理模式

模式定义

在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。

根据代理模式的使用目的,可将代理模式分为以下几种类型:

  • 远程代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在 另一台主机中,远程代理又叫做大使(Ambassador)。
  • 虚拟代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
  • Copy-on-Write代理:虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
  • 保护代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
  • Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
  • 防火墙代理:保护目标不让恶意用户接近。
  • 同步化代理:使几个用户能够同时使用一个对象而没有冲突。
  • 智能引用代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。

模式结构

代理模式包含如下角色:

  • Subject: 抽象主题角色
  • Proxy: 代理主题角色
  • RealSubject: 真实主题角色

示例代码

#include <iostream>
#include <memory>class Subject {public:Subject() = default;virtual ~Subject() = default;virtual void request() = 0;
};
using SubjectPtr = std::shared_ptr<Subject>;class RealSubject : public Subject {public:RealSubject() = default;~RealSubject() override = default;void request() override {std::cout << "RealSubject request" << std::endl;}
};class Proxy {public:Proxy() : subject_(std::make_shared<RealSubject>()) {}~Proxy() = default;void request() {preRequest();subject_->request();afterRequest();}private:void preRequest() {std::cout << "Proxy preRequest" << std::endl;}void afterRequest() {std::cout << "Proxy afterRequest" << std::endl;}SubjectPtr subject_;
};
int main() {Proxy proxy;proxy.request();return 0;
}

优点

  • 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
  • 远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
  • 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
  • 保护代理可以控制对真实对象的使用权限。

缺点

  • 由于在客户端和真实主题之间增加了代理对象,因此 有些类型的代理模式可能会造成请求的处理速度变慢。
  • 实现代理模式需要额外的工作,有些代理模式的实现 非常复杂。

适用场景

见上面代理模式的分类。

图说设计模式-结构型设计模式笔记相关推荐

  1. 23种设计模式——结构型设计模式(7种)

    目录 ☞ 23 种设计模式--创建型设计模式(5种) ☞ 23 种设计模式--结构型设计模式(7种) ☞ 23 种设计模式--行为型设计模式(11种) 3. 结构型设计模式 结构型模式描述如何将类或对 ...

  2. 设计模式 - 结构型设计模式小结

    分享一个大牛的人工智能教程.零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net 结构型模式旨在通过改变代码结构来达到解耦的目的,使得 ...

  3. 设计模式 - 结构型设计模式 - 桥梁模式(Java)

    分享一个大牛的人工智能教程.零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net 理解桥梁模式,其实就是理解代码抽象和解耦. 我们首先 ...

  4. 设计模式 - 结构型设计模式 - 代理模式(Java)

    分享一个大牛的人工智能教程.零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net 代理模式是最常使用的模式之一了,用一个代理来隐藏具体 ...

  5. 设计模式--简化解释(二)——结构型设计模式

    1.创建型设计模式 2.结构型设计模式 3.行为型设计模式 结构型设计模式 简而言之 结构模式主要涉及对象的组成,或者是实体如何相互使用.或者,另一个解释是,他们帮助回答"如何构建一个软件组 ...

  6. JavaScript设计模式(三):结构型设计模式-外观模式、适配器模式、代理模式、装饰者模式、桥接模式、组合模式、享元模式

    JavaScript设计模式 - 结构型设计模式 套餐服务-外观模式 外观模式(Facade) 水管弯弯-适配器模式 适配器模式(Adapter) 适配异类框架 参数适配 牛郎织女-代理模式 代理模式 ...

  7. 敏捷软件开发学习笔记(四)之结构型设计模式

    PHP结构型设计模式 参考 设计模式 PHP 设计模式全集 2018 什么是结构型是设计模式 结构型模式讲的是如何将类和对象按照某种布局组成更大的结构.它分为类结构型模式和对象结构型模式,其中类结构型 ...

  8. 《精通python设计模式》读书笔记之——结构型设计模式

    结构型设计模式: 结构型设计模式处理一个系统中不同实体(比如,类和对象)之间的关系,关注的是提供一种简单的对象组合方式来创造新功能.可用于实现不兼容软件之间的接口兼容. ①.适配器模式 简介: 适配器 ...

  9. 从框架源码中学习结构型设计模式

    文章目录 从框架源码学习结构型设计模式 适配器模式 应用实例 案例一:dubbo框架日志适配器 Logger接口 日志实现类 Logger适配器接口 LoggerAdapter实现类 Logger日志 ...

  10. 技术图文:03 结构型设计模式(下)

    结构型设计模式(下) 本教程主要介绍一系列用于如何将现有类或对象组合在一起形成更加强大结构的经验总结. 知识结构: 组合模式 – 树形结构的处理 Sunny 软件公司欲开发一个杀毒(AntiVirus ...

最新文章

  1. 印象笔记电脑版使用技巧_高效技巧 | 一文get印象笔记素材库的5大使用场景
  2. MySQL 基本数据类型
  3. 画师id_100位插画师是怎么过日子的?
  4. 数据结构:超好用的数据结构与算法可视化工具(USFCA旧金山大学)
  5. jetty NoSuchFieldError: MAX_INACTIVE_MINUTES
  6. linux克隆的虚拟,linux(CentOS7)下克隆虚拟机并配置网络(固定ip)
  7. 关于编译PCL1.71
  8. typeScript模块四
  9. 电脑突然卡主动不了了_必看!电脑运行卡或软件卡死无响应,怎么办?
  10. jsp 中select 下拉选择框 el 三元运算符 如何选中与不选中
  11. npm下载缓慢解决方法
  12. 39. Element compareDocumentPosition() 方法
  13. 分析国内App推广渠道和方法
  14. Java高级工程师面试题总结及参考答案
  15. Axure 9 Mac 版
  16. java推荐算法_Java编程实现基于用户的协同过滤推荐算法代码示例
  17. Qt 之 QQ系统表情(二)
  18. 线性表的链式存储结构详解
  19. 【opencv学习之十二】opencv滑条及实例
  20. fwr310刷openwrt_迅捷FWR310无线路由器的刷机

热门文章

  1. ISO27001信息安全管理体系的建立评估五大注意事项
  2. Win11画图工具没了怎么重新安装
  3. 各地的公安接口的配置说明书
  4. 桌面推演技术前沿及发展趋势
  5. 常见网络问题——架设篇
  6. 支持linux的midi键盘,十款人气MIDI键盘推荐,适合各个阶段的音乐人
  7. LoadRunner教程(16)-LoadRunner SLA分析
  8. 使用 LwIP TCP/IP 栈,在 STM32Cube 上开发应用
  9. 计算机无法打印图片,Win7电脑连接打印机可以打印文档不能打印图片怎么办
  10. [企业信息化大家学系列]处于变革边缘的中国供应链管理