C++ 设计模式 ---- 接口隔离模式

news2024/12/23 4:44:24

“接口隔离”模式

在组件构建过程中,某些接口之间直接的依赖常常会带来很多问题、甚至根本无法实现。采用添加一层间接(稳定)接口,来隔离本来互相紧密关联的接口是一种常见的解决方案。

典型模式:

1、Facade

2、Proxy

3、Adapter

4、Mediator

Facade 门面模式

系统间耦合的复杂度

在这里插入图片描述

**动机(Motivation)**💡:

上述A方案的问题在于组件的客户和组件中各种复杂的子系统有了过多的耦合,随着外部客户程序和各子系统的演化,这种过多的耦合面临很多变化的挑战。

**问题思考(Consider)**🤔:

如何简化外部客户程序和系统间的交互接口?如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦?

C++执行代码:

Facade.cpp

#include <iostream>
class SubsystemA
{
public:
    void suboperation()
    {
        std::cout << "Subsystem A method" << std::endl;
    }
};

class SubsystemB
{
public:
    void suboperation()
    {
        std::cout << "Subsystem B method" << std::endl;
    }
};

class SubsystemC
{
public:
    void suboperation()
    {
        std::cout << "Subsystem C method" << std::endl;
    }
};

class Facade
{
public:
    Facade() : subsystemA(), subsystemB(), subsystemC() {}

    void operation1()
    {
        subsystemA->suboperation();
        subsystemB->suboperation();
    }

    void operation2()
    {
        subsystemC->suboperation();
    }

private:
    SubsystemA* subsystemA;
    SubsystemB* subsystemB;
    SubsystemC* subsystemC;
};


int main()
{
    Facade* facade = new Facade();

    facade->operation1();
    facade->operation2();
    delete facade;

    return 0;
}

Facade1.cpp

#include <iostream>
using namespace std;

//子系统:牛奶系统、能量转换系统、锁定目标系统、炮弹发射系统、雷电炮加强声势
//这些子系统都可以独立工作,并且都提供了给外部调用的接口

//牛奶系统
class MilkSystem {
public:
	void immitMilk() {
		cout << "牛奶炮原料<牛奶>已经注入完毕..." << endl;
	}
};

//能量转换系统
class EnergySystem {
public:
	void energyConvert() {
		cout << "已经将所有牛奶全部吸收并且转换成能量" << endl;
	}
};

//锁定目标系统
class AimLockSystem {
public:
	void aimLock() {
		cout << "已经瞄准并且锁定了目标..." << endl;
	}
};

//炮弹发射系统
class Cannon {
public:
	void cannonFire()
	{
		cout << "朝着目标,全力开炮..." << endl;
	}
};

//雷电炮加强声势
class ThunderCannon {
public:
	void thunderCannonFire() {
		cout << "雷电炮加强声势..." << endl;
	}
};

//上层接口
class MilkCannon {
public:
	MilkCannon() {
		m_milk = new MilkSystem;
		m_energy = new EnergySystem;
		m_aimLock = new AimLockSystem;
		m_cannon = new Cannon;
		m_thunderCannon = new ThunderCannon;
	}
	~MilkCannon() {
		delete m_milk;
		delete m_energy;
		delete m_aimLock;
		delete m_cannon;
		delete m_thunderCannon;
	}
	//瞄准并锁定目标
	void aimAndLcok() {
		m_milk->immitMilk();
		m_energy->energyConvert();
		m_aimLock->aimLock();
	}
	//开炮
	void fire() {
		m_cannon->cannonFire();
		m_thunderCannon->thunderCannonFire();
	}
private:
	MilkSystem* m_milk = nullptr;
	EnergySystem* m_energy = nullptr;
	AimLockSystem* m_aimLock = nullptr;
	Cannon* m_cannon = nullptr;
	ThunderCannon* m_thunderCannon = nullptr;
};

int main() {
	MilkCannon* milkCannon = new MilkCannon;
	milkCannon->aimAndLcok();
	milkCannon->fire();
	delete milkCannon;
	return 0;
}

模式定义

为子系统中的一组接口提供一个一致(稳定)的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用(复用)。

----《设计模式》GoF

结构(Structure)

在这里插入图片描述

要点总结

从客户程序的角度来看,Facade模式简化了整个组件系统的接口,对于组件内部与外部客户程序来说,达到了一种“解耦”的效果----内部子系统的任何变化不会影响到Facade接口的变化。

