软件设计模式复习 - 副本
吉林大学软件学院设计模式复习总结
一、创建型模式
(1)简单工厂模式⭐
Factory:工厂角色,负责实现创建所有实例的内部逻辑
**Product:**抽象产品角色,是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
ConcreteProduct:具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例
小知识点:
1、简单工厂模式将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
2、简单工厂模式最大的问题在于工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背的。
心得体会:
能用该方法一般就能够用工厂方法模式,简单工厂不符合开闭原则,故不到万不得已不使用。
我的例题:
小王正在编写一个简单的计算器程序,要求输入两个整数和运算符号(加、减、乘、除),输出计算结果。小王用面向过程方法编写了下面的代码。请采用面向对象方法通过恰当的设计模式对小王的代码进行重构。请写出你所选择的设计模式,画出类图,并给出核心代码。
int main()
{
int numa,numb;
char oper;
double result;
cin>>numa>>numb;
cin>>oper;
switch(oper)
{ case ‘+’: result=numa+numb; break;
case ‘-’: result=numa-numb; break;
case '': result=numanumb; break;
case ‘/’: result=numa/(double)numb; break;
}
cout<<result<<endl;
return 0;}
代码:
#include<iostream>
using namespace std;
//抽象产品角色
class calculate
{
protected:
double ans;
public:
double get_ans()
{
return ans;
}
};
//加法
class add :public calculate
{
public:
add(int x, int y)
{
ans = x + y;
}
};
//减法
class sub :public calculate
{
public:
sub(int x, int y)
{
ans = x - y;
}
};
//除法
class mul :public calculate
{
public:
mul(int x, int y)
{
ans = (double)x * y;
}
};
//乘法
class div_ :public calculate
{
public:
div_(int x, int y)
{
ans = x * y;
}
};
//简单工厂
class calculateFactory
{
public:
calculate* cal_produce(char oper, int numa, int numb)
{
if (oper == '+')
{
return new add(numa, numb);
}
else if (oper == '-')
{
return new sub(numa, numb);
}
else if (oper == '/')
{
return new div_(numa, numb);
}
else if (oper == '*')
{
return new mul(numa, numb);
}
else
cout << "无法处理运算符\"" << oper << "\" !" << endl;
}
};
//客户端
int main()
{
int numa, numb;
char oper;
double result;
cin >> numa >> oper >> numb;
calculateFactory* factory = new calculateFactory();
result = factory->cal_produce(oper, numa, numb)->get_ans();
cout << result << endl;
}
(2)工厂方法模式⭐⭐
Product:抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,是产品对象的共同父类或接口
**ConcreteProduct:**具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建
Factory:抽象工厂声明了工厂方法,返回一个产品,与具体应用无关,是具体工厂的共同父类或接口
**ConcreteFactory:**具体工厂实现了抽象工厂中的工厂方法,返回一个具体产品类的实例,与应用密切相关
小知识点:
1、工厂模式相比简单工厂,很好的符合了开闭原则。每个工厂只负责生产特定的产品。当只有一个具体工厂时,退化成为简单工厂模式。
2、工厂方法模式的本质是:延迟到子类来选择实现。
心得体会:
也就是四个类,两个实现关系,和一个依赖关系。在抽象工厂的方法中定义的create函数,返回值为抽象产品。在客户端使用时要注意进行强制指针类型转换。
我的例题
电视工厂,将原有的工厂进行分割,为每种品牌的电视机提供一个子工厂,海尔工厂专门负责生产海尔电视机,海信工厂专门负责生产海信电视机,如果需要生产TCL电视机或创维电视机,只需要对应增加一个新的TCL工厂或创维工厂即可,原有的工厂无须做任何修改,使得整个系统具有更加的灵活性和可扩展性。
#include<iostream>
using namespace std;
//抽象产品类 电视机
class TV {
public:
virtual void play() = 0;
};
//海尔电视
class HaierTV :public TV
{
public:
void play()
{
cout << "海尔电视开始运行。。。" << endl;
}
};
class HisenseTV :public TV
{
public:
void play()
{
cout << "海信电视开始运行。。。" << endl;
}
};
//抽象工厂类
class TVFactory {
public:
virtual TV* produceTV() = 0;
};
//海尔电视工厂
class HaierFactory :public TVFactory
{
public:
TV* produceTV()
{
return new HaierTV();
}
};
//海信电视工厂
class HisenseFactory :public TVFactory
{
public:
TV* produceTV()
{
return new HisenseTV();
}
};
//客户端
int main()
{
//创建两个工厂
HaierFactory* haierFactory = new HaierFactory();
HisenseFactory* hisenseFactory = new HisenseFactory();
//创建两台电视
HaierTV* haierTV = (HaierTV*)haierFactory->produceTV();
HisenseTV* hisenseTV = (HisenseTV*)hisenseFactory->produceTV();
haierTV->play();
hisenseTV->play();
return 0;
}
(3)抽象工厂模式⭐⭐⭐⭐
看懵逼了把。。。
总结一下就是这样,电子产品公司造电子产品。小米可以造小米手机和小米电脑,苹果公司可以造iphone和mac电脑,华为可以造华为手机和华为电脑。这样就有一个抽象工厂公司类,两个抽象产品手机和电脑。每个公司可以造两种产品(两个create方法)
小知识点:
心得体会:
这种模式肯定是必考的,重点在于找到找到抽象工厂和几种抽象产品,对不同种类的产品都要创建抽象类。
我的例题:
请为一校服制造厂编写一个校服生产子系统。该工厂可为多家学校生产校服,包括秋季校服和夏季校服。一套秋季校服含一件长袖上衣和一条秋季长裤,一套夏季校服含一件短袖衬衣、一件短袖T恤、一条夏季长裤和一条短裤。不同学校校服款式不同。请设计一个子系统为三所学校(一中、二中、三中)分别生产秋季校服和夏季校服(均码)。例如,当用户输入“一中+夏季”时,系统就会生产出一套一中夏季校服;当用户输入“一中+秋季”时,系统生产出一套一中秋季校服。请写出你所选择的设计模式,画出类图,并给出核心代码。
类图简要描述一下
抽象工厂:学校类。
具体工厂 :一中、二中。
抽象校服:秋季校服、夏季校服。
具体校服:一中xx、二中xx。
代码:
#include<iostream>
using namespace std;
//抽象夏季校服
class SummerUniform
{
public:
virtual void showSummerUniform() = 0;
};
//抽象秋季校服
class AutumnUniform {
public:
virtual void showAutumnUniform() = 0;
};
//一中夏季
class oneSummer :public SummerUniform {
public:
void showSummerUniform() {
cout << "生产了一中夏季校服:包括一件短袖衬衣、一件短袖T恤、一条夏季长裤和一条短裤。" << endl;
}
};
//二中夏季
class twoSummer :public SummerUniform {
public:
void showSummerUniform() {
cout << "生产了二中夏季校服:包括一件短袖衬衣、一件短袖T恤、一条夏季长裤和一条短裤。" << endl;
}
};
//一中秋季
class oneAutumn :public AutumnUniform {
public:
void showAutumnUniform() {
cout << "生产了一中秋季校服:一件长袖上衣和一条秋季长裤" << endl;
}
};
//一中秋季
class twoAutumn :public AutumnUniform {
public:
void showAutumnUniform() {
cout << "生产了二中秋季校服:一件长袖上衣和一条秋季长裤" << endl;
}
};
//抽象工厂学校
class SchoolFactory {
public:
virtual SummerUniform* createSummerUniform() = 0;
virtual AutumnUniform* createAutumnUniform() = 0;
};
//具体学校
//一中
class OneSchool :public SchoolFactory
{
public:
SummerUniform* createSummerUniform() {
return new oneSummer();
}
AutumnUniform* createAutumnUniform()
{
return new oneAutumn();
}
};
//二中
class TwoSchool :public SchoolFactory
{
public:
SummerUniform* createSummerUniform() {
return new twoSummer();
}
AutumnUniform* createAutumnUniform()
{
return new twoAutumn();
}
};
//客户端
int main()
{
//定义一中、二中
OneSchool* oneSchool = new OneSchool();
TwoSchool* twoSchool = new TwoSchool();
//生产一套一中夏季校服
oneSummer* sum1 = (oneSummer*)oneSchool->createSummerUniform();
//生产一套二中秋季
twoAutumn* autm2 = (twoAutumn*)twoSchool->createAutumnUniform();
sum1->showSummerUniform();
autm2->showAutumnUniform();
return 0;
}
(4)建造者模式⭐⭐⭐
**Builder:**抽象建造者为创建一个产品对象的各个部件指定抽象接口
**ConcreteBuilder:**具体建造者实现了抽象建造者接口,实现各个部件的构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象
Director:指挥者负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造
Product:产品角色是被构建的复杂对象,包含多个组成部件
小知识点
1、建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。**(套餐内有主食、饮品、小吃)**这样类似的规律
2、如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。(套餐种类太多)
心得体会:说白了就是构建一个产品时,把他的各种零件分开构造,而每个零件有多种构造方法,这些零件的构造顺序由指挥者决定,而零件的各种构造组合(如果构造各种零件)由一个构建者来实现。指挥者调用它所维护的构建者的方法即可。
具体建造者中可以有一个产品类的引用!!!(指针)也可没有 有依赖关系能创建就行,最好有,比较方便,但是有可能只能创建一个(只在构造函数new了一次)。
我的例题:
小王正在设计一个导出数据的应用框架。客户要求:导出数据可能存储成不同的文件格式,例如:文本格式、数据库备份形式、Excel格式、Xml格式等等,并且,不管什么格式,导出数据文件都分成三个部分,分别是文件头、文件体和文件尾,在文件头部分,需要描述如下信息:分公司或门市点编号、导出数据的日期,对于文本格式,中间用逗号分隔,在文件体部分,需要描述如下信息:表名称、然后分条描述数据,对于文本格式,表名称单独占一行,数据描述一行算一条数据,字段间用逗号分隔,在文件尾部分,需要描述如下信息:输出人。请写出你所选择的设计模式,画出类图,并给出核心代码。
#include<iostream>
using namespace std;
class Document {
string head;
string body;
string tail;
public:
void setHead(string str)
{
head = str;
}
void setBody(string str)
{
body = str;
}
void setTail(string str)
{
tail = str;
}
void show()
{
cout << head << endl;
cout << body << endl;
cout << tail << endl;
}
};
//抽象建造者 格式
class Builder {
public:
virtual void builderHead() = 0;
virtual void builderBody() = 0;
virtual void builderTail() = 0;
virtual Document* getResult() = 0;
};
//文本格式
class TextBuilder :public Builder
{
Document* document;
public:
TextBuilder()
{
document = new Document();
}
void builderHead()
{
document->setHead("Head:XXX公司,XXXX年XX月XX日");
}
void builderBody()
{
document->setBody( "Body:表名称\n数据1\n数据2");
}
void builderTail()
{
document->setTail( "Tail:输出入XXX");
}
Document* getResult()
{
return document;
}
};
//其他格式
//xml格式
//指导者
class Director
{
Builder* builder;
public:
void setBuilder(Builder* builder)
{
this->builder = builder;
}
Document* construct()
{
builder->builderHead();
builder->builderBody();
builder->builderTail();
return builder->getResult();
}
};
//客户端
int main()
{
//创建一个建造者
TextBuilder* textBuilder = new TextBuilder();
//创建一个指导者
Director* director = new Director();
director->setBuilder(textBuilder);
//用指导者创建文本文件
Document* doc=director->construct();
doc->show();
return 0;
}
(5)原型模式⭐⭐
Prototype:抽象原型类是定义具有克隆自己的方法的接口
ConcretePrototype:具体原型类实现具体的克隆方法,在克隆方法中返回自己的一个克隆对象
Client:在客户类中只需要直接实例化或通过工厂方法等方式创建一个对象,再通过调用该对象的克隆方法复制得到多个相同的对象
小知识点:
1、原型模式的动机:使用原型模式来复制一个对象的自身,从而克隆出多个与原型对象一模一样的对象。
2、对比拷贝构造,通过原型对象创建新的对象,就不再需要关心/知道这个对象本身的类型,如复印机。
对比拷贝构造,原型模式有助于符合里氏替换原则,如水果篮里放入水果副本。
3、当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例的创建效率。
4、需要避免创建一个与产品类层次平行的工厂类层次时,并且类的实例对象只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
心得体会:
1、感觉该模式比较简单,重要的是判断清楚是要浅拷贝还是深拷贝,如果需要深拷贝,在c++中可以选择先用类的默认浅拷贝,再对复合类型成员进行new。
我的例题:
由于邮件对象包含的内容较多(如发送者、接收者、标题、内容、日期、附件等),某系统中现需要提供一个邮件复制功能,对于已经创建好的邮件对象,可以通过复制的方式创建一个新的邮件对象,如果需要改变某部分内容,无须修改原始的邮件对象,只需要修改复制后得到的邮件对象即可。使用原型模式设计该系统。在本实例中使用浅克隆实现邮件复制,即复制邮件(Email)的同时不复制附件(Attachment)。
#include<iostream>
using namespace std;
//附件 模拟深拷贝指针
class Attachment {
private:
static int num;
int mynum;
public:
Attachment()
{
num++;
mynum = num;
}
void download()
{
cout << "下载附件"<<mynum << endl;
}
};
int Attachment::num = 0;
//原型类克隆接口
class Cloneable {
protected:
Attachment* atm;
public:
Cloneable(Attachment* atm):atm(atm){}
virtual Cloneable* clone() = 0;
virtual void display()=0;
Attachment* getAttachment()
{
return atm;
}
};
//邮件
class Email :public Cloneable
{
public:
Email(Attachment* atm):Cloneable( atm){}
Cloneable* clone()
{
//先调用前拷贝
Email* temp = new Email(*this);
//再对复合数据类型进行深拷贝
temp->atm = new Attachment();
return temp;
}
void display()
{
cout << "展示邮件内容..." << endl;
atm->download();
}
};
//客户端
int main()
{
//定义一个邮件附件1
Attachment *atm1 = new Attachment();
//定义两个邮件
Email* email,*copyEmail;
email = new Email(atm1);
copyEmail = (Email*)email->clone();
email->display();
copyEmail->display();
return 0;
}
(6)单例模式⭐
这个模式我就给出两种示例代码吧。
总结:单例模式: 类中维护一个1、static本类型指针p,2、构造函数私有。3、一个创建本类型类的static create()函数,只有当p==NULL时才new,否则直接返回p。
//1.单例模式
#include <iostream>
using namespace std;
class TicketMaker {
private:
//只有一个TickerMaker对象
static TicketMaker* countTicket;
static int ticket;
//构造函数私有
TicketMaker() {}
public:
static TicketMaker* getNextTicketNumber() {
if (countTicket == nullptr) {
countTicket = new TicketMaker();
}
ticket += 1;
return countTicket;
}
void showMessage() {
cout << ticket << endl;
}
};
TicketMaker* TicketMaker::countTicket = nullptr;
int TicketMaker::ticket = 1000;
int main() {
for (int i = 0; i < 10; i++) {
TicketMaker* object = TicketMaker::getNextTicketNumber();
object->showMessage();
}
return 0;
}
//2.只允许创建固定数目个实例对象时
1、构造函数私有。2、用一个count计数,count再构造函数++,在析构函数(public)–。
#include <iostream>
using namespace std;
class DBConnections {
private:
static DBConnections* temp;
static int count;
DBConnections() {
count++;
}
public:
~DBConnections()
{
count--;
}
static DBConnections* ConnectionInfo() {
if (count < 3) {
temp = new DBConnections();
cout << "这是第 " << count << " 个对象" << endl;
}
if (count >= 3)
cout << "已经有了三个实例化对象!" << endl;
return temp;
}
};
DBConnections* DBConnections::temp = nullptr;
int DBConnections::count = 0;
int main() {
for (int i = 0; i < 10; i++) {
DBConnections* object = nullptr;
object = DBConnections::ConnectionInfo();
delete object;
}
return 0;
}
二、结构型模式
(1)适配器模式⭐
类适配器
对象适配器
对象适配器和类适配器是两种常见的适配器模式实现方式,它们都用于解决接口不兼容的问题,但在实现上有一些区别。
异同点如下:
**实现方式:**对象适配器使用对象组合的方式,将适配器和被适配者进行关联;而类适配器使用类的继承关系,通过多重继承同时继承适配器和被适配者。
关联关系:对象适配器通过将适配器与被适配者关联,使得适配器可以调用被适配者的方法;类适配器通过继承被适配者和实现目标接口,使得适配器可以同时具有被适配者和目标接口的行为。
对被适配者的透明性:对象适配器可以适配多个被适配者对象,因为适配器与被适配者是通过关联关系建立联系的,可以灵活地替换被适配者对象;类适配器只能适配一个具体的被适配者类,因为适配器是通过继承被适配者类实现的。
对目标接口的实现方式:对象适配器需要实现目标接口,并在实现中调用被适配者对象的方法来完成适配;类适配器通过继承被适配者类,可以直接使用被适配者类的方法来实现目标接口。
对被适配者类的支持:对象适配器可以适配不兼容的被适配者类,包括无法修改的或者不兼容的类;类适配器要求被适配者类必须是可继承的,因为适配器需要通过继承被适配者类来实现适配。
总的来说,对象适配器和类适配器都可以实现适配器模式,但在实现方式、关联关系、透明性和对被适配者类的支持等方面有所不同。选择哪种适配器实现方式取决于具体的需求和设计考虑。
**Target:**目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类
**Adapter:**适配器类可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系
**Adaptee:**适配者类即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码
Client:客户类
小知识点:
1、
心得体会:
1、如果一眼看出来题目中存在接口方法名字和类的实现方法名字不一样,则肯定考虑适配器模式。
我的例题:
小王正在开发一套视频监控系统,考虑到Windows Media Player和Real Player是两种常用的媒体播放器,尽管它们的API结构和调用方法存在区别,但这套应用程序必须支持这两种播放器API,而且在将来可能还需要支持新的媒体播放器。请你针对上面的描述,帮助小王进行设计,给出设计思路及所采用的设计模式,画出类图,并写出关键代码。
描述类图:应用有一个调用接口,为media和real两个播放器增加两个适配者类。
代码:
#include<iostream>
using namespace std;
// 媒体播放器接口
class MediaPlayer {
public:
virtual void play() = 0;
virtual void stop() = 0;
};
// Windows Media Player类
class WindowsMediaPlayer {
public:
void playWindowsMedia() {
// 调用Windows Media Player的API来播放媒体
cout << "Windows Media Player 开始播放媒体" << endl;
}
void stopWindowsMedia() {
// 调用Windows Media Player的API来停止媒体
cout << "Windows Media Player 停止播放媒体" << endl;
}
};
// Real Player类
class RealPlayer {
public:
void playRealMedia() {
// 调用Real Player的API来播放媒体
cout << "Real Player 开始播放媒体" << endl;
}
void stopRealMedia() {
// 调用Real Player的API来停止媒体
cout << "Real Player 停止播放媒体" << endl;
}
};
// Windows Media Player适配器
class WindowsMediaPlayerAdapter : public MediaPlayer {
private:
WindowsMediaPlayer* wmp;
public:
WindowsMediaPlayerAdapter(WindowsMediaPlayer* player) : wmp(player) {}
void play() override {
wmp->playWindowsMedia();
}
void stop() override {
wmp->stopWindowsMedia();
}
};
// Real Player适配器
class RealPlayerAdapter : public MediaPlayer {
private:
RealPlayer* rp;
public:
RealPlayerAdapter(RealPlayer* player) : rp(player) {}
void play() override {
rp->playRealMedia();
}
void stop() override {
rp->stopRealMedia();
}
};
int main() {
WindowsMediaPlayer wmp;
RealPlayer rp;
// 创建媒体播放器适配器
WindowsMediaPlayerAdapter wmpAdapter(&wmp);
RealPlayerAdapter rpAdapter(&rp);
// 在视频监控系统中使用适配器
MediaPlayer* mediaPlayers[] = { &wmpAdapter, &rpAdapter };
for (int i = 0; i < 2; i++) {
mediaPlayers[i]->play();
mediaPlayers[i]->stop();
}
return 0;
}
(2)桥接模式⭐⭐⭐⭐
Implementor:实现类接口,定义实现类的接口,这个接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor接口仅提供基本操作,对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,Abstraction中不仅拥有自己的方法,还可以调用到Implementor中定义的方法,使用关联关系来替代继承关系。
ConcreteImplementor:具体实现类,具体实现Implementor接口,在不同的具体实现类中提供基本操作的不同实现,在程序运行时,具体实现类对象将替换其父类对象,提供给抽象类具体的业务操作方法。
小知识点:
心得体会:
1、桥接模式顾名思义,将两个类用桥搭连起来。适用于两个属性组合较多时,如果封装到一个类里面,那将是m*n个类,如果将这两个类用桥接模式,则类的数量变为m+n。
2、为什么是四颗星呢?难在?要正确识别出要设计的系统中存在的两个变化的维度。
我的例题:
开发一个计算机操作系统的线程调度程序,要求实现时间片调度和抢占调度这2种调度算法,支持Windows、Unix和Linux这3个操作系统,并且将来有可能会增加新的调度算法和支持新的操作系统,请选择恰当的设计模式解决该问题,画出类图,写出关键代码。(不考虑线程调度算法的具体代码实现)
#include<iostream>
using namespace std;
// Implementation(实现类接口) - 调度算法接口
class SchedulingAlgorithm {
public:
virtual void schedule() = 0;
};
// ConcreteImplementation(具体实现类) - 时间片调度算法
class TimeSliceScheduling : public SchedulingAlgorithm {
public:
void schedule() override {
// 实现时间片调度算法
cout << "使用时间片调度算法" << endl;
}
};
// ConcreteImplementation(具体实现类) - 抢占调度算法
class PreemptiveScheduling : public SchedulingAlgorithm {
public:
void schedule() override {
// 实现抢占调度算法
cout << "使用抢占调度算法" << endl;
}
};
// Abstraction(抽象类) - 线程调度器
class ThreadScheduler {
protected:
SchedulingAlgorithm* algorithm;
public:
ThreadScheduler(SchedulingAlgorithm* algo) : algorithm(algo) {}
virtual void schedule() = 0;
virtual void setAlgorithm(SchedulingAlgorithm* algo) {
algorithm = algo;
}
};
// RefinedAbstraction(具体抽象类) - Windows线程调度器
class WindowsThreadScheduler : public ThreadScheduler {
public:
WindowsThreadScheduler(SchedulingAlgorithm* algo) : ThreadScheduler(algo) {}
void schedule() override {
// Windows平台特定的线程调度逻辑
std::cout << "Windows操作系统: ";
algorithm->schedule();
}
};
// RefinedAbstraction(具体抽象类) - Unix线程调度器
class UnixThreadScheduler : public ThreadScheduler {
public:
UnixThreadScheduler(SchedulingAlgorithm* algo) : ThreadScheduler(algo) {}
void schedule() override {
// Unix平台特定的线程调度逻辑
std::cout << "Unix操作系统: ";
algorithm->schedule();
}
};
// RefinedAbstraction(具体抽象类) - Linux线程调度器
class LinuxThreadScheduler : public ThreadScheduler {
public:
LinuxThreadScheduler(SchedulingAlgorithm* algo) : ThreadScheduler(algo) {}
void schedule() override {
// Linux平台特定的线程调度逻辑
std::cout << "Linux操作系统: ";
algorithm->schedule();
}
};
int main() {
// 创建不同的调度算法
SchedulingAlgorithm* timeSlice = new TimeSliceScheduling();
SchedulingAlgorithm* preemptive = new PreemptiveScheduling();
// 创建不同的线程调度器并使用不同的算法
ThreadScheduler* windowsScheduler = new WindowsThreadScheduler(timeSlice);
ThreadScheduler* unixScheduler = new UnixThreadScheduler(preemptive);
ThreadScheduler* linuxScheduler = new LinuxThreadScheduler(timeSlice);
// 执行线程调度
windowsScheduler->schedule();
unixScheduler->schedule();
linuxScheduler->schedule();
// 可以在运行时切换调度算法
windowsScheduler->setAlgorithm(preemptive);
windowsScheduler->schedule();
return 0;
}
(3)组合模式⭐⭐⭐
Component:抽象组件,可以是接口或抽象类,为叶子组件和容器组件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象组件中定义了访问及管理它的子组件的方法,如增加子组件、删除子组件、获取子组件等。
**Leaf:**叶子组件,在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象组件中定义的行为。对于那些访问及管理子组件的方法,可以通过异常等方式进行处理。
**Composite:**容器组件,在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象组件中定义的行为,包括那些访问及管理子组件的方法,在其业务方法中可以递归调用其子节点的业务方法。
**Client:**客户类,针对Component抽象组件类进行编程
小知识点:
心得体会
0、我觉得该模式最重要的几个点、要在**抽象组件中定义出所有的方法,但一般不定义所有属性,**并且方法默认为空实现。2、容器组件对这些方法进行重写。3、叶子节点的play是真正的play实现,而容器节点的play()是先进行一点其自己的操作后,再对其children节点的play()进行遍历实现。
1、组合模式就是为了解决树形结构的问题而诞生。要以统一的方式操作单个对象和由这些对象组成的组合对象的时候。
2、组合模式的关键是定义了一个抽象组件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象组件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。
3、组合模式出镜率不算特别高,但是一旦出境说明这个问题如果不使用它将变得非常困难。
我的例题:
小王为某五星级酒店开发点餐系统。该酒店为满足客户需要,会在不同的时段提供多种不同的餐饮,其菜单的结构图如图所示。
#include<iostream>
#include<vector>
using namespace std;
class Component
{
public:
virtual void display() = 0;
void add(Component*) {}
void remove(Component*) {}
Component* getChild(int i) {};
};
//叶子节点 菜品
class Leaf :public Component
{
string name;
//菜品价格
double price;
public:
Leaf(string name, double price) {
this->name = name;
this->price = price;
}
void display() {
cout << "[" << name << "] " << "价格: " << price << "" << endl;
}
};
class Composite:public Component
{
string name;
vector<Component*> children;
public:
Composite(string tname):name(tname){}
void display() {
cout << "菜单:" << name << endl;
cout << "--------------------------" << endl;
for (int i = 0; i < children.size(); i++)
{
children[i]->display();
}
}
void add(Component*com ) {
children.push_back(com);
}
void remove(Component* com) {
for (int i = 0; i < children.size(); i++)
{
if (children[i] == com)
children.erase(children.begin() + i);
}
}
Component* getChild(int i) {
return children[i];
}
};
//客户端
int main()
{
//创建菜品
//==主食
// 面类
//饼类
Leaf *jiDanbing = new Leaf("鸡蛋饼",2.00);
Leaf *niuRouBing= new Leaf("牛肉饼", 2.00);
Leaf* mianTiao = new Leaf("面条", 5.00);
//饮品==
Leaf*keLe= new Leaf("可乐", 3.00);
Leaf* shui = new Leaf("矿泉水", 1.00);
//创建饮品节点
Composite* yinPin= new Composite("饮品");
yinPin->add(keLe);
yinPin->add(shui);
//创建饼类
Composite* Bin = new Composite("饼类");
Bin->add(jiDanbing);
Bin->add(niuRouBing);
//创建主食类
Composite* zhuShi = new Composite("主食");
zhuShi->add(Bin);
zhuShi->add(mianTiao);
//创建所有菜单
Composite* menu = new Composite("菜单");
menu->add(zhuShi);
menu->add(yinPin);
//展示
menu->display();
return 0;
}
(4)装饰模式⭐⭐
Component:抽象组件,它是具体组件和抽象装饰类的共同父类,声明了在具体组件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
ConcreteComponent:具体组件,它是抽象组件类的子类,用于定义具体的组件对象,实现了在抽象组件中声明的方法,装饰器可以给它增加额外的职责(方法)。
Decorator:抽象装饰类,它也是抽象组件类的子类,用于给具体组件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象组件对象的引用,通过该引用可以调用装饰之前组件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
**ConcreteDecorator:**具体装饰类,它是抽象装饰类的子类,负责向组件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
小知识点:
心得体会
我的理解:迭代。即先买了一个最小的俄罗斯套娃,然后又买了一个大一点的俄罗斯套娃把第一个装进去,。。。最后又买了更大一点的俄罗斯套娃把第二个装进去。。。 后面进行operation的时候,从最外层的开始一直往内operation。 !!!一般在修饰类operation操作中,先调用其引用的operation,再调用修饰类其他独有的操作。
例子
//某个具体修饰者。
public class MilkDecorator extends CoffeeDecorator { public MilkDecorator(ICoffee coffee) { super(coffee); } @Override public void makeCoffee() { super.makeCoffee(); addMilk(); } private void addMilk(){ System.out.print("加奶 "); } }
理解误区!!! 其实在具体装饰者中可以定义独有的功能。此处不再需要在抽象修饰者中定义共有操作。因为是组合关系。
如果考试的时候不理解,请看后面的变形金刚案例。
我的例题:
某饮料店卖饮料时,可以根据顾客的要求在饮料中加入各种配料,饮料店会根据顾客所选的饮料种类和所加入的配料来计算价格。饮料店所供应的部分饮料及配料的种类和价格如下表所示。请你选择恰当的设计模式,为该饮料店开发一个计算饮料价格的程序,要求给出设计思路,画出类图,并写出关键代码。
代码:
//像该例子中一样,每个装饰者在调用其引用的组件时,在添加描述或操作后(或前,根据情况),调用其引用组件的相应方法即可。
#include <iostream>
#include <string>
// 基础饮料类
class Beverage {
public:
virtual std::string description() const = 0;
virtual double cost() const = 0;
};
// 抽象装饰者类
class CondimentDecorator : public Beverage {
protected:
Beverage* beverage;
public:
CondimentDecorator(Beverage* beverage) : beverage(beverage) {}
std::string description() const override {
//描述
return beverage->description();
}
double cost() const override {
return beverage->cost();
}
};
// 具体饮料类
//奶茶
class MilkTea : public Beverage {
public:
std::string description() const override {
return "Milk Tea";
}
double cost() const override {
return 8.0;
}
};
//奶昔
class MilkShake : public Beverage {
public:
std::string description() const override {
return "Milk Shake";
}
double cost() const override {
return 9.0;
}
};
//咖啡
class Coffee : public Beverage {
public:
std::string description() const override {
return "Coffee";
}
double cost() const override {
return 10.0;
}
};
// 具体配料装饰者类
//草莓
class StrawberryDecorator : public CondimentDecorator {
public:
StrawberryDecorator(Beverage* beverage) : CondimentDecorator(beverage) {}
std::string description() const override {
return beverage->description() + ", Strawberry";
}
double cost() const override {
return beverage->cost() + 2.0;
}
};
//布丁
class PuddingDecorator : public CondimentDecorator {
public:
PuddingDecorator(Beverage* beverage) : CondimentDecorator(beverage) {}
std::string description() const override {
return beverage->description() + ", Pudding";
}
double cost() const override {
return beverage->cost() + 3.0;
}
};
//芝士雪酪
class CheeseDecorator : public CondimentDecorator {
public:
CheeseDecorator(Beverage* beverage) : CondimentDecorator(beverage) {}
std::string description() const override {
return beverage->description() + ", Cheese";
}
double cost() const override {
return beverage->cost() + 4.0;
}
};
int main() {
// 创建一个MilkTea饮料
Beverage* beverage = new MilkTea();
std::cout << "Ordered: " << beverage->description() << " Cost: " << beverage->cost() << "元" << std::endl;
// 添加Strawberry配料
beverage = new StrawberryDecorator(beverage);
std::cout << "Ordered: " << beverage->description() << " Cost: " << beverage->cost() << "元" << std::endl;
// 添加Cheese配料
beverage = new CheeseDecorator(beverage);
std::cout << "Ordered: " << beverage->description() << " Cost: " << beverage->cost() << "元" << std::endl;
delete beverage; // 释放内存
return 0;
}
变形金刚案例
package decorator;
//抽象组件
public interface Transform
{
public void move();
}
package decorator;
具体组件
public final class Car implements Transform
{
public Car()
{
System.out.println("变形金刚是一辆车!");
}
public void move()
{
System.out.println("在陆地上移动!");
}
}
抽象装饰者 在抽象装饰者中保留其原有的操作move
package decorator;
public class Changer implements Transform
{
private Transform transform;
public Changer(Transform transform)
{
this.transform=transform;
}
public void move()
{
transform.move();
}
}
//机器人装饰者
package decorator;
public class Robot extends Changer
{
public Robot(Transform transform)
{
super(transform);
System.out.println("变成机器人!");
}
public void say()
{
System.out.println("说话!");
}
}
//飞机装饰者
package decorator;
public class Airplane extends Changer
{
public Airplane(Transform transform)
{
super(transform);
System.out.println("变成飞机!");
}
public void fly()
{
System.out.println("在天空飞翔!");
}
}
//客户端
package decorator;
public class Client
{
public static void main(String args[])
{
Transform camaro;
camaro=new Car();
camaro.move();
System.out.println("-----------------------------");
Airplane bumblebee=new Airplane(camaro);
bumblebee.move();
bumblebee.fly();
}
}
(5)外观模式⭐
**Facade:**外观角色,客户端可以调用它的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应子系统去,传递给相应的子系统对象处理。
SubSystem:子系统角色,在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
小知识点:
我的理解:
1、外观模式避免了client需要不停的与各种类进行交互,将所有操作整合到外观类Facade中。
2、在外观类中可以实现许多业务方法,在方法内调用子系统进行交互。
我的例子:电器总开关
代码 这个模式偷懒 直接看老师的java吧
package facade;
//外观类 里面里面保存四个具体的子系统类的引用(开关)
//可以再外观类中实现其他操作,比如只开一个灯等
public class GeneralSwitchFacade {
private Light lights[]=new Light[4];
private Fan fan;
private AirConditioner ac;
private Television tv;
public GeneralSwitchFacade()
{
lights[0]=new Light("左前");
lights[1]=new Light("右前");
lights[2]=new Light("左后");
lights[3]=new Light("右后");
fan=new Fan();
ac=new AirConditioner();
tv=new Television();
}
public void on()
{
lights[0].on();
lights[1].on();
lights[2].on();
lights[3].on();
fan.on();
ac.on();
tv.on();
}
public void off()
{
lights[0].off();
lights[1].off();
lights[2].off();
lights[3].off();
fan.off();
ac.off();
tv.off();
}
}
package facade;
//灯
public class Light
{
private String position;
public Light(String position)
{
this.position=position;
}
public void on()
{
System.out.println(this.position + "灯打开!");
}
public void off()
{
System.out.println(this.position + "灯关闭!");
}
}
package facade;
//电视机
public class Television
{
public void on()
{
System.out.println("电视机打开!");
}
public void off()
{
System.out.println("电视机关闭!");
}
}
.....
(6)享元模式⭐⭐⭐⭐
Flyweight: 抽象享元类,通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
ConcreteFlyweight: 具体享元类,实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
UnsharedConcreteFlyweight: 非共享具体享元类,并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
FlyweightFactory:享元工厂。
小知识点:
1、在享元模式中,存储这些共享实例对象的地方称为享元池(Flyweight Pool)。我们可以针对每一个不同的字符创建一个享元对象,将其放在享元池中,需要时再从享元池取出。
2、享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State)。 内部状态如字符的内容,外部状态如字符的颜色。
3、外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。
4、享元模式一般结合工厂模式一起使用,在它的结构图中包含了一个享元工厂类,其结构图如上图所示:左边是享元工厂,右边是具体的享元,包括共享具体享元,非共享具体享元。
5、享元模式主要优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份;其缺点是使得系统更加复杂,并且需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
心得体会:
上述类图要记得,有外部状态时,要加一个对外部状态类的依赖。
每个共享的享元只在第一次创建工厂时,在工厂的享元vector(享元池)里面new一个,后面获取时,如果享元在享元池里面,则直接返回已创建的对象,若不在享元池,则new一个。后面的例题中没有,不过我想应该是可以有的。比如当type不一样时。
我的例题:
共享网络设备(有外部状态)
虽然网络设备可以共享,但是分配给每一个终端计算机的端口(Port)是不同的,因此多台计算机虽然可以共享同一个网络设备,但必须使用不同的端口。我们可以将端口从网络设备中抽取出来作为外部状态,需要时再进行设置。
#include<iostream>
#include<vector>
using namespace std;
//外部状态 端口
class Port {
string port;
public:
Port(string port) {
this->port = port;
}
void setPotr(string port) {
this->port = port;
}
string getProt()
{
return port;
}
};
//抽象享元网卡
class NetworkDevice {
public:
virtual string getType() = 0;
virtual void use(Port* port) = 0;
};
//交换机
class Swith:public NetworkDevice
{
private:
string type;
public:
Swith(string type)
{
this->type = type;
}
string getType()
{
return type;
}
void use(Port* port)
{
cout << "当前使用Switch: 类型是:" << this->type << "端口号是:" << port->getProt() << endl;
}
};
class Hub:public NetworkDevice
{
private:
string type;
public:
Hub(string type)
{
this->type = type;
}
string getType()
{
return type;
}
void use(Port* port)
{
cout << "当前使用Hub: 类型是:" << this->type << "端口号是:" << port->getProt() << endl;
}
};
//享元工厂
class NetworkDeviceFactory {
vector<NetworkDevice*> devices;
int TotalTerminal = 0;
public:
NetworkDeviceFactory()
{
//先维护两个享元
Swith* sw1 = new Swith("Cisco - WS - C2950 - 24");
Hub* hub1 = new Hub("TP - LINK - HF8M");
devices.push_back(sw1);
devices.push_back(hub1);
}
NetworkDevice* createNetworkDevice(string type)
{
if (type == "cisco")return devices[0];
else if (type == "tp")return devices[1];
else return nullptr;
}
int getTotalDevices()
{
return devices.size();
}
int getTotalTerminal()
{
return TotalTerminal;
}
};
//客户端
int main()
{
//五个网卡,实际只有两个对象
NetworkDevice *nd1, *nd2, *nd3, *nd4,* nd5;
//创建工厂
NetworkDeviceFactory* factory = new NetworkDeviceFactory();
nd1 = factory->createNetworkDevice("cisco");
nd1->use(new Port("1000"));
nd2 = factory->createNetworkDevice("cisco");
nd2->use(new Port("1001"));
nd3= factory->createNetworkDevice("tp");
nd3->use(new Port("1111"));
return 0;
}
(7)代理模式⭐⭐⭐
**Subject:**抽象主题角色,声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
RealSubject:真实主题角色,定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
**Proxy:**代理主题角色,它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
prerequest();
ealsubject.request(); //调用真实主题对象的方法
postrequest();
小知识点:
1、代理模式分为 远程代理、虚拟代理、保护代理、缓冲代理、只能引用代理。
心得体会
猜想,考试要考的话,场景应该是对类A的操作已经写死,为了拦截一些非法操作,或者在调用A的方法之前进行一些特殊处理,则用一个类B来进行代理,在类B中调用A的方法之前或之后,进行一些其他操作,对类A进行保护。
我的例题:
在一个论坛中已注册用户和游客的权限不同,已注册的用户拥有发帖、修改自己的注册信息、修改自己的帖子等功能;而游客只能看到别人发的帖子,没有其他权限。使用代理模式来设计该权限管理模块。
在本实例中我们使用代理模式中的保护代理,该代理用于控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
代码:
写不动了,依旧使用老师的代码。 在代理类中添加了判断 level(为代理类新增的成员)为1才能调用真实主题角色的方法,为0时做判断,通报没有权限。(view没有限制)。
package proxy;
//抽象主题角色
public interface AbstractPermission
{
public void modifyUserInfo();
public void viewNote();
public void publishNote();
public void modifyNote();
public void setLevel(int level);
}
//真实主题角色
package proxy;
public class RealPermission implements AbstractPermission
{
public void modifyUserInfo()
{
System.out.println("修改用户信息!");
}
public void viewNote()
{
System.out.println("查看帖子!");
}
public void publishNote()
{
System.out.println("发布新帖!");
}
public void modifyNote()
{
System.out.println("修改发帖内容!");
}
public void setLevel(int level)
{
}
}
//代理主题角色
package proxy;
public class PermissionProxy implements AbstractPermission
{
private RealPermission permission=new RealPermission();
private int level=0;
public void modifyUserInfo()
{
if(0==level)
{
System.out.println("对不起,你没有该权限!");
}
else if(1==level)
{
permission.modifyUserInfo();
}
}
public void viewNote()
{
permission.viewNote();
}
public void publishNote()
{
if(0==level)
{
System.out.println("对不起,你没有该权限!");
}
else if(1==level)
{
permission.publishNote();
}
}
public void modifyNote()
{
if(0==level)
{
System.out.println("对不起,你没有该权限!");
}
else if(1==level)
{
permission.modifyNote();
}
}
public void setLevel(int level)
{
this.level=level;
}
}
三、行为型模式
行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。
行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。
类行为型模式:类的行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。
对象行为型模式:对象的行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象关联等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量使用关联关系来取代继承关系,因此大部分行为型设计模式都属于对象行为型设计模式。
(1)职责链模式⭐
Handler:抽象处理者角色,它定义了一个处理请求的接口,一般设计为抽象类。
ConcreteHandler:具体处理者角色,它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者。
小知识点:
心得体会和要注意的点:
1、一般要将处理基类定义为抽象类
2、在客户端创建对象时,创建的类名要为具体类
我自己的例题:
某学校的差旅费报销制度规定,要根据不同的报销金额,由不同的领导审批,1万元以下科长审批,1万元至5万元之间处长审批,5万元至10万元之间副校长审批,10万元以上校长审批。最常用的编程思想是采用条件语句进行判断,但是随着差旅费报销制度的逐渐完善,可能需要判断的条件会越来越多,可能处理的逻辑也更加复杂,代码将变得难以维护。请选择恰当的设计模式解决该问题,画出类图,写出关键代码。
类图:
#include<iostream>
using namespace std;
> **//抽象处理者(审批人)**
> **class Handler_shenPiRen**
> {
> protected:
> Handler_shenPiRen* successor;
> public:
> virtual void handleRequest(double money)=0;
> };
> //具体处理者
> //科长
> **class Handler_keZhang** : public Handler_shenPiRen
> {
> public:
> Handler_keZhang(Handler_shenPiRen* successor)
> {
> this->successor = successor;
> }
> void handleRequest(double money)
> {
> if (money < 10000)
> cout << "科长:不到一万块钱,我审批了。" << endl;
> else
> {
> cout << "科长:大于一万,我没这个权力,转交给处长吧。" << endl;
> successor->handleRequest(money);
> }
> }
> };
> //处长
> **class Handler_chuZhang** : public Handler_shenPiRen
> {
> public:
> Handler_chuZhang(Handler_shenPiRen* successor)
> {
> this->successor = successor;
> }
> void handleRequest(double money)
> {
> if (money < 50000)
> cout << "处长:不到五万块钱,我审批了。" << endl;
> else
> {
> cout << "处长:大于五万,我没这个权力,转交给副校长吧。" << endl;
> successor->handleRequest(money);
> }
> }
> };
> //副校长
> **class Handler_fuXiaoZhang** : public Handler_shenPiRen
> {
> public:
> Handler_fuXiaoZhang(Handler_shenPiRen* successor)
> {
> this->successor = successor;
> }
> void handleRequest(double money)
> {
> if (money < 100000)
> cout << "副校长:不到十万块钱,我审批了。" << endl;
> else
> {
> cout << "副校长:大于十万,我没这个权力,转交给校长吧。" << endl;
> successor->handleRequest(money);
> }
> }
> };
> //校长
> **class Handler_xiaoZhang** : public Handler_shenPiRen
> {
> public:
> Handler_xiaoZhang(Handler_shenPiRen* successor)
> {
> this->successor = successor;
> }
> void handleRequest(double money)
> {
> if (1)
> cout << "校长:不管多少钱,我审批了。" << endl;
> else
> {
> cout << "校长:" << endl;
> successor->handleRequest(money);
> }
> }
> };
> //客户端调用
> int main()
> {
> //先把四个 长定义出来
> Handler_xiaoZhang* xiaoZhang = new Handler_xiaoZhang(nullptr);
> Handler_fuXiaoZhang* fuXiaoZhang = new Handler_fuXiaoZhang(xiaoZhang);
> Handler_chuZhang* chuZhang = new Handler_chuZhang(fuXiaoZhang);
> Handler_keZhang* keZhang = new Handler_keZhang(chuZhang);
>
> //定义想要报销的金额
> double money = 0;
> cout << "输入您要报销的金额:" << endl;
> cin >> money;
> keZhang->handleRequest(money);
>
> return 0;
>
> }
(2)命令模式⭐⭐⭐
可以看到命令模式有4个参与角色
Command (抽象命令)
是一个接口,定义一个命令
**ConcreteCommand **(唱歌、跳舞)
具体的执行命令,他们需要实现Command接口
Receiver (机器人)
真正执行命令的角色,那些具体的命令引用它,让它完成命令的执行
Invoker (智能语音遥控器,里面存储一个命令或者命令vector)
负责按照客户端的指令设置并执行命令,像命令的撤销(undo()、execute()),日志的记录等功能都要在此类中完成
小知识点
心得体会
1、该模式比较难 要点在于引入命令类和确定命令的执行者。
例题(该例题较难,有两个Receiver,不知道我的做法对不对)
小王准备使用面向对象的方法设计一个快餐店的简单游戏,游戏中有顾客、服务员、菜品和厨师。每个顾客都有一个服务员帮助点菜,并且可以点多个菜;每道菜都由指定厨师制作,不同的菜可能由不同的厨师制作;顾客跟服务员点完菜后,服务员通知后厨做菜。请你针对上面的描述,帮助小王选择合适的设计模式进行设计。
类图
#include<iostream>
#include<string>
using namespace std;
//抽象菜类(抽象命令)
class Command_caiMing
{protected:
string name;
public:
string getName() { return name; }
virtual void execute() = 0;
};
//抽象Receiver厨师
class Receiver_chuShi {
public:
virtual void create(Command_caiMing *cai) = 0;
};
//王厨师
class Receiver_wangShiFu :public Receiver_chuShi
{
public:
void create(Command_caiMing *cai)
{
cout << "王师傅做了一道:" << cai->getName() << endl;
}
};
//康师傅
class Receiver_kangShiFu :public Receiver_chuShi
{
public:
void create(Command_caiMing* cai)
{
cout << "王师傅做了一道:" <<cai->getName()<< endl;
}
};
//王师傅腊肉
class wang_laRou :public Command_caiMing
{
private:
Receiver_chuShi *chushi;
public:
wang_laRou(Receiver_chuShi* chushi,string name)
{
this->chushi = chushi;
this->name = name;
}
void execute()
{
chushi->create(this);
}
};
//王师傅土豆丝
class wang_tuDou :public Command_caiMing
{
private:
Receiver_chuShi *chushi;
public:
wang_tuDou(Receiver_chuShi* chushi,string name)
{
this->chushi = chushi;
this->name = name;
}
void execute()
{
chushi->create(this);
}
};
//康师傅面条
class kang_mianTiao :public Command_caiMing
{
private:
Receiver_chuShi* chushi;
public:
kang_mianTiao(Receiver_chuShi* chushi,string name)
{
this->chushi = chushi;
this->name = name;
}
void execute()
{
chushi->create(this);
}
};
//服务员
class Invoker_fuWuYuan
{
private:
Command_caiMing* cai;
public:
Invoker_fuWuYuan()
{
cai = nullptr;
}
void setCai(Command_caiMing *cai)
{
this->cai = cai;
}
void Order()
{
cai->execute();
}
};
//客户端
int main()
{
//先创建两个厨师和菜品
Receiver_wangShiFu* wangShiFu = new Receiver_wangShiFu();
Receiver_kangShiFu* kangShiFu = new Receiver_kangShiFu();
//王师傅负责的菜
wang_laRou* laRou = new wang_laRou(wangShiFu,"腊肉");
wang_tuDou* tuDouSi = new wang_tuDou(wangShiFu, "土豆丝");
//康师傅负责的菜
kang_mianTiao* mianTiao = new kang_mianTiao(kangShiFu, "面条");
//创建一个服务员
Invoker_fuWuYuan* fuwuyuan = new Invoker_fuWuYuan();
//顾客1点腊肉
fuwuyuan->setCai(laRou);
fuwuyuan->Order();
//顾客1点土豆丝
fuwuyuan->setCai(tuDouSi);
fuwuyuan->Order();
//顾客1点面条
fuwuyuan->setCai(mianTiao);
fuwuyuan->Order();
delete wangShiFu;
//其余略
return 0;
}
(4)迭代器模式⭐⭐
**Iterator:**抽象迭代器,定义了访问和遍历元素的接口,声明了相应方法,例如:用于获取第一个元素的first()方法,用于访问下一个元素的next()方法,用于判断是否还有下一个元素的hasNext()方法,用于获取当前元素的currentItem()方法等,在具体迭代器中将实现这些方法。
**ConcreteIterator:**具体迭代器,实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。具体迭代器中维护一个对抽象聚合类的引用。private int cursor; //定义一个游标,用于记录当前访问位置
Aggregate:抽象聚合类,用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。有一个创建迭代器的函数。
ConcreteAggregate:具体聚合类,实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
小知识点:
1、需要注意的是抽象迭代器接口的设计非常重要,一方面需要充分满足各种遍历操作的要求,尽量为各种遍历方法都提供声明,另一方面又不能包含太多方法,接口中方法太多将给子类的实现带来麻烦。因此,可以考虑使用抽象类来设计抽象迭代器,在抽象类中为每一个方法提供一个空的默认实现。如果需要在具体迭代器中为聚合对象增加全新的遍历操作,则必须修改抽象迭代器和具体迭代器的源代码,这将违反“开闭原则”,因此在设计时要考虑全面,避免之后修改接口。
2、迭代器能实现“无需暴露聚合对象的内部实现,就能够访问到聚合对象中各个元素”的功能,同时,由于迭代器模式把聚合对象和访问聚合的机制实现了分离,所以可以在迭代器上实现不同迭代策略,最为典型的就是实现过滤功能的迭代器。因此,综合来看,迭代器模式的本质是“控制访问聚合对象中的元素”,而非单纯的“透明”。
3、迭代器模式是“单一职责原则”的完美体现。
心得体会
1、组合类只需要有一个组合对象vector,和一个创建迭代器的函数。在创建具体迭代器的时候,可能需要强制指针类型转换。
2、迭代器中要维护一个指向抽象类的指针。
我的例题:
电视机遥控器就是一个迭代器的实例,通过它可以实现对电视机频道集合的遍历操作,本实例我们将模拟电视机遥控器的实现。
代码:
#include<iostream>
#include<vector>
using namespace std;
class Television;
//抽象迭代器 遥控器
class TViterator {
protected:
Television* myTel;
int currentIndex = 0;
public:
TViterator(Television* myTel) :myTel(myTel) {};
virtual void setChannel(int i) = 0;
virtual string currentChannel() = 0;
virtual void next() = 0;
virtual void previous() = 0;
virtual bool isLast() = 0;
virtual bool isFirst() = 0;
};
//一个具体的遥控器
class TCLiterator:public TViterator
{
public:
TCLiterator(Television* myTel) :TViterator(myTel) {}
void setChannel(int i)
{
currentIndex = i;
}
string currentChannel();
/*{
return myTel->TVS[currentIndex];
}*/
void next()
{
if (!isLast())currentIndex++;
else cout << "已经是最后一个频道" << endl;
}
void previous(){
if (!isFirst())currentIndex--;
else cout << "已经是第一个频道" << endl;
}
bool isLast();
/*{
if (currentIndex == myTel->TVS.size() - 1)return 1;
else return 0;
}*/
bool isFirst(){
if (currentIndex == 0)return 1;
else return 0;
}
void play()
{
cout << "正在播放频道:" << currentChannel() << endl;
}
};
//抽象聚合类
class Television {
public:
vector<string>TVS;
virtual TViterator* createInterator() = 0;
};
//TCL电视
class TCLTelevision:public Television
{
public:
TCLTelevision() {
TVS = { "少儿频道", "CCTV1", "CCTV2", "CCTV3", "音乐频道" };
}
TViterator* createInterator()
{
TCLiterator* temp=new TCLiterator(this);
return temp;
}
};
string TCLiterator::currentChannel()
{
return myTel->TVS[currentIndex];
//return "ss";
}
bool TCLiterator::isLast() {
if (currentIndex == myTel->TVS.size() - 1)return 1;
else return 0;
}
//客户端
int main()
{
//先定义一个Tcl电视
TCLTelevision* tcl = new TCLTelevision();
TCLiterator *tclIterator=(TCLiterator*)tcl->createInterator();
//正放
bool flag = 1;
while (flag)
{
tclIterator->play();
if (!tclIterator->isLast())
tclIterator->next();
else flag = 0;
}
/// 逆放
flag = 1;
while (flag)
{
tclIterator->play();
if (!tclIterator->isFirst())
tclIterator->previous();
else flag = 0;
}
return 0;
}
(5)中介者模式⭐⭐⭐
Mediator: 抽象中介者,它定义一个接口,该接口用于与各同事对象之间进行通信。
ConcreteMediator: 具体中介者,它是抽象中介者的子类,通过协调各个同事对象来实现协作行为,它维持了对各个同事对象的引用。
Colleague: 抽象同事类,它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。
ConcreteColleague: 具体同事类,它是抽象同事类的子类;每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信;在具体同事类中实现了在抽象同事类中声明的抽象方法。
小的知识点:
模式的核心在于中介者类的引入, 本质:封装交互 它承担两方面职责:
1、中转作用(结构性)。通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。
2、协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。
3、中介者模式和外观模式的区别
外观模式多用来封装一个子系统内部的多个模块,目的是向子系统外部提供简单易用的接口,也就是说外观模式封装的是子系统外部和子系统内部模块间的交互;而中介者模式是提供多个平等的同事对象之间交互关系的封装,一般是用在内部实现上。
外观模式是实现单向的交互,是从子系统外部来调用子系统内部,不会反着来,而中介者模式实现的是内部多个模块间多向的交互。
中介者模式和外观模式的区别:
中介者模式的目的主要是松散多个模块之间的耦合,把这些耦合关系全部放到中介者中去实现;而外观模式的目的是简化客户端的调用,这点和中介者模式也不同。
心得体会:
该模式适用于多个对象之间互相交互,要注意各个colleague提供的函数名要完全一样(操作可以不同),如果出现不同类型的concreteColleague,可以试试给中介者中加入两个(或多个)不同的vecor,分别保存不同类。即多加一个抽象colleague
我的例题:
小明正在开发一个病房呼叫应答仿真系统。系统中每个病房配备一个呼叫按钮和一个呼叫显示应答器,疗区大厅配备一个显示应答器和一个语音播报器。假设,按下001号病房的呼叫按钮后,那么,所有的呼叫显示应答器都会显示发出呼叫的病房号001,大厅同时语音播报该病房号001。当医护人员按下任意一处的呼叫显示应答器的应答按钮后,所有呼叫显示应答器停止显示该房间号,大厅也停止语音播报。请用恰当的设计模式实现该系统,画出类图,给出核心代码。
类图:
代码:
#include<iostream>
#include<vector>
using namespace std;
class Mediator;
//抽象呼叫器(病房)
class colleagueHuJiao {
protected:
int number;
Mediator* mediator;
public:
colleagueHuJiao(Mediator* mediator)
{
this->mediator = mediator;
}
int getNum() { return number; }
virtual void response(colleagueHuJiao*) = 0;
virtual void stop() = 0;
virtual void request() = 0;
};
//具体病房
class concreteColleague_bingFang :public colleagueHuJiao
{
public:
concreteColleague_bingFang(Mediator* mediator,int number) :colleagueHuJiao( mediator)
{
this->number = number;
}
void response(colleagueHuJiao* huJiaoQi)
{
cout << "病房" << number << "显示器:" << "\"病房" << huJiaoQi->getNum() << "正在呼叫\"" << endl;
}
void stop()
{
cout << "病房" << number << "停止显示" << endl;
}
void request();
};
//医院大厅
class concreteColleague_daTing:public colleagueHuJiao
{
public:
concreteColleague_daTing(Mediator* mediator, int number) :colleagueHuJiao(mediator)
{
this->number = number;
}
void response(colleagueHuJiao* huJiaoQi)
{
cout << "大厅显示器:" << "\"病房" << huJiaoQi->getNum() << "正在呼叫\"" << endl;
cout << "大厅语音播报:" << "\"病房" << huJiaoQi->getNum() << "正在呼叫!!!\"" << endl;
}
void stop()
{
cout << "大厅停止显示和播报" << endl;
}
void request();
//不完整的类型mediator 函数在后面定义
//mediator->stop();
};
//抽象中介者类
class Mediator {
protected:
vector< colleagueHuJiao*>colleagueVector;
public:
//添加呼叫器
void addColleague(colleagueHuJiao* hujiao)
{
colleagueVector.push_back(hujiao);
}
//删除呼叫器
void deleteColleague(colleagueHuJiao* hujiao)
{
for (int i = 0; i < colleagueVector.size(); i++)
{
if (colleagueVector[i] == hujiao)
colleagueVector.erase(colleagueVector.begin()+i);
}
}
//呼叫操作
virtual void request(colleagueHuJiao* huJiaoZhe) = 0;
//停止呼叫
virtual void stop() = 0;
};
//具体中介者类
class concreteMediator :public Mediator
{
public:
//呼叫操作
void request(colleagueHuJiao* huJiaoZhe)
{
for (int i = 0; i < colleagueVector.size(); i++)
{
colleagueVector[i]->response(huJiaoZhe);
}
}
//停止呼叫
void stop()
{
for (int i = 0; i < colleagueVector.size(); i++)
{
colleagueVector[i]->stop();
}
}
};
void concreteColleague_bingFang::request()
{
mediator->request(this);
}
void concreteColleague_daTing ::request()
{
mediator->stop();
//大厅没有呼叫功能,按下后调用停止
}
//客户端
int main()
{
//先定义一个中介者
concreteMediator* myMediator = new concreteMediator();
//定义一个大厅显示对象
concreteColleague_daTing* daTing = new concreteColleague_daTing(myMediator,000);
//定义三个病房 001 002 003
concreteColleague_bingFang* bingFang001 = new concreteColleague_bingFang(myMediator, 001);
concreteColleague_bingFang* bingFang002 = new concreteColleague_bingFang(myMediator, 002);
concreteColleague_bingFang* bingFang003 = new concreteColleague_bingFang(myMediator, 003);
//将三个病房和大厅加入中介者
myMediator->addColleague(daTing);
myMediator->addColleague(bingFang001);
myMediator->addColleague(bingFang002);
myMediator->addColleague(bingFang003);
//病房002按下呼叫按钮
bingFang002->request();
cout << "--------------------" << endl;
//大厅按下停止按钮
daTing->request();
cout << "--------------------" << endl;
//病房002按下呼叫按钮
bingFang001->request();
cout << "--------------------" << endl;
//大厅按下停止按钮
daTing->request();
return 0;
}
(6)备忘录模式 ⭐⭐
Originator:原发器,它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。
package dp.memento;
public class Originator {
private String state;
public Originator(){ }
// 创建一个备忘录对象
public Memento createMemento() { return new Memento(this); }
// 根据备忘录对象恢复原发器状态
public void restoreMemento(Memento m) { state = m.state; }
public void setState(String state) { this.state=state; }
public String getState() { return this.state; }
}
Memento:备忘录,存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同。
package dp.memento;
class Memento {
private String state;
public Memento(Originator o) {
state = o.getState();
}
public void setState(String state) {
this.state=state;
}
public String getState() {
return this.state;
}
}
**Caretaker:**负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。
小知识点:
1、在设计备忘录类时需要考虑其封装性,除了Originator类,不允许其他类来调用备忘录类Memento的构造函数与相关方法
2、 原发器与备忘录之间的关系是非常特殊的,它们要分享信息而不让其他类知道
3、在C++中可以使用friend关键字,让原发器类和备忘录类成为友元类,互相之间可以访问对象的一些私有的属性;
4、备忘录模式和原型模式这两个模式可以组合使用。
心得体会:
1、三个类分别为原发类、备忘录、负责人。其中原发类提供函数save()和restore(),注意restore的参数是一个备忘录。
备忘录只需提供一个原始的构造函数(参数是备忘录,一般调用原发类的save())(其余函数好像没用到),负责人保存一个备忘录指针,以及其get()和set().
2、备忘录模式做到了,只有负责人能使用其函数。通过函数私有化,以及创建友元类。而且备忘录在使用过程中一般不是显示创建。
如果一个负责人想保存多个备忘录,换成vector<>,为备忘录添加一个标记属性如序号,在restore时选择备忘录序号。
我的例题:
某系统提供了用户信息操作模块,用户可以修改自己的各项信息。为了使操作过程更加人性化,现使用备忘录模式对系统进行改进,使得用户在进行了错误操作之后可以恢复到操作之前的状态。
#include<iostream>
using namespace std;
//备忘录类
class Memento
{
string account;
string password;
string telNo;
string getAccount() { return account; }
string getPassword() { return password; }
string getTelNo() {return telNo;}
void setAccount(string account) { this->account = account; }
void setPassword(string password) { this->password = password; }
void setTelNo(string telNo) { this->telNo = telNo; }
public:
Memento(string account, string password, string telNo)
{
this->account = account;
this->password = password;
this->telNo = telNo;
}
//friend class Caretaker;
friend class UserInfoDTO;
};
//原发类
class UserInfoDTO {
string account;
string password;
string telNo;
public:
string getAccount() { return account; }
string getPassword() { return password; }
string getTelNo() { return telNo; }
void setAccount(string account) { this->account = account; }
void setPassword(string password) { this->password = password; }
void setTelNo(string telNo) { this->telNo = telNo; }
Memento* saveMemento()
{
return new Memento(account, password, telNo);
}
void restoreMemento(Memento* user)
{
this->account = user->getAccount();
this->password = user->getPassword();
this->telNo = user->getTelNo();
}
void show() {
cout << "账户:" << this->account << endl;
cout << "密码: " << this->password << endl;
cout << "电话: " << this->telNo << endl;
}
};
//负责人
class Caretaker {
Memento* memento;
public:
Caretaker(){ memento=NULL; }
Caretaker(Memento* m) {
memento = m;
}
Memento* getMemento() {
return memento;
}
void setMemento(Memento * m) {
memento = m;
}
};
//客户端
int main()
{
//创建一个用户
UserInfoDTO* user = new UserInfoDTO();
//创建一个负责人
Caretaker* caretaker = new Caretaker();
user->setAccount("111111");
user->setPassword("aaa123456");
user->setTelNo("12412312");
user->show();
cout << "===============================" << endl;
caretaker->setMemento(user->saveMemento());
user->setAccount("111111");
user->setPassword("333333333");
user->setTelNo("098766");
user->show();
cout << "===============================" << endl;
user->restoreMemento(caretaker->getMemento());
user->show();
return 0;
}
(7)观察者模式⭐⭐
Subject:目标又称为主题,它是指被观察的对象。
**ConcreteSubject:**具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。如果无须扩展目标类,则具体目标类可以省略。
**Observer:**观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。
ConcreteObserver:具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者中定义的update()方法。通常在实现时,可以调用具体目标类的attach()方法将自己添加到目标类的集合中或通过detach()方法将自己从目标类的集合中删除。
小的知识点:
1、观察者模式适用于当一个对象的状态发生改变的时候,如何让依赖于它的所有对象得到通知
2、观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。
3、在有些更加复杂的情况下,具体观察者类的update()方法在执行时需要使用到具体目标类中的状态属性,因此在具体观察者类与具体目标类之间有时候还存在关联或依赖关系,在具体观察者类中定义一个具体目标类实例,通过该实例获取存储在具体目标类中的状态。
4、模式优点:01支持广播通信,由于目标发送通知给观察者是面向所有注册的观察者,所以每次目标通知的信息就要对所有注册的观察者进行广播。当然,也可以通过在目标上添加新的功能来限制广播的范围。
02观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。
5、模式缺点:由于观察者模式每次都是广播通信,不管观察者需不需要,每个观察者都会被调用update方法,如果观察者不需要执行相应处理,要及时解注册。若两个对象互为观察者和目标对象,要注意避免相互广播造成死循环、有可能导致系统崩溃的问题。
心得体会
改模式只有两个层面的类,观察者和目标类。比较简单,在写观察者的update()方法时注意是否需要传一个目标类进来,可能需要读一些目标类的信息。(如我的例题)
我的例题
小王正在为某公司设计开发一套物业租赁管理系统,该公司有多种类型的物业,如公寓、商铺等,并且在将来可能会增加新的物业类型,如别墅、车库等;公司的经纪每租出一个物业,主管就会收到相应的租赁信息。请你针对上面的描述,帮助小王选择合适的设计模式进行设计。
类图
代码:
#include<iostream>
#include<vector>
using namespace std;
class Subject_wuYe;
//抽象主管(接口)
class observer_zhuGuan
{
public:
virtual void update(Subject_wuYe* ) = 0;
};
//具体主管
class concrete_zhuGuan :public observer_zhuGuan
{
public:
//类外实现
void update(Subject_wuYe* subj);
};
//抽象物业
class Subject_wuYe
{
protected:
string name;
vector<observer_zhuGuan*> observers;
public:
Subject_wuYe(string name) { this->name = name; }
string getName() { return name; }
void addObserver(observer_zhuGuan* obs)
{
observers.push_back(obs);
}
void deleteObserver(observer_zhuGuan* obs)
{
for (int i = 0; i < observers.size(); i++)
{
if (observers[i] == obs)
observers.erase(observers.begin() + i);
}
}
void notify() {
for (int i = 0; i < observers.size(); i++)
{
observers[i]->update(this);
}
}
virtual void zuLin() = 0;
};
//具体物业类
//公寓
class apartment:public Subject_wuYe
{
public:
apartment(string name):Subject_wuYe(name)
{
}
void zuLin()
{
cout << "租赁了一座公寓" << endl;
notify();
}
};
//商铺
class shop :public Subject_wuYe
{
public:
shop(string name) :Subject_wuYe(name)
{
}
void zuLin()
{
cout << "租赁了一座商铺" << endl;
notify();
}
};
//别墅
class bieShu :public Subject_wuYe
{
public:
bieShu(string name) :Subject_wuYe(name)
{
}
void zuLin()
{
cout << "租赁了一座别墅" << endl;
notify();
}
};
void concrete_zhuGuan::update(Subject_wuYe* subj)
{
cout << "主管收到一条" << subj->getName() << "的租赁信息" << endl;
}
//客户端
int main()
{
//创建一个主管
concrete_zhuGuan* liZhuGuan = new concrete_zhuGuan();
//创建三个物业对象
apartment* apart01 = new apartment("公寓1");
shop* shop888 = new shop("888号商铺");
bieShu* bieShu22 = new bieShu("别墅22");
//为三个物业分别添加观察者主管
apart01->addObserver(liZhuGuan);
shop888->addObserver(liZhuGuan);
bieShu22->addObserver(liZhuGuan);
//有人租赁别墅22
bieShu22->zuLin();
//有人租赁公寓1
apart01->zuLin();
//有人租赁888号商铺
shop888->zuLin();
//delete 略
}
例题2
当前开发一个新语言、新中间件或程序框架时,基于充分利用多核CPU能力等方面的考虑,多采用事件循环处理模型+回调函数的设计方式,使用该模型的示例代码和预期输出详见下表。针对该模型,请写出你所选择的设计模式,画出类图,并给出Trigger类及相关类的设计和实现,使得输出能够符合预期结果。(为简便起见,假设所有事件处理函数的原型均为void xxxx(void);)
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <functional>
// 声明观察者接口
class Observer {
public:
virtual void handleEvent() = 0;
};
// Trigger 类,主题类
class Trigger {
private:
std::unordered_map<std::string, std::vector<Observer*>> eventHandlers;
public:
// 注册事件和回调函数
void on(const std::string& eventName, Observer* observer) {
if (observer == NULL)eventHandlers[eventName].clear();
else
eventHandlers[eventName].push_back(observer);
}
// 触发事件
void raiseEvent(const std::string& eventName) {
if (eventHandlers.find(eventName) != eventHandlers.end()) {
for (Observer* observer : eventHandlers[eventName]) {
std::cout << "Process " << eventName << " start" << std::endl;
observer->handleEvent();
std::cout << "Process " << eventName << " end" << std::endl;
}
}
}
};
// 实现具体观察者类
class EventObserver : public Observer {
private:
std::function<void()> callback;
public:
EventObserver(std::function<void()> func) : callback(func) {}
void handleEvent() override {
callback();
}
};
void func01()
{
std::cout << "func01()" << std::endl;
}
int main() {
Trigger trigger;
// 注册事件和回调函数
trigger.on("event01", new EventObserver(func01));
trigger.on("event01", new EventObserver([]() {
std::cout << "func02()" << std::endl;
}));
trigger.on("event02", new EventObserver([]() {
std::cout << "func02()" << std::endl;
}));
trigger.on("event03", new EventObserver([]() {
std::cout << "func03()" << std::endl;
}));
// 触发事件
trigger.raiseEvent("event01");
trigger.raiseEvent("event02");
trigger.raiseEvent("event03");
trigger.on("event01",nullptr);
trigger.raiseEvent("event01");
trigger.raiseEvent("event02");
return 0;
}
(8)状态模式⭐⭐⭐
Context:环境类,又称为上下文类,它是拥有多种状态的对象。
State:抽象状态类,它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。
**ConcreteState:**具体状态类,它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。
小的知识点
1、State与Context之间可能也存在依赖或者关联关系。
2、状态转换的方式:(1) 统一由环境类来负责状态之间的转换,此时,它充当了状态管理器(State Manager)角色。
public void changeState() {
if (value == 0) { //判断属性值,根据属性值进行状态转换
this.setState(new ConcreteStateA());
}
else if (value == 1) {
this.setState(new ConcreteStateB());
}
}
(2) 由具体状态类来负责状态之间的转换
public void changeState(Context ctx) {
if (ctx.getValue() == 1) { //根据环境对象中的属性值进行状态转换
ctx.setState(new ConcreteStateB());
}
else if (ctx.getValue() == 2) {
ctx.setState(new ConcreteStateC());
}
}
心得体会:
状态模式,将一些(共同的可分离也可不分离)环境类的方法提取出来,构成状态类,每个具体状态类可以对这些方法进行不同的实现。相同的实现可以放在抽象状态类中实现。
必要时,状态类同时关联一个环境类 如控温案例。
我的例题:
小明为某银行开发一个报警系统,该系统功能描述如下,请你帮助小明完成设计,给出设计模式的名称,画出类图,写出关键代码。
我的代码
//此代码的状态转换或许有一点问题,可以定义一个状态转换函数在环境类或者抽象状态类中,根据环境对象的某个value值进行状态转换
#include<iostream>
using namespace std;
//抽象状态 时间
class TimeState
{
public:
virtual void useJinKu() = 0;
void useJinLin()
{
cout << "使用警铃向报警中心发送紧急事态通知!!!" << endl;
}
virtual void usePhone() = 0;
};
//白天
class Day :public TimeState
{
public:
void useJinKu()
{
cout << "白天使用金库,已在报警中心留下记录" << endl;
}
void usePhone()
{
cout << "白天使用电话,正在呼叫报警中心..." << endl;
}
};
//晚上
class Night :public TimeState
{
public:
void useJinKu()
{
cout << "晚上使用金库,已向报警中心发送紧急事态通知!!!" << endl;
}
void usePhone()
{
cout << "晚上使用电话,正在呼叫报警中心的留言电话..." << endl;
}
};
class JinKu
{
private:
TimeState* state;
public:
JinKu(TimeState* state) {
this->state = state;
}
void setState(TimeState* state)
{
this->state = state;
}
void useJinKu()
{
state->useJinKu();
}
void useJingLin()
{
state->useJinLin();
}
void usePhone()
{
state->usePhone();
}
};
//客户端
int main()
{
//创建两个状态
Day* day = new Day();
Night* night = new Night();
//创建一个金库
JinKu* jinKu = new JinKu(day);
jinKu->useJinKu();
jinKu->useJingLin();
jinKu->usePhone();
//到了晚上
jinKu->setState(night);
jinKu->useJinKu();
jinKu->useJingLin();
jinKu->usePhone();
return 0;
}
(9)策略模式⭐⭐
Context:环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
Strategy:抽象策略类为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
ConcreteStrategy:具体策略类实现了在抽象策略类中声明的算法,运行时具体策略对象将覆盖环境类中定义的抽象策略对象,使用一种具体的算法实现某个业务处理。
小知识点:
1、具体的策略可能被多个环境类使用,提高了重用性。
2、策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,一定程度上增加了客户端的使用难度。
3、策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。
4、缺点:客户端必须知道每个策略类,任何细小的变化都要重新生成一个策略类。
5、小明在使用策略模式进行设计时,发现策略所对应的多个算法在实现上有很多公共功能,请你给出建议帮助小明能更好地实现复用?小明再进一步设计时,又发现这些算法的实现步骤都是一样的,只是在某些局部步骤的实现上有所不同,那么请你再帮帮小明,如何能更好地实现复用?
1、如果策略对应的多个算法有很多公共功能,可尝试将该算法分解成小的子算法,将公共的子算法的实现放在抽象策略接口中实现,在具体策略类中只需调用这些子算法就行,增强了代码的重用性,更加方便简洁。
2、当算法的实现步骤是一样的,只是某些局部的实现上有所不同,考虑使用模板方法,将基本方法写在父类中,子类在继承时将不一样的步骤进行覆盖重写。
心得体会:
策略模式应该还算简单,如果题目中出现多个算法的嵌套如折上折,则不再适合策略模式,考虑使用修饰模式。
我的例题:
小王正在开发一套电影院售票系统,在该系统中需要为不同类型的用户提供不同的电影票打折方式,具体打折方案如下:(1) 学生凭学生证可享受票价7折优惠;(2) 年龄在10周岁及以下的儿童可享受每张票减免15元的优惠(原始票价需大于等于30元);(3) 影院会员卡用户除享受票价半价优惠外还可进行积分,积分累计到一定额度可换取电影院赠送的奖品。该系统在将来可能还要根据需要引入新的打折方式。请你针对上面的描述,帮助小王进行设计,给出设计思路及所采用的设计模式,画出类图,并写出关键代码。
类图:
代码:
#include<iostream>
using namespace std;
//打折方法接口
class discount
{
public:
virtual double discounter(double fee) = 0;
};
//具体策略
class Student_discount:public discount
{
double discounter(double fee)
{
return fee * 0.7;
}
};
class Children_discount :public discount
{
double discounter(double fee)
{
if (fee >= 30)
return fee - 15;
else return fee;
}
};
class Vip_discount :public discount
{
double discounter(double fee)
{
return fee/2;
cout << "您本次购票为您积分fee分" << endl;
}
};
//购票系统 (环境类)
class TickeSell {
discount* dc;
double fee;
public:
TickeSell(double fee, discount* dc)
{
this->fee = fee;
this->dc = dc;
}
void setFee(double fee)
{
this->fee = fee;
}
void setDiscount(discount* dc)
{
this->dc = dc;
}
void buyTicket()
{
double temp = dc->discounter(fee);
cout << "原票价为" << fee << "元,您的折后需支付价格为:" << temp << "元。" << endl;
}
};
//客户端类
int main()
{
//先创建几个折扣
Student_discount* student_discount = new Student_discount();
Children_discount* children_discount = new Children_discount();
Vip_discount* vip_discount = new Vip_discount();
//不妨置票价为40元
//首先创建一个学生买票
TickeSell* ticketSell = new TickeSell(40, student_discount);
ticketSell->buyTicket();
//儿童买票,票价为35
ticketSell->setFee(35);
ticketSell->setDiscount(children_discount);
ticketSell->buyTicket();
//vip买票,票价为50
ticketSell->setFee(50);
ticketSell->setDiscount(vip_discount);
ticketSell->buyTicket();
return 0;
}
(10)模板方法模式
该模式较简单,使用一个泛化,将需要改变的方法在子类中重写覆盖。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)