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

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.mzph.cn/diannao/53187.shtml

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

相关文章

前端报文加密

前端加密功能 前端提供简单的AES对称加密算法&#xff0c;注意key 和后端网关配置相同&#xff0c;这里打包混淆后&#xff0c;相对安全。 &#xff08;lun-ui\src\store\modules\user.js、base-gateway-dev.yml&#xff09; 后端解密功能 使用hutool提供的工具类进行解密pub…

Linux之grafana+onealert报警

grafana介绍 Grafana是一个开源的度量分析和可视化工具&#xff0c;可以通过将采集的数据分析&#xff0c;查询&#xff0c;然后进行可视化的展示,并能实现报警。 grafana安装与登录 在grafana服务器上安装grafana 下载地址&#xff1a;https://grafana.com/grafana/downloa…

Opencv中的直方图(4)局部直方图均衡技术函数createCLAHE()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 创建一个指向 cv::CLAHE 类的智能指针并初始化它。 函数原型 Ptr<CLAHE> cv::createCLAHE (double clipLimit 40.0,Size tileGridSize…

12、Django Admin在列表视图页面上显示计算字段

两种方法&#xff1a; 注册模型有两种方式&#xff0c;需要首先添加或者修改admin中的注册模型如下方式 admin.register(Origin) class OriginAdmin(admin.ModelAdmin):list_display ("name",) 1、在models的模型类中添加函数 def hero_count(self,):return sel…

评价决策类——层次分析法+数学建模+实战分析

目录 一、前言 二、历年题型分析 2.1 常用算法归纳 2.1.1 优化类算法 2.1.2 预测类算法 2.1.3 评价决策类 2.1.4 NP-hard类 2.2 评价类模型求解 2.2.1 层次分析法&#xff08;AHP&#xff09; 2.2.2 多指标评价法&#xff08;MCDA&#xff09; 2.2.3 算法区别 三、层…

如何把自动获取的ip地址固定

在大多数网络环境中&#xff0c;‌设备通常会自动从DHCP服务器获取IP地址。‌这种动态分配IP的方式虽然灵活方便&#xff0c;‌但在某些特定场景下&#xff0c;‌我们可能需要将设备的IP地址固定下来&#xff0c;‌以确保网络连接的稳定性和可访问性。‌本文将详细介绍如何把自…

MySQL-基础篇-事务(事务简介、事务操作、事务的四大特性、并发事务引发的问题、事务的隔离级别)

文章目录 1. 事务简介2. 事务操作2.1 未控制事务2.2 控制事务2.2.1 查看事务的提交方式2.2.2 设置事务的提交方式2.2.3 提交事务2.2.4 回滚事务2.2.5 开启事务2.2.6 完善转账案例 3. 事务的四大特性&#xff08;ACID&#xff09;4. 并发事务引发的问题5. 事务隔离级别5.1 演示5…

羊大师:白露养生经,羊奶不可少

随着秋风渐起&#xff0c;白露悄然而至&#xff0c;自然界中的万物开始展现出成熟与收获的喜悦。在这个季节转换的微妙时刻&#xff0c;我们的身体也需要顺应天时&#xff0c;进行一番细致的调养。白露养生&#xff0c;不仅是对自然的敬畏&#xff0c;更是对健康生活的追求。 在…

橘子学ES实战操作之管道类型Ingest pipelines的基本使用

简介 我们在使用ES的时候&#xff0c;经常的用法就是把其他数据源比如Mysql的数据灌到ES中。 借用ES的一些功能来提供数据的全文检索以及聚合分析之类的功能。 在这个灌数据的过程中&#xff0c;我们经常会对数据做一些治理&#xff0c;类似ETL的能力。然后把治理后的数据写入…

Eclipse 自定义字体大小

常用编程软件自定义字体大全首页 文章目录 前言具体操作1. 打开设置对话框2. 打开字体设置页面3. 找到Text Font&#xff0c;点击修改4. 修改字体 前言 Eclipse 自定义字体大小&#xff0c;统一设置为 Courier New &#xff0c;大小为 三号 具体操作 【Windows】>【Perfer…

Mac M1 安装Hadoop教程(安装包安装)

一、引言 前面一期&#xff0c;我分享了通过homebrew方式安装Hadoop&#xff0c;本期我将通过安装包方式介绍下hadoop如何安装。二、下载open jdk8 官方下载地址 注意如果是x86架构的苹果电脑&#xff0c;Architecture选择x86 64-bit或者 x86-32bit。 下载后&#xff0c;将得…

移动UI:成就勋章页面该如何设计,用例子说明。

移动应用的UI成就勋章页面通常是一个展示用户在应用中取得成就和获得勋章的页面。这种页面通常用于激励用户参与应用的活动&#xff0c;增加用户的参与度和忠诚度。 UI设计成就勋章页面时&#xff0c;一般会包括以下元素和功能&#xff1a; 1. 勋章列表&#xff1a; 展示用户…

Codeforces Round 970 (Div. 3) (个人题解)(未补完)

前言&#xff1a; 昨天晚上的比赛&#xff0c;可惜E题太笨了没想到如何解决&#xff0c;不过好在看到F过的多直接跳过去写F了&#xff0c;能过个5个也还不错了&#xff0c;而且一个罚时也没吃。之后的题我还是会再能补的时候补完的噢&#xff01; 正文&#xff1a; 链接&…

Context-Aware Depth and Pose Estimation for Bronchoscopic Navigation翻译

文章目录 摘要1.引言2. 相关工作3. 方法概述3.1. 基于CT数据的虚拟深度生成3.2. 基于视频帧的上下文感知深度估计3.3. 方法概述3.3.1. 从CT数据生成虚拟深度图3.3.2. 基于上下文感知的深度估计从视频帧中3.3.3. 相机姿态估计通过 2D/3D 配准 4. 实验4.1. 数据集和基线4.2. 实现…

【C++ Primer Plus习题】10.8

问题: 解答: main.cpp #include <iostream> #include "List.h" using namespace std;int main() {Item item 0;List list;cout << "请输入item(0结束):";cin >> item;while (item!0){cin.get();list.add(item);cout << "请…

C4单细胞|国产平台解密睾丸肿瘤细胞“朋友圈”

单细胞&#xff08;核&#xff09;转录组学已成为生命科学研究的基础工具&#xff0c;在疾病、免疫、生命演化、器官结构、发育等领域发挥重要作用&#xff0c;但规模化研究往往是制约单细胞层面研究的重要因素。为了突破单细胞研究门槛高、费用高的瓶颈&#xff0c;凌恩生物重…

CentOS 7 上安装 JDK 8 的步骤:

CentOS 7 上安装 JDK 8 的步骤&#xff1a; 一、下载 JDK 1.可以从 Oracle 官网下载 JDK 8&#xff0c;但需要注册账号。也可以从其他可靠的镜像站点下载。 jdk.java.net http://jdk.java.net/ 2.例如&#xff0c;可以从 Oracle 官网下载 Linux 版本的 JDK 8 压缩包&#x…

自建DCI为何超过租赁DCI:优势探索

在现代数据的基础设施发展过程中&#xff0c;数据中心&#xff08;DCI&#xff09; 的自建和租赁选择变得越来越关键。随着对数据传输需求的增加和行业的发展&#xff0c;运行商必须要仔细考虑哪种方法更符合其战略目标和运营要求。相较于租赁式DCI的便捷性和灵活性&#xff0c…

CSS3换装达人原理

引言 换装或者是换皮肤是常见的行为&#xff0c;我们可以先看下效果&#xff1a; 选择不同的颜色&#xff0c;就可以秒变人物服装的颜色&#xff0c;原理其实非常简单 实现步骤 主要分为三步&#xff0c;即素材的提供、布局样式、动态控制 图片提供 提供两张图片&#xf…

[数据集][目标检测]街道乱放广告牌检测数据集VOC+YOLO格式114张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;114 标注数量(xml文件个数)&#xff1a;114 标注数量(txt文件个数)&#xff1a;114 标注类别…