Facade设计模式更注重从架构的层次去看整个系统,而不是单个类的层次。Facade很多时候更是一种架构设计模式。

Facade设计模式并非一个集装箱,可以任意地放进任何多个对象。Facade模式中组件的内部应该是“相互耦合关系比较大的一系列组件”,而不是一个简单的功能集合。

Proxy 代理模式

**动机(Motivation)**💡:

在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者、或者系统结构带来很多麻烦。

**问题思考(Consider)**🤔:

如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性?增加一层间接层是软件开发中常见的解决方式。

代理模式.cpp

使用代理模式,不能改变所代理的类的接口,使用代理模式的目的是为了加强控制

#include <iostream>
using namespace std;
//抽象支付类
class PayMent {
public:
	virtual void payment() = 0;//支付
	virtual ~PayMent() {}
};

//现金支付
class Cash :public PayMent {
public:
	Cash(int money) {
		m_money = money;
	}
	void payment() override {
		cout << "准备结账..." << endl;
		cout << "支付金额:"<<m_money <<"元" << endl;
	}
private:
	int m_money = 0;
};

//微信支付
class WeiXin :public PayMent {
public:
	WeiXin(int money) {
		m_isPay = true;
		m_cash = new Cash(money);
	}
	~WeiXin() {
		if (m_cash != nullptr) {
			delete m_cash;
		}
	}
	//判断是否已经支付了
	bool isPay() {
		return m_isPay;
	}
	void payment()override {
		if (isPay()) {
			cout << "微信支付..." << endl;
			m_cash->payment();
		}
	}
private:
	bool m_isPay = false;
	Cash* m_cash = nullptr;
};

int main() {
	//现金支付
	PayMent* pay = new Cash(10);
	pay->payment();
	delete pay;
	cout << "===================================" << endl;
	//微信支付
	pay = new WeiXin(10);
	pay->payment();
	delete pay;
	return 0;
}

在这里插入图片描述

模式定义

为其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问。

----《设计模式》GoF

结构(Structure)

在这里插入图片描述

要点总结

“增加一层间接层”是软件系统对许多复杂问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy对象便是这一问题的常用手段。

具体proxy设计模式的实现方法,实现粒度都相差很大,有些可能对单个对象做细粒度的控制,如copy-on-write技术,有些可能对组件模块提供抽象代理层,在架构层次对对象做proxy。

Proxy并不一定要求保持接口完整的一致性,只要能过实现间接控制,有时候损及一些透明性是可以接受的。

Adapter 适配器

**动机(Motivation)**💡:

在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。

**问题思考(Consider)**🤔:

如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?

Adapter.cpp

#include <iostream>
using namespace std;
class Foreigner {
public:
	virtual string Introduction() = 0;
	void recvMessage(string msg) {
		cout << "中国导游说:" << msg << endl;
	}
	virtual ~Foreigner() {}
};

//美国人
class American :public Foreigner {
public:
	string Introduction() override {
		return string("我来自美国...");
	}
};

//法国人
class French :public Foreigner {
public:
	string Introduction() override {
		return string("我来自法国...");
	}
};

//导游
class Guide {
public:
	void recvMessage(string msg) {
		cout << msg << endl;
	}
	string sendMessage() {
		return string("欢迎来这里参观中国的名胜古迹...");
	}
};

//抽象适配器类
class AbstractAdapter {
public:
	AbstractAdapter(Foreigner* foreigner) :m_foreigner(foreigner) {};
	virtual void translateToGuide() = 0;
	virtual void translateToHuman() = 0;
	virtual ~AbstractAdapter() {}
protected:
	Guide m_guide;
	Foreigner* m_foreigner = nullptr;
};

//英语适配器
class EnglishAdapter : public AbstractAdapter {
public:
	//继承构造函数
	using AbstractAdapter::AbstractAdapter;
	void translateToGuide() override {
		string msg = m_foreigner->Introduction();
		// 翻译并将信息传递给导游对象
		m_guide.recvMessage("美国人说:" + msg);
	}
	void translateToHuman() override {
		//接受导游的信息
		string msg = m_guide.sendMessage();
		//翻译并将导游的话转发给导游
		m_foreigner->recvMessage("美国人," + msg);
	}
};

