Modem:Adapter模式与Bridge模式

2024-04-18 05:08
文章标签 模式 adapter bridge modem

本文主要是介绍Modem:Adapter模式与Bridge模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

</pre>本例取自《敏捷软件开发》第25章,使用C++实现<p></p><p></p><p>1.背景</p><p>    <img src="" alt="" /></p><p>         Modem类有四个接口,Dial、HandUp、Send、Receive,客户使用Modem,有三种实际的Modem与之对应</p><p></p><p>代码</p><p></p><pre code_snippet_id="475013" snippet_file_name="blog_20140925_2_2898509" name="code" class="cpp">#ifndef MODEM_H_
#define MODEM_H_#include <string>
#include <iostream>using namespace std;class Modem
{
public:virtual ~Modem(){}virtual void dial()=0;virtual void hangUp()=0;virtual void send()=0;virtual void receive()=0;
};class HayesModem: public Modem
{
public:void dial(){cout<<"HayesModem dial!"<<endl;}void hangUp(){cout<<"HayesModem hangUp!"<<endl;}void send(){cout<<"HayesModem send!"<<endl;}void receive(){cout<<"HayesModem receive!"<<endl;}
};class USRoboticsModem:public Modem
{
public:void dial(){cout<<"USRoboticsModem dial!"<<endl;}void hangUp(){cout<<"USRoboticsModem hangUp!"<<endl;}void send(){cout<<"USRoboticsModem send!"<<endl;}void receive(){cout<<"USRoboticsModem receive!"<<endl;}
};#endif /* MODEM_H_ */

#include "Modem.h"void modem()
{Modem& m1 = *(new HayesModem());m1.dial();m1.send();m1.receive();m1.hangUp();}

运行结果

HayesModem dial!
HayesModem send!
HayesModem receive!
HayesModem hangUp!

2.变化一

     有一种新的Modem加入,不需要拨号(没有Dial与HandUp的接口),对于该需求,理想的实现方式,是使用接口隔离原则(ISP)的思想,将四个接口分离为两个接口类(Dialer和Modem),使用DedicatedModem的客户不需要看到dial和hangUp接口

     也即,用户代码,如果给的是一个DedicatedModem的对象,他们的代码也是可以执行的。(原有dialModem用户的代码,如果给的是一个DedicatedModem的引用,而不是DialModem的引用,他们的代码是不用修改的,可能是为了升级吧)

class Dialler
{
public:virtual ~Dialler(){}virtual void dial()=0;virtual void hangUp()=0;
};class Modem
{
public:virtual ~Modem(){}virtual void send()=0;virtual void receive()=0;
};class HayesModem: public Modem, public Dialler
{
public:void dial(){cout<<"HayesModem dial!"<<endl;}void hangUp(){cout<<"HayesModem hangUp!"<<endl;}void send(){cout<<"HayesModem send!"<<endl;}void receive(){cout<<"HayesModem receive!"<<endl;}
};class USRoboticsModem:public Modem, Dialler
{
public:void dial(){cout<<"USRoboticsModem dial!"<<endl;}void hangUp(){cout<<"USRoboticsModem hangUp!"<<endl;}void send(){cout<<"USRoboticsModem send!"<<endl;}void receive(){cout<<"USRoboticsModem receive!"<<endl;}
};class DedicatedModem:public Modem
{
public:void send(){cout<<"DedicatedModem send!"<<endl;}void receive(){cout<<"DedicatedModem receive!"<<endl;}
};

void modem()
{HayesModem modem;DedicatedModem dModem;/*---------模拟客户代码------------*/Modem& m1 = modem;Dialler& d1 = modem;d1.dial();m1.send();m1.receive();d1.hangUp();Modem& m2 = dModem;m2.send();m2.receive();
}

运行结果:

HayesModem dial!
HayesModem send!
HayesModem receive!
HayesModem hangUp!
DedicatedModem send!
DedicatedModem receive!


上述代码的问题在于,需要修改原有客户的代码(原有客户只是用Modem&,现在需要使用Modem&和Dialler&,当然,这个是在不让客户看到HayesModem和DedicatedModem类型的前提下的)


