设计模式(四)

行为模型设计模式

策略模式:

定义了算法家族分别封装起来,让他们之间可以相互替换,此模式使算法的变化不会影响到使用算法的客户。(将每一个算法策略封装到接口中,根据需要设定的策略,使具体实现和策略解耦)

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法都是完成相同的工作只是实现不同,他们可以以相同的方式调用所有方法,减少各类算法类之间的耦合。

策略模式的优缺点:优点是定义算法族使用继承可以把公共的代码移到父类中,提高复用性;算法的变化和客户端分离,提高可扩展性。缺点:可能导致类过多,每个具体策略类都会产生一个新类,客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

刘备取西川,庞军师献出三种上中下三种策略供刘备选择,当然目的就一个攻取西川。

#include <iostream>
#include <memory>
using namespace std;
class seizeSiChuan
{
public:virtual ~seizeSiChuan() = default;virtual string attackSiChuan(const string &msg) const = 0;
};
// 上计
class BestPolicy : public seizeSiChuan
{
public:explicit BestPolicy(const string &str): PolicyInfo(str){}string attackSiChuan(const string &msg) const override{cout << msg << "庞统军师的上计 " << PolicyInfo << " 刘备觉上计未免太急" << endl;return "NO";}private:string PolicyInfo;
};
// 中计
class MiddlePolicy : public seizeSiChuan
{
public:explicit MiddlePolicy(const string &str): PolicyInfo(str){}string attackSiChuan(const string &msg) const override{cout << msg << "庞统军师的中计 " << PolicyInfo << "刘备觉中计不快不慢,正得其宜,可以行之" << endl;return "OK";}private:string PolicyInfo;
};// 下计
class LowerPolicy : public seizeSiChuan
{
public:explicit LowerPolicy(const string &str): PolicyInfo(str){}string attackSiChuan(const string &msg) const override{cout << msg << "庞统军师的下计 " << PolicyInfo << "刘备觉下计又觉太缓" << endl;return "NO";}private:string PolicyInfo;
};
// 考虑选择
class makeContext
{
public:explicit makeContext(seizeSiChuan *m_contest): mcontest(m_contest) {}~makeContext(){if (mcontest != nullptr){delete mcontest;mcontest = nullptr;}}string attackSiChuan(const string &msg) const{if (mcontest != nullptr){return mcontest->attackSiChuan(msg);}else{return "NO";}}private:seizeSiChuan *mcontest = nullptr;
};
// 你是刘备决定选取哪种策略
int main()
{while (true){unique_ptr<makeContext> context(nullptr);char s;cin >> s;if (s == 'B'){ // 上计context.reset(new makeContext(new BestPolicy("上计:点兵出其不意直接攻打 ")));}else if (s == 'M'){ // 中策context.reset(new makeContext(new MiddlePolicy("中计:使用类似于假途灭虢之计 ")));}else if (s == 'L'){ /// 下策context.reset(new makeContext(new LowerPolicy("下计:罢兵回荆州后徐图缓取之 ")));}if (context != nullptr){if (context->attackSiChuan("夺取西川选择了") == "OK"){cout << "英雄所见略同,就这么定了" << endl;break;}else{cout << "英雄所见非同,可再行商议" << endl;}}else{cout << "只有上中下三种策略";}}
}

 策略可以可以自由切换,避免使用多重条件判断。但是策略类增多并且所有策略类都需要对外暴露。

模板模式:

定义了一个操作中定义一个算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法结构即可以重新定义算法某些特定步骤。

模板方法模式是通过把不变的行为搬到超类里,去除子类重复代码体现其优势,模板方法体现的就是一个很好的复用平台。当不变和可变的行为在方法的子类实现中混合在一起的时候不变的行为就会在子类中重复出现,通过模板方法模式把这些行为移到单一的地方这样就帮助子类摆脱重复的不变行为的纠缠。

使用场景:需要实现算法不变的部分,将可变的部分留给子类;各个子类中的公共行为提取出来并集中到一个公共父类避免代码重用;通过子类来决定父类算法中的某个步骤是否执行,实现子类对父类的反向控制。

比如:一道《鲜虾仁》不同厨师有不同的做法。总体且将其分为厨师处理+上菜+请品尝,公共部分可以放在抽象类里,至于厨师如何处理就看各自的方式。

#include <iostream>
#include <memory>class chef
{
public:virtual ~chef() = default;chef(const std::string &str) :chefName(str){}void TemplateMethod(){std::cout << chefName <<std::endl;step1();step2();step3();}
protected:virtual void step1()const = 0;virtual void step2()const = 0;virtual void step3(){std::cout<<"请品尝"<<std::endl;}
private:std::string chefName;
};class chefA : public chef
{public:// 构造函数传递咖啡名称chefA() : chef("广东黄师傅:") {}protected:virtual void step1()const override{std::cout << "<鲜虾仁>步骤: 生腌食材---";}virtual void step2()const override{std::cout << "摆碗上菜--- ";}};class chefB : public chef
{public:chefB() : chef("上海刘师傅:") {}
protected:virtual void step1()const override{std::cout << "<鲜虾仁>步骤: 蒸煮+蘸料---";}virtual void step2()const override{std::cout << "装盘上菜--- ";}
};int main()
{std::unique_ptr<chef> cf = std::make_unique<chefA>();cf->TemplateMethod();cf.reset(new chefB());cf->TemplateMethod();return 0;
}

模板模式获得一种反向控制结构效果,体现了依赖倒置原则,就是父类调用子类的操作(高层调用低层的操作)低层模块实现高层模块声明的接口。这样控制权在父类,低层模块反而要依赖高层模块。但是因为模板模式采用继承这种强制约束关系,导致复用性不高。比如果改做汤TemplateMethod里面的步骤就不合适了,我们就无法再次使用这个模板了,这样就导致了不能复用子类的实现步骤。

观察者模式:

定义了一种一对多的依赖关系让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象并自动更新。

特点:观察者模式关键对象是主题subject和观察者observer,一个主题可以有任意数目的依赖它的observer,当subject发生变化时所有observer都可以得到通知,subject发出通知时并不需要知道它的观察者,具体观察者是谁它根本不知道,而任何一个具体观察者并不知道也不需要知道其他观察者的存在。观察者所作的就是解除耦合,让耦合的双方都依赖于抽象不依赖于具体,从而使得各自的变化不会影响另一边的变化。

使用场景:当一个对象的改变需要同时改变其他对象且不知道有多少对象有待改变时,考虑使用观察者模式。

比如微信公众号:‌微信公众号是主题对象‌,它负责发布内容并通知订阅者;‌微信用户是观察者‌,他们订阅公众号以接收更新。当‌公众号的内容更新时‌,所有订阅的用户都会收到通知,这体现了观察者模式的“当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并进行更新”的原则。此外,观察者模式还允许订阅者根据自己的需要选择是否接收通知,这与微信公众号中用户可以取消订阅的功能相吻合。这种灵活性是观察者模式的一个重要特点,它使得系统可以根据环境的变化动态地调整其行为‌。

不足:如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知会造成资源浪费;如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

#include <iostream>
#include <vector>
#include <algorithm>// 观察者接口
class Observer
{
public:virtual void update(std::string &msg) = 0;virtual ~Observer() = default;
};// 主题接口
class Subject
{
public:virtual void registerObserver(Observer *observer) = 0;virtual void removeObserver(Observer *observer) = 0;virtual void notifyObservers() = 0;virtual ~Subject() = default;
};// 具体主题实现
class takeAction : public Subject
{
private:std::vector<Observer *> observers;std::string msg;public:takeAction() : msg("") {}void registerObserver(Observer *observer) override{observers.push_back(observer);}void removeObserver(Observer *observer) override{auto it = std::find(observers.begin(), observers.end(), observer);if (it != observers.end()){observers.erase(it);}}void notifyObservers() override{for (Observer *observer : observers){observer->update(msg);}}// 添加获取观察者的函数const std::vector<Observer *> &getObservers() const{return observers;}void KongMingSignal(char c){if (c == 'O'){msg = "收到火起信号,进攻";notifyObservers();}else{msg = "收到火熄信号,偃旗息鼓";notifyObservers();}}
};// 具体观察者实现
class ObserverGeneral : public Observer
{
private:std::string name;public:ObserverGeneral(const std::string &name) : name(name) {}void update(std::string &msg) override{std::cout << name << " " << msg << std::endl;}
};int main()
{takeAction takeAction;takeAction.registerObserver(new ObserverGeneral("关羽"));takeAction.registerObserver(new ObserverGeneral("张飞"));Observer* ObserverGeneralA = new ObserverGeneral("赵云");takeAction.registerObserver(ObserverGeneralA);char updates;std::cin >> updates;takeAction.KongMingSignal(updates);takeAction.removeObserver(ObserverGeneralA);takeAction.KongMingSignal(updates);// 释放动态分配的观察者对象for (Observer *observer : takeAction.getObservers()){delete observer;}return 0;
}

迭代器模式:

提供一种方法顺序访问一个聚合对象中各个元素,而不暴露该对象的内部的结构。

使用场景:当需要访问一个聚合对象而且不管这些对象是什么都需要遍历的时候,就应该选择使用迭代器模式。

#include <iostream>
#include <vector>// 迭代器接口
class Iterator {
public:virtual int next() = 0;virtual bool hasNext() = 0;
};// 具体迭代器
class ConcreteIterator : public Iterator {
private:std::vector<int> data;int position;public:ConcreteIterator(std::vector<int> vec) : data(vec), position(0) {}int next() override {return data[position++];}bool hasNext() override {return position < data.size();}
};// 聚合对象接口
class Aggregate {
public:virtual Iterator* createIterator() = 0;
};// 具体聚合对象
class ConcreteAggregate : public Aggregate {
private:std::vector<int> data;public:void addData(int value) {data.push_back(value);}Iterator* createIterator() override {return new ConcreteIterator(data);}
};int main() {ConcreteAggregate aggregate;aggregate.addData(1);aggregate.addData(2);aggregate.addData(3);Iterator* iterator = aggregate.createIterator();while (iterator->hasNext()) {std::cout << iterator->next() << " ";}delete iterator;return 0;
}

 实例主要包括:

  1. 迭代器(Iterator):定义了访问和遍历元素的接口。
  2. 具体迭代器(Concrete Iterator):实现迭代器接口,对集合进行具体的迭代操作。
  3. 聚合对象(Aggregate):定义创建相应迭代器对象的接口。
  4. 具体聚合对象(Concrete Aggregate):实现聚合对象接口,返回一个具体迭代器的实例。

责任链模式:

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这个对象连成一条链,将请求沿着这条链传递,直到有对象处理它为止。

优点:解耦:请求发起者和接收者都没有明确信息,且链中的对象自己并不知道链的结构,职责链可以简化对象的相互连接,仅需要保持一个指向其后继的引用,不需要保持它所有的候选者的引用。灵活性增强:可以动态改变处理请求的顺序和组合,

不足:由于请求需要依次通过链中的所有对象,因此在处理请求时可能会影响性能,特别是在链中有大量节点时。调试困难:当请求在链中传递时,可能会变得难以跟踪和调试,特别是在复杂的链条中。如果责任链的末端没有对象能够处理请求,那么请求可能会一直传递到链的末端,导致请求无法被处理。

#include <iostream>
#include <memory>
class Handler
{
public:virtual ~Handler() = default;virtual void setNextHandler(Handler *nextHandler) = 0;virtual void HandlerRequest(int request) = 0;
};class ConcreteHandler1 : public Handler
{
public:void HandlerRequest(int request) override{if (request < 10){std::cout << "ConcreteHandler1 handle the number " << request << " request" << std::endl;}else if (nextHandler != nullptr){nextHandler->HandlerRequest(request);}}void setNextHandler(Handler *nextHandler) override{this->nextHandler = nextHandler;}private:Handler *nextHandler;
};
class ConcreteHandler2 : public Handler
{
public:void HandlerRequest(int request) override{if (request >= 10 && request < 20){std::cout << "ConcreteHandler2 handle the number " << request << " request" << std::endl;}else if (nextHandler != nullptr){nextHandler->HandlerRequest(request);}}void setNextHandler(Handler *nextHandler) override{this->nextHandler = nextHandler;}private:Handler *nextHandler;
};int main()
{ConcreteHandler1 handler1;ConcreteHandler2 handler2;handler1.setNextHandler(&handler2);handler1.HandlerRequest(5);handler1.HandlerRequest(15);handler1.HandlerRequest(25);return 0;
}

如果请求的值为5,ConcreteHandler1将处理请求;如果请求的值为15,ConcreteHandler2将处理请求。如果没有处理者能够处理请求,请求将被忽略。

命令模式:

将一个请求封装成一个对象,从而可以使用不同的请求,从而可以用不同的请求对客户进行参数化,对请求排队或者日志请求记录、以及支持可撤销的操作。

优势:命令模式将请求发送者和接收者解耦,只需知道如何发送命令不关心如何执行命令,降低耦合度。可扩展性:容易地添加新的命令,而无需修改现有的代码,符合开闭原则。支持撤销和重做:通过保存命令的历史记录,可以实现撤销和重做操作,提高系统的灵活性。支持队列请求:可以将命令对象存储在队列中,实现请求的排队和延迟执行。日志记录:可以记录命令对象,实现日志记录和回放功能。

缺点:可能会导致系统中产生过多的命令对象,影响系统的性能;使用命令模式可能会使系统变得更加复杂。

#include <iostream>
#include <memory>
// 命令接收者
class Receiver
{
public:void action(){std::cout << "执行命令" << std::endl;}void undoAction(){std::cout << "拒不执行" << std::endl;}
};
// 命令基类
class Command
{
public:virtual void execute() = 0;virtual void undo() = 0;
};
// 具体命令类
class ConcreteCommand1 : public Command
{
public:
private:Receiver *receiver;public:ConcreteCommand1(Receiver *receiver) : receiver(receiver) {}void execute(){receiver->action();}void undo(){receiver->undoAction();}
};// 调用者类
class Invoker
{
private:Command *command;
public:void setCommand(Command *cmd){command = cmd;}void executeCommand(){command->execute();}void undoCommand(){command->undo();}
};int main()
{Receiver *receiver = new Receiver(); //士兵收到信进攻或抗命Command *command = new ConcreteCommand1(receiver); //传令兵传信Invoker *invoker = new Invoker(); //将军发号施令invoker->setCommand(command);invoker->executeCommand();invoker->undoCommand();delete receiver;delete command;delete invoker;return 0;
}

备忘录模式:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到保存的状态。

优点:‌状态保存与恢复‌:当前状态的保存与恢复这对于撤销操作或程序崩溃后的恢复非常有用。‌封装性和隔离性‌:通过备忘录模式,对象的状态被封装在备忘录对象中,不会暴露给其他对象。这保护了对象的封装性和隔离性,使得对象的状态变化对其他对象透明。

缺点:‌内存占用‌:由于每个备忘录对象需要保存一份完整的状态,当对象的状态较多或状态改变频繁时,可能会引起较大的内存占用。性能开销‌:备忘录模式的创建、存储和恢复状态的操作可能会引起一定的性能开销,特别是在状态较大或对象较复杂的情况下。

何时使用:文本编辑器‌:用于保存撤销和重做操作之前的文本状态,实现撤销和恢复功能。‌游戏存档‌;‌事务管理‌:在数据库事务管理中,备忘录模式可以用于保存事务执行之前的状态,以便在需要回滚事务时可以恢复到之前的状态。‌安全监管‌:在安全监管中,备忘录模式可以用于记录系统的运行状态。

#include <iostream>
// 负责存储 Originator 对象的状态
class Memento
{
public:Memento() = default;Memento(int state) : value(state) {}int GetState() const{return value;}private:int value;
};
// 负责创建和管理备忘录
class Originator
{
public:void InitState(){value = 100;}void show() const{std::cout << "State " << value << std::endl;}void ChangeState(){value = 0;}void RecoveryState(const Memento &me){value = me.GetState();}Memento SaveState() const{return Memento(value);}private:int value = 0;
};
// 负责保存备忘录
class Caretaker
{
public:void SaveMemento(const Memento &me){saveMe = me;}Memento GetMemento() const{return saveMe;}
private:Memento saveMe;
};int main()
{Originator org;org.InitState();org.show();Caretaker ct;ct.SaveMemento(org.SaveState());org.ChangeState();org.show();org.RecoveryState(ct.GetMemento());org.show();return 0;
}

状态模式:

当一个对象的内在状态改变时允许改变其行为,对象看起来似乎改变了它的类。

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。

优点易于扩展: 状态模式使得新状态的添加和现有状态的修改变得简单,只需添加新的状态类或修改现有状态类即可,减少了代码的耦合性易于扩展。简化条件判断促进代码组织: 状态模式可以有效地消除大量的条件分支语句封装在独立的状态类中,使代码更加清晰和易于理解和有助于代码的组织和维护。

缺点:增加类的数量: 引入了多个状态类和上下文类,可能会增加程序中的类的数量和状态过于细分,使得代码结构更加复杂。可能导致性能损失: 在状态切换频繁且状态类较多的情况下,可能会引起性能损失,需要谨慎设计状态转换逻辑。

#include <iostream>
#include <string>
using namespace std;
// 状态接口
class State
{
public:virtual void handle() = 0; // 处理状态的方法
};// 具体状态类
class OnState : public State
{
public:void handle() override{cout << "Light is ON" << endl;}
};class OffState : public State
{
public:void handle() override{cout << "Light is OFF" << endl;}
};class BlinkState : public State
{
public:void handle() override{cout << "Light is Blinking" << endl;}
};// 上下文类
class Light
{
private:State *state; // 当前状态
public:Light() : state(new OffState()) {} // 初始状态为关闭void setState(State *newState){delete state;state = newState;}void performOperation(){ // 执行当前状态的操作state->handle();}~Light(){delete state;}
};int main()
{Light light;light.performOperation();light.performOperation();light.setState(new OffState());light.performOperation();light.setState(new BlinkState());light.performOperation();return 0;
}

访问者模式:

表示一个作用于某对象结构中的各个元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

访问者模式用于在不改变已有类层次结构的前提下定义新的操作。它将操作封装到独立的访问者类中,避免了在对象类中添加新操作带来的修改。访问者模式适用于当一个对象结构包含多个类,并且你希望对这些对象应用不同的操作而又不希望修改这些类的情况。通过定义不同的访问方法,可以让访问者在不同对象上执行不同的操作,从而实现灵活的算法变化。访问者模式需要在对象结构中添加接受访问者的方法,以便访问者能够访问对象的数据。

#include <iostream>
#include <memory>
#include <vector>class ConcreteElementA;
class ConcreteElementB;
// 访问者基类
class Vistor
{
public:virtual ~Vistor() = default;virtual void VisitConcreteElementA(ConcreteElementA *ConcreteElementA) const = 0;virtual void VisitConcreteElementB(ConcreteElementB *concreteElementB) const = 0;
};
// 元素类基类
class Element
{
public:virtual ~Element() = default;virtual void Accept(const std::shared_ptr<Vistor> &vistor) = 0;
};
// 具体元素类
class ConcreteElementA : public Element
{
public:virtual void Accept(const std::shared_ptr<Vistor> &vistor) override{vistor->VisitConcreteElementA(this);}
};class ConcreteElementB : public Element
{
public:virtual void Accept(const std::shared_ptr<Vistor> &vistor) override{vistor->VisitConcreteElementB(this);}
};
// 具体的Visitor类
class ConcreteVistorA : public Vistor
{
public:virtual void VisitConcreteElementA(ConcreteElementA *ConcreteElementA) const override{std::cout << "ConcreteElementA ConcreteVistorA" << std::endl;}virtual void VisitConcreteElementB(ConcreteElementB *concreteElementB) const override{std::cout << "ConcreteElementB ConcreteVistorA" << std::endl;}
};class ConcreteVistorB : public Vistor
{
public:virtual void VisitConcreteElementA(ConcreteElementA *ConcreteElementA) const override{std::cout << "ConcreteElement-A ConcreteVistorB" << std::endl;}virtual void VisitConcreteElementB(ConcreteElementB *concreteElementB) const override{std::cout << "ConcreteElement-B ConcreteVistorB" << std::endl;}
};class ObjectStructure
{
public:void Attach(std::shared_ptr<Element> element){m_elements.push_back(element);}void detach(std::shared_ptr<Element> elment){auto it = m_elements.begin();while (it != m_elements.end()){if (*it == elment){it = m_elements.erase(it);}else{++it;}}}void accept(std::shared_ptr<Vistor> vistor){for (auto element : m_elements){element->Accept(vistor);}}private:std::vector<std::shared_ptr<Element>> m_elements;
};int main()
{ObjectStructure os;os.Attach(std::make_shared<ConcreteElementA>());os.Attach(std::make_shared<ConcreteElementB>());std::shared_ptr<ConcreteVistorA> va = std::make_shared<ConcreteVistorA>();std::shared_ptr<ConcreteVistorB> vb = std::make_shared<ConcreteVistorB>();os.accept(va);os.accept(vb);return 0;
}

中介者模式:

中介者模式又称为调停者模式,用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

优点:简化了对象之间的交互,将各同事解耦,减少子类生成,可以简化各同事类的设计和实现。缺点: 在具体中介者类中包含了同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。

#include<iostream>
using namespace std;
class Mediator;
//抽象国家  
class Country    
{  
protected:    Mediator *pmediator; //中介  
public:  virtual void SetMediator(Mediator *mediator){} //设置中介  virtual void SendMessage(string message) {}    //向中介发信息  virtual void GetMessage(string message) {}     //从中介获取信息  
};  //抽象中介者  
class Mediator    
{  
public:    virtual void Send(string message, Country *person) {}    virtual void SetAmerica(Country *America) {}  //让中介认识具体的对象  virtual void SetChina(Country *China) {}    
};  //美国  
class America: public Country    
{    
public:    void SetMediator(Mediator *mediator) { pmediator = mediator; }    void SendMessage(string message) { pmediator->Send(message, this); }    void GetMessage(string message) { cout<<"美国收到中国的信息: "<<message; }    
};  //中国  
class China: public Country    
{    
public:    void SetMediator(Mediator *mediator) { pmediator = mediator; }    void SendMessage(string message) { pmediator->Send(message, this); }    void GetMessage(string message) { cout<<"中国收到美国信息: "<<message; }    
};  //联合国  
class UN : public Mediator    
{    
private:    Country *USA; //美国  Country *CNA; //中国  
public:    UN(): USA(NULL), CNA(NULL) {}    void SetAmerica(Country *America) { USA = America; }    void SetChina(Country *China) { CNA = China; }    void Send(string message, Country *country)     {    if(country == USA) //美国给中国发信息  CNA->GetMessage(message); //中国收到信息  else    USA->GetMessage(message);    }  
};  //测试代码  
int main()    
{       Mediator *mediator = new UN();  Country *pAmerica = new America();  Country *pChina = new China();   mediator->SetAmerica(pAmerica);    mediator->SetChina(pChina);    pAmerica->SetMediator(mediator);    pChina->SetMediator(mediator);    pAmerica->SendMessage("中国威胁论\n");    pChina->SendMessage("真正威胁世界安全的是美国\n");    delete pAmerica;   delete pChina;   delete mediator;    return 0;    
}  

解释器模式:

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

优点灵活性高: 解释器模式可以灵活地扩展和改变语言的语法规则,而无需修改原有代码。易于实现语法解析: 解释器模式将语法规则表示为一个抽象语法树,便于对表达式进行分析和解释。易于扩展新的表达式: 可以通过继承或组合的方式轻松添加新的表达式,扩展语言的功能。

缺点:性能开销: 解释器模式通常会对表达式进行解释和分析,可能会产生较大的性能开销,特别是处理复杂的语言规则时。复杂度高: 随着语法规则的复杂度增加,解释器模式的实现可能会变得复杂,维护成本也会随之提高,因此不适合复杂的语法

#include <iostream>
#include <string>
using namespace std;
class Context
{public:void set_data(int data){this->data = data;}void set_ret(int ret){this->ret = ret;}int get_data(){return this->data;}int get_ret(){return this->ret;}private:int data;int ret;
};class Expression
{protected:Context *context;public:virtual void interpret(Context *context) = 0;
};
//后置++解释器
class PlusExpression : public Expression
{virtual void interpret(Context *context){int temp = context->get_data();temp++;context->set_ret(temp);}
};int main()
{Expression *exp = nullptr;Context *data = nullptr;exp = new PlusExpression;data = new Context;data->set_data(0);cout << "原始数据:" << data->get_data() << endl;exp->interpret(data);data->get_ret();cout << "++解释后:" << data->get_ret() << endl;delete data;delete exp;return 0;
}

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

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

相关文章

腾讯云Linux服务器运维,安装JDK、rabbitmq、nginx、Redis、ClickHouse

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; CODING 代码托管目录 1、…

elasticsearch的docker方式安装及golang1.22版本使用elasticsearch7的示例代码

1.准备linux服务器,ip地址为192.168.137.116 2.安装docker-ce yum -y install docker-ce 配置docker镜像 vim /etc/docker/daemon.json{"registry-mirrors": ["https://harbor:443", "https://985ecq8w.mirror.aliyuncs.com","https://…

c# checkbox的text文字放到右边

checkbox的text文字放到右边 实现方法如下图 特此记录 anlog 2024年9月2日

RKNPU2从入门到实践 ---- 【8】借助 RKNN Toolkit lite2 在RK3588开发板上部署RKNN模型

前言 作者使用的平台为Ubuntu20.04虚拟系统&#xff0c;开发板为瑞芯微RK3588&#xff0c;开发板上的系统为Ubuntu22.04系统。 一、任务 完成RKNN模型的部署&#xff0c;RKNN模型的部署是将RKNN模型放到开发板上&#xff0c;应用程序可以加载RKNN模型&#xff0c;从而在嵌入式…

《JavaEE进阶》----5.<SpringMVC②剩余基本操作(CookieSessionHeader响应)>

Cookie和Session简介。 Spring MVC的 2.请求 Cookie的设置和两种获取方式 Session的设置和三种获取方式。 3.响应 1.返回静态页面 2.返回数据 3.返回HTML片段 4.返回JSON 5.设置状态码 6.设置header 三、&#xff08;接上文&#xff09;SpringMVC剩余基本操作 3.2postman请求 …

两大电商巨头强强联手,实力宠卖家,一键通9国市场!

独家深度剖析&#xff0c;Lazada与Daraz分别作为东南亚与南亚电商领域的璀璨明星&#xff0c;正携手演绎一场前所未有的商业盛宴。这两大电商巨擘的强强联合&#xff0c;不仅标志着电商版图的一次重大扩张&#xff0c;更是为全球商家开启了一扇通往东南亚与南亚九大市场广阔蓝海…

【MySQL数据库管理问答题】第14章 使用 MySQL InnoDB 集群实现高可用性

目录 1. 结合“体系结构”&#xff0c;请说明你对 InnoDB 集群的整体认知。 2. 请对组复制的原理和功能做一个完整的描述&#xff0c;并说明组复制有哪些先决条件和限制。 3. MySQL Shell (mysqlsh)和 MySQL Router (mysqlrouter) 各自提供了什么样的集群管理功能&#xff1…

Uniapp 调用aar、jar包

废话 坑是真的多&#xff0c;官方文档简陋到可以忽略不计。 大概流程 1. 新建一个Android模块&#xff0c;需要用这个模块打包成aar 2. 用这个模块引用uniapp-v8-release.aar以及你需要用到的aar、jar&#xff0c;用不到则忽略这步 坑一&#xff1a;不要直接放到这个模块的…

详解JavaScript

目录 JavaScript 引入样式 基础语法 变量 数据类型 运算符 JavaScript对象 数组 数组定义 数组操作 函数 语法格式 关于参数个数 函数表达式 对象 JQuery 语法 选择器 事件 常见的事件 操作元素 获取/设置元素内容 获取/设置元素属性 获取/设置CSS属性 …

StringTable

10.1. String的基本特性 String&#xff1a;字符串&#xff0c;使用一对""引起来表示String声明为final的&#xff0c;不可被继承String实现了Serializable接口&#xff1a;表示字符串是支持序列化的。String实现了Comparable接口&#xff1a;表示string可以比较大小…

JMeter 工具安装以及简单使用

一、安装以及汉化 傻瓜式JMeter下载和环境配置及永久汉化-CSDN博客https://blog.csdn.net/weixin_45608163/article/details/136528719 二、发送GET请求 配置请求头: 配置该线程组的请求: 放在线程组统计,下面请求则共享配置

[图解]SysML和EA建模住宅安全系统-活动作为块

1 00:00:00,210 --> 00:00:04,360 下一个步骤是识别潜在的失效 2 00:00:06,850 --> 00:00:11,150 这里它是用一个块定义图来表达的 3 00:00:12,150 --> 00:00:16,790 图17.21&#xff0c;失效模式识别和因果依赖 4 00:00:19,110 --> 00:00:22,400 但是这个块定义…

Java基于微信小程序的美食推荐小程序,附源码

博主介绍&#xff1a;✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不…

通过Docker部署Nacos,以及Docker Desktop进行管理

目录 一.不需要持久化存储 1.启动容器 2.查看容器和镜像​ 3.容器管理 二.持久化存储启动mysql容器 1.创建docker卷 2.运行容器,指定卷 3.在nacos里面随便建个配置文件 4.停止并删除nacos容器 5.重新运行容器,并且挂载相同的卷,也就是上面第二步的命令 6.打开nacos并…

Tortoise-ORM FastAPI integration 中文文档(完整版)

Tortoise-ORM FastAPI integration 中文文档(完整版) 前言 初衷:在学习的时候发现 Tortoise-ORM FastAPI integration 官方文档缺中文版,翻阅英文文档效率低,萌生翻译想法。 本系列旨在原汁原味的翻译 Tortoise-ORM FastAPI integration 官方文档,帮助英语不好的小伙伴快速…

UE5游戏——显示打击怪物的伤害值显示

要在Unreal Engine 5中实现显示打击怪物时的伤害数值&#xff0c;你可以按照以下步骤操作&#xff1a; 1. 创建UI元素: 在UE5的内容浏览器中创建一个新的User Widget蓝图&#xff08;或者直接从项目设置的默认UI蓝图开始&#xff09;。在这个蓝图中添加一个Text Block组件用于…

相似图像、相似商品检索的流程具体是什么样的?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 1. 数据收集和预处理&#xff1a; 首先&#xff0c;我们需要构建一个包含丰富图像或商品信息的数据库。针对每个图像或商品&#xff0c;我们需确保以下几点&#xff1a; 高质量的图像数据。相关的元数…

fiddler安装和汉化和抓https的包

下载和汉化 官网下载 https://www.telerik.com/ 1、「安装英文版Fiddler」、假如将Fiddler安装在&#xff1a;「D:\Programs\Fiddler」 2、将压缩包「fiddler菜单汉化」文件夹中的「FiddlerTexts.txt」复制到「D:\Programs\Fiddler\」 3、将压缩包「fiddler菜单汉化」文件夹…

外部排序之文件归并

概述 外部排序&#xff08;External Sorting&#xff09;是一种用于处理无法完全加载到内存中的大量数据的排序技术。由于内存的限制&#xff0c;传统的内存排序算法&#xff08;如快速排序、归并排序&#xff09;可能无法处理超大规模的数据集合。因此&#xff0c;需要采用外…

JVM面试(一)什么是虚拟机?什么是class文件?

什么是java虚拟机&#xff1f; 如果通俗点来讲&#xff0c;我们在电脑上一行行敲出来的代码&#xff0c;电脑本身是不认识的&#xff0c;最终是要转成电脑可以运行的101001这种字节。 但是这些我们又不可能手动来转换&#xff0c;所以呢&#xff0c;就需要一个工具&#xff0…