//法语适配器
class FrenchAdapter :public AbstractAdapter {
public:
	using AbstractAdapter::AbstractAdapter;
	void translateToGuide() override {
		string msg = m_foreigner->Introduction();
		//翻译并将信息传递给导游对象
		m_guide.recvMessage("法国人说:" + msg);
	}
	void translateToHuman() override {
		//接受导游的信息
		string msg = m_guide.sendMessage();
		//翻译并将导游的话转发给法国人
		m_foreigner->recvMessage("法国人," + msg);
	}
};

int main() {
	Foreigner* human = new American;
	EnglishAdapter* american = new EnglishAdapter(human);
	american->translateToGuide();
	american->translateToHuman();
	delete human;
	delete american;

	human = new French;
	FrenchAdapter* french = new FrenchAdapter(human);
	french->translateToGuide();
	french->translateToHuman();
	delete human;
	delete french;

	return 0;
}

在这里插入图片描述

模式定义

将一个类的接口转换成客户希望的另一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作。
----《设计模式》GoF

要点总结

Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用,类库迁移等方面非常有用。

GoF 23定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,一般不推荐使用。对象适配器采用“对象组合“的方式,更符合松耦合精神。

Adapter模式可以实现的非常灵活,不必拘泥于GoF 23中定义的两种结构。例如:完全可以将Adapter模式中的”现存对象”作为新的接口方法参数,来达到适配的目的。

Mediator 中介者

**动机(Motivation)**💡:

在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。

在这种情况下,我们可使用一个“中介对象”来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。