3.使用Adapt来解决

    引入一个Adapt来匹配DedicatedModem(两个接口)和Modem(四个接口),Adapt继承Modem(目标接口),并持有一个DedicatedModem的引用(还可以使用继承)

由其进行接口翻译,如果是send和receive,则委托给DedicatedModem,如果是dial和hangUp,则实现一个Fake的。

    这样,原有代码就不需要进行修改(使用拨号Modem的用户还是使用Modem的引用,新的使用DedicatedModem的用户,只是用send和receive接口)


class Modem
{
public:virtual ~Modem(){}virtual void dial()=0;virtual void hangUp()=0;virtual void send()=0;virtual void receive()=0;
};class HayesModem: public Modem
{
public:void dial(){cout<<"HayesModem dial!"<<endl;}void hangUp(){cout<<"HayesModem hangUp!"<<endl;}void send(){cout<<"HayesModem send!"<<endl;}void receive(){cout<<"HayesModem receive!"<<endl;}
};class USRoboticsModem:public Modem
{
public:void dial(){cout<<"USRoboticsModem dial!"<<endl;}void hangUp(){cout<<"USRoboticsModem hangUp!"<<endl;}void send(){cout<<"USRoboticsModem send!"<<endl;}void receive(){cout<<"USRoboticsModem receive!"<<endl;}
};class DedicatedModem
{
public:void send(){cout<<"DedicatedModem send!"<<endl;}void receive(){cout<<"DedicatedModem receive!"<<endl;}
};class DedicatedModemAdapter:public Modem
{
public:DedicatedModemAdapter(DedicatedModem& arg):modem(arg){}void dial(){cout<<"Fake dial!"<<endl;}void hangUp(){cout<<"Fake hangUp!"<<endl;}void send(){modem.send();}void receive(){modem.receive();}
private:DedicatedModem& modem;
};
void modem()
{HayesModem modem;DedicatedModem dModem;DedicatedModemAdapter adapter(dModem);/*---------模拟客户代码------------*/Modem& m1 = modem;m1.dial();m1.send();m1.receive();m1.hangUp();DedicatedModem& m2 = dModem;m2.send();m2.receive();
}
运行结果:

HayesModem dial!
HayesModem send!
HayesModem receive!
HayesModem hangUp!
DedicatedModem send!
DedicatedModem receive!


【为什么要引入Adapt?本来,如果直接定义一个DedicatedModem,其与Modem之间没有关联,对于原有使用dialmodem的用户和使用DedicatedModem的用户都没有影响】

原因在于,在引入DedicatedModem需求的时候的一个特殊的约定,就是“用户代码,如果给的是一个DedicatedModem的对象,他们的代码也是可以执行的”


比如将用户代码,修改为以下的样子,原有用户使用的引用m1已经从一个dialModem的对象变成了一个Adapter的对象,代码依旧要可以能够执行,所以说Adapte的引入,在这个例子中,更多的是为了兼容原有代码的需要。

void modem()
{HayesModem modem;DedicatedModem dModem;DedicatedModemAdapter adapter(dModem);/*---------模拟客户代码------------*/Modem& m1 = adapter;m1.dial();m1.send();m1.receive();m1.hangUp();DedicatedModem& m2 = dModem;m2.send();m2.receive();
}

执行结果:

Fake dial!
DedicatedModem send!
DedicatedModem receive!
Fake hangUp!
DedicatedModem send!
DedicatedModem receive!


4.使用Bridge模式来解决

    在建模的时候,可以将Modem作为一个抽象的类,然后派生出DialModem和DedicatedModem,然后这两个类分别可以有多个品牌的Modem来实现,但是这样的话,对于这种自由度不是很稳定(会扩展、会变化)的场景,用不了多久,就会派生出大量的类。

     Bridge就是将这些自由度(也可以做为变化方向)先进行打散,分为dial、hangup、send、receive四个自由度,每个自由度单独由一个Impler实现,Impler又通过接口委托给真实的Modem厂家来实现。然后ModemConnectionController又将这些变化方向“桥接”起来,组合成一个整体

代码

