设计模式-装饰器代理观察者

news/2024/9/17 7:40:38/ 标签: 设计模式, c++

3.7 装饰器模式(代码见vs)

装饰器又叫做包装模式,允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法完整性的前提下,提供了额外的功能。

如下图:装饰器类ConDecorator想给汽车类增加新的功能,并且不能改变原来的代码,怎么做?

可以把Car基类作为装饰器类的属性,然后通过继承重写Car的原来的show方法,并且在装饰器的show方法内部,通过属性Car类对象(这个对象传过来的是一个具体的子类对象,比如Audi或者Bmw)调用具体子类对象的show方法,在这个方法基础上,增加新的方法和功能。这样就实现了不改变原来代码的基础上,增加了新的功能。然后我们通过使用装饰器对象,就可以使用装饰后的新功能。

装饰器模式,既有继承又有合成关系。

#include <iostream>
using namespace std;//装饰器模式
class Car
{
public:virtual void show() = 0;
};
//具体车型:奥迪
class Audi :public Car
{
public:virtual void show() { cout << "这是一辆奥迪车,标配。"; }
};
//具体车型:宝马
class Bmw :public Car
{
public:virtual void show() { cout << "这是一辆宝马车,标配。"; }
};
//第一个装饰器,加上定速巡航
class ConDecorator01 :public Car
{Car* car;
public:ConDecorator01(Car* c) :car(c) {};void show(){//先调用原来的show方法,通过属性car来调用car->show();//在原来的基础上加装饰,装定速巡航cout << "装饰了定速巡航" << endl;}
};
//第二个装饰器,加上自动刹车
class ConDecorator02 :public Car
{Car* car;
public:ConDecorator02(Car* c) :car(c) {};void show(){//先调用原来的show方法,通过属性car来调用car->show();//在原来的基础上加装饰,装自动刹车cout << "装饰了自动刹车" << endl;}
};
//第三个装饰器,加上自动泊车
class ConDecorator03 :public Car
{Car* car;
public:ConDecorator03(Car* c) :car(c) {};void show(){//先调用原来的show方法,通过属性car来调用car->show();//在原来的基础上加装饰,装自动泊车cout << "装饰了自动泊车" << endl;}
};
void test01()
{Car* c1 = new Bmw();//标配的宝马c1->show();cout << endl;Car* d1 = new ConDecorator01(c1);d1->show();//此时有了定速巡航功能Car* c2 = new Audi();//标配的奥迪c2->show();cout << endl;Car* d2 = new ConDecorator02(c2);d2->show();//此时有了自动刹车功能delete c1;delete c2;delete d1;delete d2;
}

装饰器模式练习:

大家知不知道QQ秀这个游戏,80后应该知道, 给动画人物搭配不同服饰。比如穿T恤,衬衫,外套,皮鞋,运动鞋,靴子...,根据下面的类图完成这个练习。

注意:这个练习跟上面的汽车例子不同,汽车例子是车有抽象层和具体层的类,装饰器只有一层,每个装饰器直接实现装饰。这个作业是被装饰的人只有一层,装饰器有两层,抽象层定义接口,不负责具体的装饰,具体装饰由具体层的装饰器完成。

//未装饰的人
class Person
{string name;
public:Person() {};//无参构造需要有,因为子类构造的时候要用Person(string na) :name(na) {};virtual void show() { cout << "我是" << name << endl; }
};
//装饰类父类,抽象类
class Finery:public Person
{
protected:Person* per;
public:Finery(Person* p) :per(p) {};//这里用到了Person的无参构造virtual void show() = 0;
};
//具体装饰:长裤
class LongTrouser :public Finery
{
public:LongTrouser(Person* p) :Finery(p) {};void show(){per->show();//调用原来未装饰的show方法//接下来加装饰cout << "穿上长裤" << endl;}
};
//具体装饰:T恤
class Tshirts :public Finery
{
public:Tshirts(Person* p) :Finery(p) {};void show(){per->show();//调用原来未装饰的show方法//接下来加装饰cout << "穿上T恤" << endl;}
};
void test02()
{Person* xm = new Person("小明");xm->show();//没装饰cout << "装饰后:" << endl;Finery* ts_xm = new Tshirts(xm);//穿上T恤ts_xm->show();Finery* lt_xm = new LongTrouser(xm);//穿上长裤lt_xm->show();delete xm;delete ts_xm;delete lt_xm;
}

3.8 代理模式(代码见vs)

代理模式也称为委托模式,作用就是为其他对象提供一种代理以控制对这个对象的访问。它允许你在不直接访问对象的情况下,通过一个代理对象来控制对该对象的访问。这个代理对象可以作为客户端和实际对象之间的中介,从而实现一些特定的控制功能,比如限制访问、记录访问日志等。

代理模式和装饰器模式很像:

1)相同点:都是继承了目标抽象类,都将目标抽象类关联到本类中作为属性。

2)代理强调的是对目标对象的控制权(强迫用户使用代理,不用就无法访问目标对象);装饰器模式强调的是在不修改源代码的基础上添加新的功能。

//代理模式
//抽象层,房东
class Landlord
{
public:virtual void rentHouse() = 0;
};
//具体的房东:Tom
class Tom :public Landlord
{
public:virtual void rentHouse() { cout << "Tom出租一套房子" << endl; }
};
//代理类
class Proxy :public Landlord
{Landlord* landlord;
public:Proxy(Landlord* land):landlord(land){}//接下来对房东出租房子的行为加限制,必须给中介交钱后,才能出租房子virtual void rentHouse(){cout << "中介先收取佣金" << endl;landlord->rentHouse();//然后才可以使用出租房子的方法}
};
void test03()
{Landlord* tom = new Tom();Landlord* proxy = new Proxy(tom);//中介代理了tom的房子proxy->rentHouse();//通过中介租房子,必须先交钱delete tom;delete proxy;
}

总结:

优点:职责清晰:真实角色就是实现实际的业务逻辑,不关心其他非本职的事务,通过后期的代理完成非本质事务,编程简单清晰。 高扩展性:具体主题角色可变。

缺点:由于在客户端和真实主题之间增加了代理,因此可能会造成请求的处理速度变慢(因为代理加了控制)。实现代理模式需要额外的工作,有些实现非常复杂。

代理模式练习:

//送礼者抽象类,某个人
class SomeOne
{
public:virtual void giveFlowers() = 0;virtual void giveDolls() = 0;virtual void giveChoc() = 0;
};
//具体送礼的人
class One :public SomeOne
{string name;
public:One(string n) :name(n) {};virtual void giveFlowers() { cout << name << "送您鲜花" << endl; }virtual void giveDolls(){ cout << name << "送您洋娃娃" << endl; }virtual void giveChoc(){ cout << name << "送您巧克力" << endl; }
};
//代理类
class Proxy_for :public SomeOne
{SomeOne* m_one;
public:Proxy_for(SomeOne* one) :m_one(one) {};virtual void giveFlowers() { cout << "送鲜花需要收取佣金100" << endl; m_one->giveFlowers(); }virtual void giveDolls() { cout << "送洋娃娃需要收取佣金150" << endl; m_one->giveDolls(); }virtual void giveChoc() { cout << "送巧克力需要收取佣金80" << endl; m_one->giveChoc(); }
};
void test04()
{SomeOne* jerry = new One("jerry");SomeOne* p = new Proxy_for(jerry);p->giveFlowers();p->giveDolls();p->giveChoc();delete jerry;delete p;
}

3.9 观察者模式(代码见vs)

观察者模式又叫做发布-订阅模式,定义了一种一对多的依赖关系,一对多,一是发布者,多是订阅者。让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动的更新。观察者往往定义一个抽象观察者,多个具体观察者。

被观察主题对象应该包含一个容器来存放观察者对象,当被观察者发生改变时通知容器内所有的观察者对象。这样才能实现一对多。

观察者对象加入到主题的容器中,相当于订阅了主题,然后就可以接收被观察者的通知。观察者也可以被删除掉,停止订阅这个主题。

需求:主题作为发布者群发消息,多个观察者订阅主题,主题有消息时通知所有观察者。

//观察者模式
//抽象观察者
class AbsObserver
{
public:virtual void update(string content) = 0;//更新的接口,参数content是更新的内容
};
//主题类,发布者
class Subject
{string title;//标题list<AbsObserver*> obs;//容器中存放抽象观察者的地址
public:Subject(string t):title(t){}~Subject(){//发布者析构的时候,需要将全部的订阅者析构掉if (obs.size()==0){return;}//如果容器中有订阅者,逐个回收for (auto o:obs){delete o;//回收每个观察者对象obs.remove(o);//将观察者对象地址从链表中移除}}void attach(AbsObserver* someone)//绑定,即将某个观察者加入订阅,加入容器中{obs.push_back(someone);}void detach(AbsObserver* someone)//解绑,将某个观察者解除订阅,从容器中移除{obs.remove(someone);delete someone;}string getTitle() { return title; }void notify(string content)//通知,将content通知给订阅者{if (obs.size()==0){return;}for (auto o:obs)//逐个通知容器内的订阅者{o->update(content);//将content传递给订阅者}}
};
//具体观察者
class ConsObserver :public AbsObserver
{string name;//观察者姓名Subject* subject;//订阅的主题
public:ConsObserver(string n, Subject* s) :subject(s), name(n) {};void update(string content)//将发布者传过来的content内容进行展示{cout << "标题:[" << subject->getTitle() << "],内容:" << content << "," << name << "已收到" << endl;}
};
void test05()
{//准备主题和观察者对象Subject* subject = new Subject("天气预报");AbsObserver* ob1 = new ConsObserver("观察者1号", subject);AbsObserver* ob2 = new ConsObserver("观察者2号", subject);//加入订阅subject->attach(ob1);subject->attach(ob2);//发布通知subject->notify("最近天气炎热并且伴有大风");//解除订阅subject->detach(ob2);//再次通知,此时ob2就收不到消息了subject->notify("中秋节期间天气很好,大家可以轻松出行");//回收的时候,只需要回收主题对象即可,主题的析构中回收了所有订阅者delete subject;
}

观察者模式总结

观察者模式的优势:主题(Subject)无需耦合某个具体的观察者(如ConsObserver),而只需要知道其抽象接口AbsObserver即可。观察者模式解除了主题和具体观察者的耦合,依赖于抽象,而不是依赖具体。从而使得观察者的变化不会影响主题。

观察者模式的缺点:性能损耗,即在函数调用前遍历观察者列表的开销。

应用场景:通知,群发的场景。

注意:在销毁观察者对象前,必须取消订阅此观察者对象,否则通知一个已销毁的观察者可能导致程序崩溃。

观察者模式练习:

在此基础上,改造主题,主题是默认的新闻主页,主题下面还有具体的频道:经济、体育、娱乐(选择一个即可)。这样主题也分为两个层,观察者可以订阅新闻主页,也可以订阅具体的频道。

//抽象观察者
class Observer
{
public:virtual void update(string content) = 0;//更新的接口,参数content是更新的内容
};
//主题类,发布者,新闻主页
class MainSubject
{string title;//标题list<Observer*> obs;//容器中存放抽象观察者的地址
public:MainSubject(){}MainSubject(string t) :title(t) {}virtual ~MainSubject(){//发布者析构的时候,需要将全部的订阅者析构掉if (obs.size() == 0){return;}//如果容器中有订阅者,逐个回收for (auto o : obs){delete o;//回收每个观察者对象obs.remove(o);//将观察者对象地址从链表中移除}}virtual void attach(Observer* someone)//绑定,即将某个观察者加入订阅,加入容器中{obs.push_back(someone);}virtual void detach(Observer* someone)//解绑,将某个观察者解除订阅,从容器中移除{obs.remove(someone);delete someone;}virtual string getTitle() { return title; }//需要写成虚函数,子类才可以实现多态virtual void notify(string content)//通知,将content通知给订阅者{if (obs.size() == 0){return;}for (auto o : obs)//逐个通知容器内的订阅者{o->update(content);//将content传递给订阅者}}
};
//具体的主题:经济主题
class Subject_jingji :public MainSubject
{string title;//标题list<Observer*> obs;//容器中存放抽象观察者的地址
public:Subject_jingji(string t) :title(t) {}~Subject_jingji(){//发布者析构的时候,需要将全部的订阅者析构掉if (obs.size() == 0){return;}//如果容器中有订阅者,逐个回收for (auto o : obs){delete o;//回收每个观察者对象obs.remove(o);//将观察者对象地址从链表中移除}}void attach(Observer* someone)//绑定,即将某个观察者加入订阅,加入容器中{obs.push_back(someone);}void detach(Observer* someone)//解绑,将某个观察者解除订阅,从容器中移除{obs.remove(someone);delete someone;}string getTitle() { return title; }void notify(string content)//通知,将content通知给订阅者{if (obs.size() == 0){return;}for (auto o : obs)//逐个通知容器内的订阅者{o->update(content);//将content传递给订阅者}}
};
//具体观察者
class ConcreteObserver :public Observer
{string name;//观察者姓名MainSubject* subject;//订阅的主题
public:ConcreteObserver(string n, MainSubject* s) :subject(s), name(n) {};void update(string content)//将发布者传过来的content内容进行展示{cout << "标题:[" << subject->getTitle() << "],内容:" << content << "," << name << "已收到" << endl;}
};
void test06()
{MainSubject* sub = new MainSubject("今日新闻");//首页MainSubject* sub_jingji = new Subject_jingji("经济新闻");//经济频道主题Observer* ob1 = new ConcreteObserver("观察者1号", sub);//观察者1号订阅了首页Observer* ob2 = new ConcreteObserver("观察者2号", sub_jingji);//观察者2号订阅了经济Observer* ob3 = new ConcreteObserver("观察者3号", sub_jingji);//观察者3号订阅了经济//加入订阅sub->attach(ob1);sub_jingji->attach(ob2);sub_jingji->attach(ob3);//发布信息sub->notify("各类新闻汇聚于此");sub_jingji->notify("中指研究院发布了2024年1-8月份全国房价走势,同比和环比均下架");//取消订阅sub_jingji->detach(ob2);sub_jingji->notify("中国汽车出口份额世界第一");delete sub;delete sub_jingji;
}


http://www.ppmy.cn/news/1522734.html

相关文章

ssm微信小程序的英语学习激励系统论文源码调试讲解

2 关键技术介绍 2.1 SSM框架 开发信息管理系统的主流框架是SSM&#xff08;Spring Spring MVC MyBatis&#xff09;&#xff0c;SSM框架web层使用Spring MVC框架&#xff0c;使传输前后端数据变得简单&#xff1b;对于业务层使用Spring作为轻量级控制反转和面向切面的容器框…

Java JVM 垃圾回收算法详解

Java 虚拟机&#xff08;JVM&#xff09;是运行 Java 应用程序的核心&#xff0c;它的垃圾回收&#xff08;Garbage Collection, GC&#xff09;机制是 JVM 中非常重要的一个部分。垃圾回收的主要任务是自动管理内存&#xff0c;回收那些不再被使用的对象&#xff0c;从而释放内…

网吧业务安全对抗(有源码)

网吧业务竞争激烈&#xff0c;网吧都会有以下系统软件。 无盘: 无盘是指没有硬盘。好处是统一维护管理和节约成本。本人研究无盘好几年&#xff0c;后面会专门发帖介绍。 计费: 是指收费系统。 营销软件: 包括销售饮品、‌零食和向客户发送电子邮件营销和短信营销等。产品如…

Redis配置

redis配置管理 可以直接打开配置文件进行查看和修改&#xff0c;也可以通过config命令来进行查看和修改。 配置文件位置 Linux中默认在/etc/redis/redis.conf。Windows 中默认在安装目录下&#xff0c;名为 redis.windows.conf。 查看redis配置 使用redis-cli连接redis后&…

AI绘画SD中如何安装/更新/卸载 Stable Diffusion WebUI 插件?SD新手必看的保姆级教程!

大家好&#xff0c;我是画画的小强 最近有一部分朋友对如何在AI绘画StableDiffusion中 安装管理 WebUI 插件十分陌生&#xff0c;不知道如何下手。 今天就系统地为大家介绍一下 WebUI 插件安装、更新、卸载的相关知识&#xff0c;让初学者能快速掌握插件的使用方法&#xff0c…

MySQL 的关键字

MySQL 中的关键字是数据库中具有特殊含义的保留字&#xff0c;它们用于定义数据库结构、操作数据库数据和控制数据库行为。关键字在 MySQL 查询中扮演着至关重要的角色&#xff0c;因为它们是 SQL 语句的核心组成部分。 1. 数据定义语言 (DDL) 关键字 数据定义语言 (DDL) 关键…

FC 协议概述

FC协议&#xff0c;全称为Fibre Channel&#xff08;光纤通道&#xff09;协议&#xff0c;是一种高速网络技术&#xff0c;主要用于连接计算机和存储设备。它最初在1988年开发&#xff0c;目的是提高硬盘协议的传输带宽&#xff0c;侧重于数据的快速、高效、可靠传输。到了上世…

滚雪球学MyBatis-Plus(09):乐观锁与性能优化

前言 在上期内容中&#xff0c;我们详细介绍了 MyBatis Plus 的条件构造器。通过使用 QueryWrapper 和 LambdaQueryWrapper&#xff0c;我们学会了如何构建各种复杂的查询条件&#xff0c;并将这些条件应用于服务层和控制层。条件构造器的灵活性和强大功能&#xff0c;使得查询…

基于django的在线音乐网站设计/基于python的音乐播放系统

Django在线音乐网站设计 摘要&#xff1a;计算机网络如果结合使用信息管理系统&#xff0c;能够提高管理员管理的效率&#xff0c;改善服务质量。优秀的在线音乐网站设计能够更有效管理音乐资讯规范&#xff0c;帮助管理者更加有效管理音乐网站&#xff0c;可以帮助提高克服人工…

JavaScript 实用技巧

1. 使用 const 和 let 替代 var 在 ES6 之前&#xff0c;我们通常使用 var 声明变量。但如今&#xff0c;推荐使用 const 和 let&#xff0c;因为它们具有块级作用域&#xff0c;可以避免很多潜在的问题。 const PI 3.14; // 常量&#xff0c;无法重新赋值 let age 25; // …

misc流量分析

一、wireshark语法 1、wireshark过滤语法 &#xff08;1&#xff09;过滤IP地址 ip.srcx.x..x.x 过滤源IP地址 ip.dstx.x.x.x 过滤目的IP ip.addrx.x.x.x 过滤某个IP &#xff08;2&#xff09;过滤端口号 tcp.port80tcp.srcport80 显示TCP的源端口80tcp.dstport80 显示…

【高阶数据结构】B树、B+树、B*树

B树、B树、B*树 1. 常见的搜索结构2. B树概念3. B树的插入分析4. B树的插入实现4.1 B树的节点设计4.2 B树的部分插入实现14.3 B树的查找4.4 B树的部分插入实现24.5 插入key的过程4.7 B树的插入完整代码4.8 B树的简单验证4.9 B树的删除4.10 B树的性能分析 5. B树6. B*树7. 总结8…

gitk无法打开

1、电脑重装&#xff0c;重新安装git工具后&#xff0c;发现无法打开现有的仓库&#xff0c;报错如下&#xff1a; 搜索网上的信息&#xff0c;显示是目录下没有.git文件夹&#xff0c;但是在xshell查看文件夹是存在的。 然后进行测试git log指令发现也无法进行显示。 然后按…

网站安全问题整改

网站安全、政务云、第三方安全检测机构等评测出来的网站web安全问题整改&#xff0c;如果你也正需要做这方面&#xff0c;请联系我吧

一、selenium自动化简介selenium工具集

文章目录 一、简介二、组成部分三、selenium工具集3.1 Selenium IDE3.2 Selenium WebDriver3.3 Selenium Grid3.4 Appium 一、简介 官方网站 Selenium 是支持 web 浏览器自动化的一系列工具和库的综合项目。 它提供了扩展来模拟用户与浏览器的交互&#xff0c;用于扩展浏览器分…

ListBox等控件的SelectedItem,SelectedValue,SelectedValuePath属性详解

引言 初学WPF可能会对诸如ComboBox、ListBox等集合控件的当前选择项的绑定有所疑惑&#xff0c;控件提供了两个可绑定对象&#xff1a;SelectedItem\SelectedValue&#xff0c;同时还有DisplayMemberPath\SelectedValuePath。本节来讲述一下它们的设计意图和用法。 1、Select…

Apache POl的使用(导出报表)

介绍 Apache POl是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是&#xff0c;我们可以使用 PO! 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。一般情况下&#xff0c;POI都是用于操作 Excel 文件。 Apache POl的应用场景: 银行网银系统导出交…

java常用集合方法

目录 一、Iterator接口二、Iterable接口三、Collection接口四、Collection与Iterable关系 一、Iterator接口 Iterator 是一个集合迭代器接口&#xff0c;它提供了以下方法&#xff1a; 判断迭代器中是否还拥有元素&#xff0c;有则返回true&#xff0c;否则返回false boolean …

CTK框架(四): 插件编写

目录 1.生成插件 1.1.环境说明 1.2.服务类&#xff0c;纯虚类&#xff0c;提供接口 1.3.实现插件类&#xff0c;实现纯虚函数 1.4.激活插件&#xff0c;加入ctk框架的生命周期中 1.5.添加资源文件 1.6..pro文件 2.使用此插件 3.总结 1.生成插件 1.1.环境说明 编译ct…

ArcGIS Pro SDK (十二)布局 9 布局元数据与图框

ArcGIS Pro SDK (十二)布局 9 布局元数据与图框 文章目录 ArcGIS Pro SDK (十二)布局 9 布局元数据与图框1 布局元数据1.1 布局元数据2 布局地图框2.1 更改与地图框关联的地图2.2 更改地图框照相机设置2.3 将地图框缩放到单个图层的范围2.4 将地图框范围更改为多个图层中的…