中介者模式可以减少对象之间混乱无序的依赖关系,从而使其耦合松散,限制对象之间的直接交互,迫使它们通过一个中介者对象进行合作。[来自爱编程的大丙https://subingwen.cn/design-patterns/mediator/]
推荐看爱编程的大丙的C++设计模式,写得真的很棒!

/*
 * C++ Design Patterns: Mediator
 * Author: Jakub Vojvoda [github.com/JakubVojvoda]
 * 2016
 *
 * Source code is licensed under MIT License
 * (for more details see LICENSE)
 *
 */

#include <iostream>
#include <vector>
#include <string>

class Mediator;

/*
 * Colleague classes
 * each colleague communicates with its mediator whenever
 * it would have otherwise communicated with another colleague
 */
class Colleague
{
public:
  Colleague( Mediator* const m, const unsigned int i ) : 
    mediator( m ), id( i ) {}
  
  virtual ~Colleague() {}
  
  unsigned int getID()
  {
    return id;
  }
  
  virtual void send( std::string ) = 0;
  virtual void receive( std::string ) = 0;

protected:
  Mediator *mediator;
  unsigned int id;
};

class ConcreteColleague : public Colleague
{
public:
  ConcreteColleague( Mediator* const m, const unsigned int i ) : 
    Colleague( m, i ) {}
  
  ~ConcreteColleague() {}
  
  void send( std::string msg );
  
  void receive( std::string msg )
  {
    std::cout << "Message '" << msg << "' received by Colleague " << id << std::endl;
  }
};

/*
 * Mediator
 * defines an interface for communicating with Colleague objects
 */
class Mediator
{
public:
  virtual ~Mediator() {}
  
  virtual void add( Colleague* const c ) = 0;
  virtual void distribute( Colleague* const sender, std::string msg ) = 0;

protected:
  Mediator() {}
};

/*
 * Concrete Mediator
 * implements cooperative behavior by coordinating Colleague objects
 * and knows its colleagues
 */
class ConcreteMediator : public Mediator
{
public:
  ~ConcreteMediator()
  {
    for ( unsigned int i = 0; i < colleagues.size(); i++ )
    {
      delete colleagues[ i ];
    }
    colleagues.clear();
  }
  
  void add( Colleague* const c )
  {
    colleagues.push_back( c );
  }
  
  void distribute( Colleague* const sender, std::string msg )
  {
    for ( unsigned int i = 0; i < colleagues.size(); i++ )
    {
      if ( colleagues.at( i )->getID() != sender->getID() )
      {
        colleagues.at( i )->receive( msg );
      }
    }
  }

private:
  std::vector<Colleague*> colleagues;
};

void ConcreteColleague::send( std::string msg )
{
  std::cout << "Message '"<< msg << "' sent by Colleague " << id << std::endl;
  mediator->distribute( this, msg );
}


int main()
{
  Mediator *mediator = new ConcreteMediator();
  
  Colleague *c1 = new ConcreteColleague( mediator, 1 );
  Colleague *c2 = new ConcreteColleague( mediator, 2 );
  Colleague *c3 = new ConcreteColleague( mediator, 3 );
  
  mediator->add( c1 );
  mediator->add( c2 );
  mediator->add( c3 );
  
  c1->send( "Hi!" );
  c3->send( "Hello!" );
  
  delete mediator;
  return 0;
}

模式定义

用一个中介对象来封装(封装变化)一系列的对象交互。中介者使各对象不需要显式的相互引用(编译时依赖->运行时依赖),从而使其耦合松散(管理变化),而且可以独立地改变它们之间的交互。

----《设计模式》GoF

要点总结

将多个对象间复杂的关联关系解耦,Mediator模式将多个对象间的控制逻辑进行集中管理,变“多个对象互相关联”为“多个对象和一个中介者关联”,简化了系统的维护,抵御了可能的变化。

随着控制逻辑的复杂化,Mediator具体对象的实现可能相当复杂。这时候可以对Mediator对象进行分解处理。

Facade模式是解耦系统间(单向)的对象关联关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/783568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MongoDB原生语句更新嵌套数组的值

一、更新一层嵌套数组 首先执行MongoDB原生语句脚本在user集合中产生一些样本数据,如下所示: db.user.insert({"_id":1,"title":"爱情公寓3","students":[{"student_id":1001,"student_name":"林宛瑜&quo…

Docker介绍及安装使用

Docker介绍及安装使用 一、Docker的概述1、Docker是什么&#xff1f;2、Docker的Logo3、Docker的设计宗旨&#xff08;一次封装&#xff0c;到处运行&#xff09;4、容器化越来越受欢迎的原因 二、Docker与虚拟机的区别三、Docker的使用场景四、Docker的核心概念1、镜像2、容器…

基于linux下的高并发服务器开发(第二章)- 2.25 sigprocmask 函数使用

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);- 功能&#xff1a;将自定义信号集中的数据设置到内核中&#xff08;设置阻塞&#xff0c;解除阻塞&#xff0c;替换&#xff09;- 参数&#xff1a;- how : 如何对内核阻塞信号集进行处理SIG_BLOCK: 将用户设…

【MySQl】MySQl中的乐观锁是怎么实现的

文章目录 前言一、乐观锁二、如何实现乐观锁呢&#xff0c;一般来说有以下2种方式2.1、使用数据版本&#xff08;Version&#xff09;记录机制实现2.2、乐观锁定的第二种实现方式和第一种差不多 前言 mysql中的乐观锁是怎么实现的&#xff1f;很多新手对此不是很清楚&#xff…

第一次参加【CSDN周赛(考试/编程竞赛)】第65期,应该注意些什么?都考什么题目?要具备什么知识?耗时__,我居然取得了__分的成绩

订阅专栏,学习更多干货知识!! 第一次参加 CSDN里的竞赛(考试),都需要注意些什么?考试都考了什么?要具备什么知识?本文带你了解一下!! 🤾🏿‍♂️目录 🌁一、先来看结果吧(有Bug?)🥕1.1 什么情况!🥕1.2 测评报告🥤1.2.1 选择题🥤1.2.2 编程题🥕1…

Redis持久化:分别启用rdb和aof,并查看是否有对应文件生成

一、rdb 简介&#xff1a;在指定的时间间隔内将内存中的数据集快照写入磁盘&#xff0c; 也就是Snapshot快照&#xff0c;它恢复时是将快照文件直接读到内存里。 1. 进入redis.conf文件中查看配置文件 [rootserver ~]# vim /usr/local/redis-stable/redis.conf 2.把持久化的…

《Docker数据管理:卷、挂载和持久化,保障容器环境数据安全》

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【博客682】k8s apiserver bookmarks机制以更高效检测变更

k8s apiserver bookmarks机制以更高效检测变更 list-watch背景&#xff1a; List-Watch 是kubernetes中server和client通信的最核心的机制&#xff0c; 比如说api-server监听etcd&#xff0c; kubelet监听api-server&#xff0c; scheduler监听api-server等等&#xff0c;其实…

Paragon NTFS2023最新版Mac读写NTFS磁盘工具

Paragon NTFS for Mac是Mac平台上一款非常优秀的读写工具&#xff0c;可以在Mac OS X中完全读写、修改、访问NTFS硬盘、U盘等外接设备的文件。这款软件最大的亮点简书可以让我们读写 NTFS 分区&#xff0c;因为在Mac OS X 系统上&#xff0c;默认状态下我们只能读取NTFS 分区&a…

152. 乘积最大子数组

152. 乘积最大子数组 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 152. 乘积最大子数组 https://leetcode.cn/problems/maximum-product-subarray/ 完成情况&#xff1a; 解题思路&#xff1a; 看好题目&…

Nginx 301重定向分析

参考; 404 - 墨天轮 深度硬核文:Nginx的301重定向处理过程分析 - 知乎 Nginx的301状态码处理逻辑设计 HTTP协议中3xx开头的状态响应码都是表示重定向的响应。根据RFC的定义&#xff1a; 301 Moved Permanently 302 Found 303 See Other 307 Temporary Redirect 301是永…

STL——String类(2)成员函数详解

目录 前言 一.String的成员函数&#xff1a; 1.基本成员函数 代码实验&#xff1a; 实验结果&#xff1a; 类对象每次扩容后的capacity数据展示&#xff1a; 1.2. resize()&#xff1a;调整字符串大小 1.3reserve()&#xff1a;请求更改该对象的容量capacity值 代码实验…

分组密码模式的填充

分组加密 在密码学中&#xff0c;分组加密(Block cipher)&#xff0c;又称分块加密或块密码&#xff0c;是一种对称密钥算法。 它将明文分成多个等长的模块(block)&#xff0c;使用确定的算法和对称密钥对每组分别加密解密。 常见的分组加密算法有: DES、3DES、AES、IDEA。 …

Ubuntu虚拟机部署配置

目录 虚拟机镜像下载 VirtualBox7下载 VirtualBox7安装镜像流程 创建虚拟机 虚拟机挂单独硬盘 网络设置 检查虚拟机配置 启动虚拟机 ubuntu配置 查询虚拟机IP地址 修改ROOT密码 更新apt NTP同步 挂载磁盘&关闭swap 虚拟机镜像下载 目前国内操作系统镜像源非常…

2023-07-19力扣今日二题

链接&#xff1a; 2737. 找到最近的标记节点 题意&#xff1a; 给一个n节点有向图&#xff0c;求节点s和点集marked中的最短距离&#xff0c;没有可以的到达的点则返回-1 解&#xff1a; 摇了一题困难过了2/3&#xff0c;搞不定了 没有负权边的单源最短距离&#xff0c;迪…

代码-【2 单链表A、B交集存放到C】

1&#xff09;基本思想&#xff1a; A、B两个链表的元素均递增有序&#xff0c;所以可以按顺序&#xff0c;同时从A中和B中各取一个结点的值来对比&#xff1b;如果A中结点的值比较小&#xff0c;则A中的指针右移&#xff1b;如果B中的结点的值比较小&#xff0c;则B中的指针右…

优思学院|工程师学习六西格玛有什么用?

很久以前&#xff0c;世上没有当今盛行的各种简单易用的统计软件&#xff0c;那时复杂的统计分析只能依靠公司的统计顾问。而和他们在一起工作时&#xff0c;工程师必须知道怎样进行提问。 如果工程师缺乏对统计工具的认识&#xff0c;即使拿到正确的工具也于事无补。当顾问遇…

2. Makefile之目标、依赖(附示例)

一、本节概要 本专栏所有内容围绕Makefile官方文档进行刨析&#xff0c;给出详细具体示例做辅助理解手撕Makefile官方手册 二、Makefile中的目标和依赖 在一个简单的Makefile中&#xff0c;每条规则通常由以下几个部分组成&#xff1a; target ... : prerequisites ...rec…

修改虚拟机ip为静态ip

在使用虚拟机的时候&#xff0c;默认情况下使用的DHCP协议分配的动态IP地址&#xff0c;使得每次打开虚拟机后当前的IP地址都可能会发生变化&#xff0c;这样不方便管理。为了能够给当前虚拟机设置一个静态IP地址&#xff0c;方便后期使用XShell等连接工具进行连接&#xff0c;…

C/C++内存分布

C/C内存管理 1.C/C内存分布2.C语言中动态内存管理方法2.1 malloc/calloc/realloc/free 3.C内容管理方法3.1new/delete操作内置类型3.2 new和delete操作自定义类型 4.operator new与operator delete函数5.new和delete的实现原理5.1内置类型5.2自定义类型 6定位new表达式&#xf…