class Modem
{
public:virtual ~Modem(){}virtual void dial()=0;virtual void hangUp()=0;virtual void send()=0;virtual void receive()=0;
};class DedicatedModem
{
public:virtual ~DedicatedModem(){}virtual void send()=0;virtual void receive()=0;
};class ModemImplementation
{
public:virtual ~ModemImplementation(){}virtual void dial()=0;virtual void hangUp()=0;virtual void send()=0;virtual void receive()=0;
};class HayesModem: public ModemImplementation
{
public:void dial(){cout<<"HayesModem dial!"<<endl;}void hangUp(){cout<<"HayesModem hangUp!"<<endl;}void send(){cout<<"HayesModem send!"<<endl;}void receive(){cout<<"HayesModem receive!"<<endl;}
};class USRoboticsModem:public ModemImplementation
{
public:void dial(){cout<<"USRoboticsModem dial!"<<endl;}void hangUp(){cout<<"USRoboticsModem hangUp!"<<endl;}void send(){cout<<"USRoboticsModem send!"<<endl;}void receive(){cout<<"USRoboticsModem receive!"<<endl;}
};class ModemConnectionController:public Modem, public DedicatedModem
{
public:ModemConnectionController(ModemImplementation& arg):impl(arg){}
protected:void dialImp(){impl.dial();}void hangUpImp(){impl.hangUp();}void sendImp(){impl.send();}void receiveImp(){impl.receive();}
private:ModemImplementation& impl;
};class DedModemController:public ModemConnectionController
{
public:DedModemController(ModemImplementation& arg):ModemConnectionController(arg){}void dial(){cout<<"Fake dial!"<<endl;}void hangUp(){cout<<"Fake hangUp!"<<endl;}void send(){ModemConnectionController::sendImp();}void receive(){ModemConnectionController::receiveImp();}
};class DialModemController:public ModemConnectionController
{
public:DialModemController(ModemImplementation& arg):ModemConnectionController(arg){}void dial(){ModemConnectionController::dialImp();}void hangUp(){ModemConnectionController::hangUpImp();}void send(){ModemConnectionController::sendImp();}void receive(){ModemConnectionController::receiveImp();}
};

void modem()
{HayesModem modem;DialModemController dialController(modem);DedModemController dedController(modem);/*---------模拟客户代码------------*/Modem& m1 = dialController;m1.dial();m1.send();m1.receive();m1.hangUp();DedicatedModem& m2 = dedController;m2.send();m2.receive();
}

执行结果:

HayesModem dial!
HayesModem send!
HayesModem receive!
HayesModem hangUp!
HayesModem send!
HayesModem receive!


这种实现,就会有很多的好处,首次它分离了连接策略和硬件实现。ModemConnectController的每个派生类代表了一个新的连接策略。在这个策略的实现中可以使用sendImp、receiveImp、dialImp和hangImp中的一个或者多个。新imp方法的增加不会影响到使用者

可以直接替换Modem的实现,不用考虑Modem是Dedi还是Dial这个变化方向

void modem()
{HayesModem modem;USRoboticsModem modem2;DialModemController dialController(modem);DedModemController dedController(modem2);/*---------模拟客户代码------------*/Modem& m1 = dialController;m1.dial();m1.send();m1.receive();m1.hangUp();DedicatedModem& m2 = dedController;m2.send();m2.receive();
}

实现结果:

HayesModem dial!
HayesModem send!
HayesModem receive!
HayesModem hangUp!
USRoboticsModem send!
USRoboticsModem receive!


5.总结

   使用Adapter模式的解决方案是简单和直接的。它让所有的依赖关系都指向正确的方向,并且实现起来非常简单。Bridge模式稍稍有些复杂,建议开始时不要使用bridge模式,知道你明显可以看出需要完全分离出连接策略和通信策略并且需要增加新的连接策略时,才使用这种方法。


这篇关于Modem:Adapter模式与Bridge模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/913831

相关文章

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

Redis高可用-主从复制、哨兵模式与集群模式详解

《Redis高可用-主从复制、哨兵模式与集群模式详解》:本文主要介绍Redis高可用-主从复制、哨兵模式与集群模式的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录Redis高可用-主从复制、哨兵模式与集群模式概要一、主从复制(Master-Slave Repli

一文带你搞懂Redis Stream的6种消息处理模式

《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2

Nginx location匹配模式与规则详解

《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架