• 设计模式:适配器模式(C++实现)


    设计模式:适配器模式(C++实现)

    1. 应用背景

    在最近面试的过程中,除了单例、工厂模式、观察者模式之外,其他的设计模式也经常涉及到了。因此记录一下,这是最原始的应用背景哈哈(开玩笑)。

    适配器模式在生活中的应用也是很常见到的。比如我们电脑连接投影仪(或者显示屏)的时候,老一点的电脑接口经常是VGA的,而新一点的可以是HDMI的或者TypeC的。

    假设我现在有一个老的电脑显示屏,只能接受VGA作为输入信号。而我的电脑比较新,只有HDMI的接口,也就是只能输出HDMI信号。那么这时候就需要一个转换器(适配器)来作为中间的桥梁,来将HDMI的信号转换为老的信号,从而被显示器接收工作。

    在企业中,很多项目的接口经常是老的。这个老的一般指的是“接口传入的对象是老的”。而现在新开发了一些新的、好用的新对象,怎么把新的对象传入到老的接口里面?那就是使用适配器模式。

    下面对适配器模式做一些抽象的总结:适配器类往往是继承自老的对象类,因此可以把适配器对象传入老的接口。而适配器类往往需要传入一个新的对象,在适配器的内部中完成的工作,都是新的对象完成的。这样从外界看起来,确实是对老接口传入了一个老对象(适配器),老对象又开放了一个接口接受新对象。当需要执行具体的任务时,是新的对象完成的。

    2. 代码

    按上面的场景,首先我们先考虑只有老接口和老对象的情况:

    class VGA {
    public:
    	virtual void display() = 0; // 纯虚函数 统一接口
    };
    
    class Computer01 : public VGA {
    public:
    	void display() {
    		cout << "play video by VGA!" << endl;
    	}
    };
    
    class Projector {
    public:
    	void display(VGA* obj) { // 投影仪的接口比较老旧 只能接收VGA的信号作为播放源
    		obj->display();
    	}
    };
    
    int main() {
    	Projector p;
    	p.display(new Computer01());
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这里的 Computer01 是第一代的电脑,比较老,只能输出VGA的信号,而投影仪只能接受一个VGA的老对象,这样工作是没问题的。

    而问题是,假设有新的电脑版本 Computer02 出现了,只能输出HDMI信号,那么投影仪器是无法工作的:

    class HDMI {
    public:
    	virtual void display() = 0; // 纯虚函数 统一接口
    };
    
    
    class Computer02 : public HDMI {
    public:
    	void display() {
    		cout << "play video by HDMI!" << endl;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    所以现在需要创建一个适配器类VGAToHDMIAdaptor(属于旧对象类,继承自VGA):

    class VGAToHDMIAdaptor : public VGA {
    public:
    	// 构造函数接收一个新的对象
    	VGAToHDMIAdaptor(HDMI* obj) : obj_(obj) {}
    	/*
    		根据多态 虽然Projector对象中的 display函数接收到的是一个VGA的虚基类的指针 
    		但是现在的VGAToHDMIAdaptor类是继承自VGA类,并进行重写
    		因此最终调用到这个子类的display函数中
    	*/
    	void display() {
    		obj_->display();
    	}
    
    private:
    	HDMI* obj_;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    VGAToHDMIAdaptor虽然是一个“旧对象”,但是它的构造函数可以接受一个新的对象。新的对象被使用了一根指针 obj_ 保存起来,等到需要真正做事的时候,就可以通过这根指针来完成。

    这样,旧接口里面传入一个旧对象,这个旧对象又开放了一个接口(这里是构造函数)来接受一个新对象。因此,虽然有新对象(第二版的电脑)和旧接口(投影仪的输入 VGA信号),但是可以通过适配器来愉快的工作了:

    int main() {
    
    	Projector p;
    	p.display(new VGAToHDMIAdaptor(new Computer02()));
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    全部代码:

    #include 
    using namespace std;
    
    class VGA {
    public:
    	virtual void display() = 0; // 纯虚函数 统一接口
    };
    
    class Computer01 : public VGA {
    public:
    	void display() {
    		cout << "play video by VGA!" << endl;
    	}
    };
    
    class HDMI {
    public:
    	virtual void display() = 0; // 纯虚函数 统一接口
    };
    
    
    class Computer02 : public HDMI {
    public:
    	void display() {
    		cout << "play video by HDMI!" << endl;
    	}
    };
    
    class Projector {
    public:
    	void display(VGA* obj) { // 投影仪的接口比较老旧 只能接收VGA的信号作为播放源
    		obj->display();
    	}
    };
    
    
    class VGAToHDMIAdaptor : public VGA {
    public:
    	// 构造函数接收一个新的对象
    	VGAToHDMIAdaptor(HDMI* obj) : obj_(obj) {}
    	/*
    		根据多态 虽然Projector对象中的 display函数接收到的是一个VGA的虚基类的指针 
    		但是现在的VGAToHDMIAdaptor类是继承自VGA类,并进行重写
    		因此最终调用到这个子类的display函数中
    	*/
    	void display() {
    		obj_->display();
    	}
    
    private:
    	HDMI* obj_;
    };
    
    
    int main() {
    
    	Projector p;
    	p.display(new Computer01());
    	//p.display(new Computer02());
    
    	p.display(new VGAToHDMIAdaptor(new Computer02()));
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    运行结果:

    在这里插入图片描述

  • 相关阅读:
    日常工作中常用的抓包工具都有哪些呢?
    计算机毕业设计Python+Django的大学生提问论坛系统
    Python 爱心代码
    【内存管理】C与C++的内存管理异同点
    嵌入式Linux下运行 DotNet 应用简单示例
    自动驾驶开源数据集汇总
    Linux C语言实现通过socket发送数据,C语言测试socket以太网网速。recv函数的一些不足。
    mysql8.0英文OCP考试第101-110题
    该设备正在使用中。请关闭可能使用该设备的所有程序或窗口,然后重试。
    11.20 知识总结(choices参数、MVC和MTV的模式、Django与Ajax技术)
  • 原文地址:https://blog.csdn.net/zsiming/article/details/126690360