乌镇镇住房建设局网站,网站栏目建设调研,wordpress初始设置,用凡科可以做视频网站吗C设计模式#xff08;李建忠#xff09;
本文是学习笔记#xff0c;如有侵权#xff0c;请联系删除。
参考链接
Youtube: C设计模式
Gtihub源码与PPT#xff1a;https://github.com/ZachL1/Bilibili-plus
豆瓣: 设计模式–可复用面向对象软件的基础 文章目录 C设计模…C设计模式李建忠
本文是学习笔记如有侵权请联系删除。
参考链接
Youtube: C设计模式
Gtihub源码与PPThttps://github.com/ZachL1/Bilibili-plus
豆瓣: 设计模式–可复用面向对象软件的基础 文章目录 C设计模式李建忠14 外观模式FacadeMotivationFacade模式定义Facade结构 15 代理模式ProxyMotivationProxy模式定义Proxy结构 16 适配器AdapterMotivation适配器模式定义适配器模式结构 17 中介者MediatorMotivationMediator定义Mediator结构代码示例Mediator和Facade的区别 18 状态模式StateMotivation状态模式定义状态模式结构代码举例 备忘录模式MementoMotivationMomento定义Momento结构 后记 “接口隔离”模式
在组件构建过程中某些接口之间直接的依赖常常会带来很多问题、甚至根本无法实现。采用添加一层间接稳定接口来隔离本来互相紧密关联的接口是一种常见的解决方案。
典型模式
·Façade
·Proxy
·Adapter
·Mediator
14 外观模式Facade
Motivation
下图中左边方案的问题在于组件的客户和组件中各种复杂的子系统有了过多的耦合随着外部客户程序和各子系统的演化这种过多的耦合面临很多变化的挑战。
如何简化外部客户程序和系统间的交互接口如何将外部客户程 序的演化和内部子系统的变化之间的依赖相互解耦 将一个系统划分成为若干个子系统有利于降低系统的复杂性。一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小。达到该目标的途径之一是就是引入一个 外观facade对象它为子系统中较一般的设施提供了一个单一而简单的界面。
例如有一个编程环境它允许应用程序访问它的编译子系统。这个编译子系统包含了若干个类如 Scanner、Parser、ProgramNode、BytecodeStream 和 ProgramNodeBuilder用于实现这一编译器。有些特殊应用程序需要直接访问这些类但是大多数编译器的用户并不关心语法分析和代码生成这样的细节他们只是希望编译一些代码。对这些用户编译子系统中那些功能强大但层次较低的接口只会使他们的任务复杂化。
为了提供一个高层的接口并且对客户屏蔽这些类编译子系统还包括一个 Compiler 类。这个类定义了一个编译器功能的统一接口。 Compiler 类是一个外观它给用户提供了一个单一而简单的编译子系统接口。它无需完全隐藏实现编译功能的那些类即可将它们结合在一起。编译器的外观可方便大多数程序员使用同时对少数懂得如何使用底层功能的人它并不隐藏这些功能如下图所示。 Facade模式定义
为子系统中的一组接口提供一个一致的界面 Facade模式定义了一个高层接口这个接口使得这一子系统更加容易使用。
Facade结构 参与者 • Facade (Compiler) — 知道哪些子系统类负责处理请求。 — 将客户的请求代理给适当的子系统对象。 • Subsystem classes (Scanner、Parser、ProgramNode 等) — 实现子系统的功能。 — 处理由 Facade 对象指派的任务。 — 没有 facade 的任何相关信息即没有指向 facade 的指针。
协作 • 客户程序通过发送请求给 Facade 的方式与子系统通讯 Facade 将这些消息转发给适当的子系统对象。尽管是子系统中的有关对象在做实际工作但 Facade 模式本身也必须将它的接口转换成子系统的接口。 • 使用 Facade 的客户程序不需要直接访问子系统对象。
要点总结
从客户程序的角度来看Façade模式简化了整个组件系统的接口 对于组件内部与外部客户程序来说达到了一种“解耦”的效 果——内部子系统的任何变化不会影响到Façade接口的变化。
Façade设计模式更注重从架构的层次去看整个系统而不是单个类的层次。Façade很多时候更是一种架构设计模式。
Façade设计模式并非一个集装箱可以任意地放进任何多个对象。Facade模式中组件的内部应该是“相互耦合关系比较大的一系列 组件”而不是一个简单的功能集合。
15 代理模式Proxy
Motivation
在面向对象系统中有些对象由于某种原因比如对象创建的开销很大或者某些操作需要安全控制或者需要进程外的访问等直接访问会给使用者、或者系统结构带来很多麻烦。
如何在不失去透明操作对象的同时来管理/控制这些对象特有的复 杂性增加一层间接层是软件开发中常见的解决方式。
代码示例
class ISubject{
public:virtual void process();
};//Proxy的设计
class SubjectProxy: public ISubject{public:virtual void process(){//对RealSubject的一种间接访问//....}
};class ClientApp{ISubject* subject;public:ClientApp(){subjectnew SubjectProxy();}void DoTask(){//...subject-process();//....}
};Proxy模式定义
为其他对象提供一种代理以控制对这个对象的访问。
Proxy结构 参与者 • Proxy — 保存一个引用使得代理可以访问实体。若 RealSubject 和 Subject 的接口相同 Proxy 会引用 Subject。 — 提供一个与 Subject 的接口相同的接口这样代理就可以用来替代实体。 — 控制对实体的存取并可能负责创建和删除它。
• Subject — 定义 RealSubject 和 Proxy 的共用接口这样就在任何使用 RealSubject 的地方都可以使用Proxy。
• RealSubject — 定义 Proxy 所代表的实体。
chatGPT给出的proxy例子
以下是一个简单的 C 代理模式的例子其中实现了一个图片加载的代理。
#include iostream
#include string// Subject 接口
class Image {
public:virtual void display() const 0;
};// RealSubject 类
class RealImage : public Image {
private:std::string filename;public:RealImage(const std::string filename) : filename(filename) {loadImage();}void loadImage() {std::cout Loading image: filename std::endl;}void display() const override {std::cout Displaying image: filename std::endl;}
};// Proxy 类
class ImageProxy : public Image {
private:RealImage* realImage;std::string filename;public:ImageProxy(const std::string filename) : realImage(nullptr), filename(filename) {}void display() const override {if (realImage nullptr) {realImage new RealImage(filename);}realImage-display();}
};// Client 代码
int main() {// 使用代理加载图片实际图片加载在需要显示时进行Image* image new ImageProxy(sample.jpg);// 第一次显示图片会触发加载image-display();// 第二次显示图片直接使用已加载的图片image-display();return 0;
}在这个例子中Image 是代理模式的 Subject 接口RealImage 是 RealSubject 类负责实际加载和显示图片。ImageProxy 是代理类用于延迟加载和控制对 RealImage 对象的访问。客户端通过代理类使用图片代理类在需要时创建或使用实际图片对象。
分布式系统中代理模式的使用
在分布式系统中代理模式被广泛应用以解决各种问题。以下是一些在分布式系统中常见的代理模式应用 远程代理Remote Proxy 远程代理用于在不同的地址空间中代表对象。在分布式系统中对象可能存在于不同的节点或服务器上远程代理允许客户端通过代理访问远程对象就像访问本地对象一样。 虚拟代理Virtual Proxy 虚拟代理用于延迟对象的创建和初始化特别是在对象很大或需要耗费大量资源的情况下。在分布式系统中虚拟代理可以用于延迟加载远程对象避免一开始就加载所有的远程资源。 安全代理Protection Proxy 安全代理用于控制对对象的访问包括对某些操作的权限验证。在分布式系统中安全代理可以用于对远程服务的访问进行身份验证和权限控制。 缓存代理Caching Proxy 缓存代理用于缓存一些开销较大的操作的结果以提高性能。在分布式系统中可以使用缓存代理来缓存远程服务的响应减少不必要的网络开销。 负载均衡代理Load Balancing Proxy 负载均衡代理用于分发请求到多个服务器以平衡系统的负载。在分布式系统中负载均衡代理可以用于在多个节点之间分发请求提高系统的整体性能和可扩展性。 日志记录代理Logging Proxy 日志记录代理用于记录对象的操作以便进行调试、分析或审计。在分布式系统中日志记录代理可以用于记录远程服务的请求和响应帮助排查问题和监控系统状态。
这些代理模式的应用有助于提高分布式系统的灵活性、安全性、性能和可维护性。代理模式在分布式系统中的使用可以根据具体的需求和架构进行灵活选择和组合。
要点总结
“增加一层间接层”是软件系统中对许多复杂问题的一种常见解 决方法。在面向对象系统中直接使用某些对象会带来很多问题 作为间接层的proxy对象便是解决这一问题的常用手段。
具体proxy设计模式的实现方法、实现粒度都相差很大有些可能对单个对象做细粒度的控制如copy-on-write技术有些可能对组件模块提供抽象代理层在架构层次对对象做proxy。
Proxy并不一定要求保持接口完整的一致性只要能够实现间接控制有时候损及一些透明性是可以接受的。
16 适配器Adapter
Motivation
在软件系统中由于应用环境的变化常常需要将“一些现存的对象”放在新的环境中应用但是新环境要求的接口是这些现存对象所不满足的。
如何应对这种“迁移的变化”如何既能利用现有对象的良好实现同时又能满足新的应用环境所要求的接口
代码示例
//目标接口新接口
class ITarget{
public:virtual void process()0;
};//遗留接口老接口
class IAdaptee{
public:virtual void foo(int data)0;virtual int bar()0;
};//遗留类型
class OldClass: public IAdaptee{//....
};//对象适配器
class Adapter: public ITarget{ //继承
protected:IAdaptee* pAdaptee;//组合public:Adapter(IAdaptee* pAdaptee){this-pAdapteepAdaptee;}virtual void process(){int datapAdaptee-bar();pAdaptee-foo(data); }
};//类适配器
class Adapter: public ITarget,protected OldClass{ //多继承}int main(){IAdaptee* pAdapteenew OldClass();ITarget* pTargetnew Adapter(pAdaptee);pTarget-process();
}class stack{deque container;};class queue{deque container;};
适配器模式定义
将一个类的接口转换成客户希望的另外一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适配器模式结构 参与者 • Target — 定义 Client 使用的与特定领域相关的接口。
• Client — 与符合 Target 接口的对象协同。
• Adaptee — 定义一个已经存在的接口这个接口需要适配。以前的、遗留的接口。
• Adapter — 对 Adaptee 的接口与 Target 接口进行适配。
协作 • Client 在 Adapter 实例上调用一些操作。接着适配器调用 Adaptee 的操作实现这个请求。
要点总结
Adapter模式主要应用于“希望复用一些现存的类但是接口又与复用环境要求不一致的情况”在遗留代码复用、类库迁移等方面非常有用。
GoF23定义了两种Adapter模式的实现结构对象适配器和类适配器。但类适配器采用多继承”的实现方式一般不推荐使用。对象适配器采用”对象组合”的方式更符合松耦合精神。
Adapter模式可以实现的非常灵活不必拘泥于Gof23中定义的两种结构。例如完全可以将Adapter模式中的“现存对象”作为新的接口方法参数来达到适配的目的。
chatGPT给出的adapter例子
下面是一个简单的C Adapter 模式的例子假设有一个旧的类 OldClass其接口不符合新的需求然后通过适配器 Adapter 将其适配为符合新需求的接口
#include iostream// 旧的类接口不符合新需求
class OldClass {
public:void legacyMethod() {std::cout Legacy method of OldClass std::endl;}
};// 新的接口符合新需求
class NewInterface {
public:virtual void newMethod() 0;virtual ~NewInterface() {}
};// 适配器将OldClass适配为NewInterface
class Adapter : public NewInterface {
private:OldClass oldInstance;public:void newMethod() override {// 在这里调用OldClass的方法以适应新的接口oldInstance.legacyMethod();}
};// 客户端代码使用新的接口
void clientCode(NewInterface* newObject) {newObject-newMethod();
}int main() {// 使用适配器将OldClass适配为NewInterfaceAdapter adapter;// 客户端代码使用新的接口clientCode(adapter);return 0;
}在这个例子中OldClass 是一个旧的类具有 legacyMethod 方法但其接口不符合 NewInterface 的新需求。通过创建一个适配器类 Adapter它继承了 NewInterface 并持有一个 OldClass 的实例将 legacyMethod 适配为 newMethod。最后在客户端代码中我们可以使用 NewInterface 的接口而实际上调用了 OldClass 的方法。这就是 Adapter 模式的作用。
17 中介者Mediator
Motivation
在软件构建过程中经常会出现多个对象互相关联交互的情况对象之间常常会维持一种复杂的引用关系如果遇到一些需求的更改这种直接的引用关系将面临不断的变化。
在这种情况下我们可使用一个“中介对象”来管理对象间的关联关系避免相互交互的对象之间的紧耦合引用关系从而更好地抵御变化。
Mediator定义
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用从而使其耦合松散而且可以独立地改变它们之间的交互。
Mediator结构 参与者 • Mediator (中介者) — 中介者定义一个接口用于与各同事Colleague对象通信。
• Concrete Mediator (具体中介者) — 具体中介者通过协调各同事对象实现协作行为。 — 了解并维护它的各个同事。
• Colleague class (同事类)
— 每一个同事类都知道它的中介者对象。
— 每一个同事对象在需与其他的同事通信的时候与它的中介者通信。
一个可能的对象结构 代码示例
中介者模式是一种行为型设计模式其主要目的是减少对象之间的直接通信而是通过一个中介者对象进行协调。这可以降低对象之间的耦合度使系统更易于维护和扩展。
在中介者模式中有以下几个主要参与者 Mediator (中介者): 定义一个接口用于与各同事Colleague对象通信。中介者负责协调各同事之间的交互通过中介者进行通信而不是直接相互引用。 Concrete Mediator (具体中介者): 实现中介者接口通过协调各同事对象来实现协作行为。具体中介者了解并维护它的各个同事。 Colleague class (同事类): 每一个同事类都知道它的中介者对象而不知道其他同事的存在。每一个同事对象在需要与其他同事通信的时候通过它的中介者来实现通信。
中介者模式的典型应用场景是当一个系统中对象之间的交互复杂且对象之间相互依赖性较高时。通过引入中介者可以将系统从多对多的关系简化为多对一的关系降低了系统的复杂性。
以下是一个简单的中介者模式的示例假设有一个聊天室系统
#include iostream
#include stringclass Mediator;// 同事类
class Colleague {
protected:Mediator* mediator;public:Colleague(Mediator* mediator) : mediator(mediator) {}virtual void send(const std::string message) 0;virtual void receive(const std::string message) 0;
};// 具体同事类
class ConcreteColleague : public Colleague {
public:ConcreteColleague(Mediator* mediator) : Colleague(mediator) {}void send(const std::string message) override {std::cout Sending message: message std::endl;mediator-mediate(this, message);}void receive(const std::string message) override {std::cout Received message: message std::endl;}
};// 中介者接口
class Mediator {
public:virtual void mediate(Colleague* sender, const std::string message) 0;
};// 具体中介者
class ConcreteMediator : public Mediator {
private:Colleague* colleague1;Colleague* colleague2;public:void setColleague1(Colleague* colleague) {colleague1 colleague;}void setColleague2(Colleague* colleague) {colleague2 colleague;}void mediate(Colleague* sender, const std::string message) override {if (sender colleague1) {colleague2-receive(message);} else if (sender colleague2) {colleague1-receive(message);}}
};int main() {ConcreteMediator mediator;ConcreteColleague colleague1(mediator);ConcreteColleague colleague2(mediator);mediator.setColleague1(colleague1);mediator.setColleague2(colleague2);colleague1.send(Hello from Colleague 1);colleague2.send(Hi from Colleague 2);return 0;
}在这个例子中Colleague 表示同事类的抽象ConcreteColleague 是具体的同事类Mediator 是中介者的抽象而 ConcreteMediator 是具体的中介者。同事对象通过中介者来进行通信。
要点总结
将多个对象间复杂的关联关系解耦Mediator模式将多个对象间的控制逻辑进行集中管理变“多个对象互相关联”为“多个对象和一个中介者关联”简化了系统的维护抵御了可能的变化。
随着控制逻辑的复杂化Mediator具体对象的实现可能相当复杂。这时候可以对Mediator对象进行分解处理。
Façade模式是解耦系统间单向的对象关联关系Mediator模式是解耦系统内各个对象之间双向的关联关系。
Mediator和Facade的区别
中介者模式和外观模式Facade 模式是两种不同的设计模式它们解决了不同的问题并在实现上有一些区别。 中介者模式Mediator Pattern 目的 中介者模式的主要目的是通过减少对象之间的直接通信来解耦对象之间的关系。中介者模式通过引入一个中介者对象将对象之间的通信集中到中介者对象从而降低对象之间的耦合度。实现 中介者模式通常包括中介者接口和具体中介者类同时每个对象都持有中介者的引用。对象通过中介者进行通信而不直接与其他对象通信。例子 聊天室是一个经典的中介者模式的例子其中聊天室充当中介者聊天室成员通过聊天室进行消息的发送和接收。 外观模式Facade Pattern 目的 外观模式的主要目的是提供一个简化的接口用于访问系统的复杂子系统。外观模式通过引入一个外观类将客户端与子系统的复杂性隔离使客户端只需与外观类进行交互而不需要直接了解子系统的细节。实现 外观模式包括一个外观类该类封装了子系统的接口并为客户端提供一个简化的接口。客户端只需通过外观类来与系统交互而不需要了解系统内部的复杂结构。例子 电脑启动过程中的 BIOS、操作系统加载、应用程序启动等过程可以使用外观模式来简化客户端与这些复杂子系统的交互。
区别总结
目的不同 中介者模式的主要目的是解耦对象之间的关系降低耦合度外观模式的主要目的是提供一个简化的接口隔离客户端与子系统的复杂性。涉及对象数量 中介者模式通常涉及多个对象之间的通信而外观模式通常涉及对多个子系统的封装。关注点 中介者模式关注对象之间的通信和解耦而外观模式关注对复杂系统的简化接口。
在实际应用中选择使用中介者模式还是外观模式取决于问题的性质和需求。
“状态变化”模式
在组件构建过程中某些对象的状态经常面临变化如何对这些变化进行有效的管理同时又维持高层模块的稳定 “状态变化” 模式为这一问题提供了一种解决方案。
典型模式
·State
·Memento
18 状态模式State
Motivation
在软件构建过程中某些对象的状态如果改变其行为也会随之而发生变化比如文档处于只读状态其支持的行为和读写状态支持的行为就可能完全不同。
如何在运行时根据对象的状态来透明地更改对象的行为而不会为对象操作和状态转化之间引入紧耦合
考虑一个表示网络连接的类TCPConnection。一个TCPConnection对象的状态处于若干不同状态之一:连接已建立Established、正在监听(Listening)、连接已关闭(Closed)。当一个TCPConnection对象收到其他对象的请求时它根据自身的当前状态作出不同的反应。例如一个Open请求的结果依赖于该连接是处于连接已关闭状态还是连接已建立状态。State模式描述了TCPConnection如何在每一种状态下表现出不同的行为。
这一模式的关键思想是引入了一个称为TCPState的抽象类来表示网络的连接状态。TCPState类为各表示不同的操作状态的子类声明了一个公共接口。TCPState的子类实现与特定状态相关的行为。例如TCPEstablished和TCPClosed类分别实现了特定于TCPConnection的连接已建立状态和连接已关闭状态的行为。
TCPConnection类维护一个表示TCP连接当前状态的状态对象一个TCPState子类的实例。TCPConnection类将所有与状态相关的请求委托给这个状态对象。TCPConnection使用它的TCPState子类实例来执行特定于连接状态的操作。
一旦连接状态改变TCPConnection对象就会改变它所使用的状态对象。例如当连接从已建立状态转为已关闭状态时TCPConnection会用一个TCPClosed的实例来代替原来的TCPEstablished的实例。 状态模式定义
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
状态模式结构 参与者 • Context (环境如 TCPConnection) — 定义客户感兴趣的接口。 — 维护一个ConcreteState子类的实例这个实例定义当前状态。
• State (状态如 TCPState) — 定义一个接口以封装与Context 的一个特定状态相关的行为。
• ConcreteState subclasses (具体状态子类如 TCPEstablished, TCPListen, TCPClosed) — 每一子类实现一个与Context的一个状态相关的行为。
代码举例
假设我们有一个简单的文档编辑器其中文档可以处于三种状态草稿Draft、审核中UnderReview和已发布Published。我们可以使用 State 模式来管理这些不同的文档状态。
首先定义文档状态的抽象基类 DocumentState
#include iostreamclass Document;class DocumentState {
public:virtual void Edit(Document* document) 0;virtual void Review(Document* document) 0;virtual void Publish(Document* document) 0;
};
接下来实现具体的文档状态类
class DraftState : public DocumentState {
public:void Edit(Document* document) override;void Review(Document* document) override;void Publish(Document* document) override;
};class UnderReviewState : public DocumentState {
public:void Edit(Document* document) override;void Review(Document* document) override;void Publish(Document* document) override;
};class PublishedState : public DocumentState {
public:void Edit(Document* document) override;void Review(Document* document) override;void Publish(Document* document) override;
};然后定义文档类 Document该类维护当前文档的状态并委托状态对象来处理文档的编辑、审核和发布操作
class Document {
private:DocumentState* currentState;public:Document() : currentState(new DraftState()) {}void ChangeState(DocumentState* newState) {delete currentState;currentState newState;}void Edit() {currentState-Edit(this);}void Review() {currentState-Review(this);}void Publish() {currentState-Publish(this);}~Document() {delete currentState;}
};最后实现具体的文档状态类的方法
// Implementation of DraftState methods
void DraftState::Edit(Document* document) {std::cout Editing the document. std::endl;
}void DraftState::Review(Document* document) {std::cout Reviewing is not applicable in Draft state. std::endl;
}void DraftState::Publish(Document* document) {std::cout Publishing is not applicable in Draft state. std::endl;
}// Implementation of UnderReviewState methods
void UnderReviewState::Edit(Document* document) {std::cout Editing is not allowed during review. std::endl;
}void UnderReviewState::Review(Document* document) {std::cout Reviewing the document. std::endl;
}void UnderReviewState::Publish(Document* document) {std::cout Publishing is not allowed during review. std::endl;
}// Implementation of PublishedState methods
void PublishedState::Edit(Document* document) {std::cout Editing is not allowed for published documents. std::endl;
}void PublishedState::Review(Document* document) {std::cout Reviewing is not allowed for published documents. std::endl;
}void PublishedState::Publish(Document* document) {std::cout The document is already published. std::endl;
}在这个例子中文档的状态包括草稿、审核中和已发布而 Document 类委托给具体的状态对象来处理不同状态下的操作。这样文档类的行为可以根据其当前状态的变化而动态改变。
下面是一个简单的 main 函数演示了如何使用上述定义的文档类和状态类
int main() {Document document;// Initial state is Draftdocument.Edit(); // Output: Editing the document.document.Review(); // Output: Reviewing is not applicable in Draft state.document.Publish(); // Output: Publishing is not applicable in Draft state.// Change state to UnderReviewdocument.ChangeState(new UnderReviewState());document.Edit(); // Output: Editing is not allowed during review.document.Review(); // Output: Reviewing the document.document.Publish(); // Output: Publishing is not allowed during review.// Change state to Publisheddocument.ChangeState(new PublishedState());document.Edit(); // Output: Editing is not allowed for published documents.document.Review(); // Output: Reviewing is not allowed for published documents.document.Publish(); // Output: The document is already published.return 0;
}在这个 main 函数中我们创建了一个文档对象并在不同状态下调用了 Edit、Review 和 Publish 方法。通过改变文档的状态我们可以看到文档对象的行为随之改变符合状态模式的设计思想。
要点总结
State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中在对象状态切换时切换相应的对象但同时维持State 的接口这样实现了具体操作与状态转换之间的解耦。
为不同的状态引入不同的对象使得状态转换变得更加明确而且可以保证不会出现状态不一致的情况因为转换是原子性的——即要么彻底转换过来要么不转换。
如果State对象没有实例变量那么各个上下文可以共享同一个State对象从而节省对象开销。
以下是状态模式和策略模式之间的区别
关注点 状态模式 侧重于允许对象在其内部状态发生变化时更改其行为。它关注于表示对象的状态以及状态之间的转换。策略模式 侧重于定义一组算法封装每个算法并使它们可以互换。它允许客户端选择适当的算法而无需更改客户端代码。 状态/策略转换的责任 状态模式 状态之间的转换通常由上下文对象内部控制。上下文对象通过更改其内部状态来改变其行为。策略模式 特定策略算法的选择通常由客户端或外部实体控制。客户端决定使用哪种策略并可以在策略之间动态切换。
备忘录模式Memento
Motivation
在软件构建过程中某些对象的状态在转换过程中可能由于某种需要要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态便会暴露对象的细节实现。
如何实现对象状态的良好保存与恢复但同时又不会因此而破坏 对象本身的封装性。
代码举例
#include iostream
#include string// Memento备忘录类用于存储 Originator 内部状态的快照
class Memento {
private:std::string state; // 内部状态
public:Memento(const std::string s) : state(s) {} // 构造函数初始化内部状态std::string getState() const { return state; } // 获取内部状态void setState(const std::string s) { state s; } // 设置内部状态
};// Originator原发器类拥有需要存储的内部状态并能够创建和恢复备忘录
class Originator {
private:std::string state; // 内部状态
public:Originator() {} // 默认构造函数Memento createMomento() {Memento m(state); // 创建备忘录保存当前内部状态return m;}void setMomento(const Memento m) {state m.getState(); // 从备忘录中恢复内部状态}
};int main() {Originator orginator;// 捕获对象状态存储到备忘录Memento memento orginator.createMomento();// ... 改变 orginator 状态// 从备忘录中恢复orginator.setMomento(memento);return 0;
}
Momento定义
在不破坏封装性的前提下捕获一个对象的内部状态并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
Momento结构 参与者 • Memento (备忘录) — 备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态。 — 防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接口管理者(caretaker)只能看到备忘录的窄接口—它只能将备忘录传递给其他对象。相反原 发器能够看到一个宽接口允许它访问返回到先前状态所需的所有数据。理想的情况是只允许生成本备忘录的那个原发器访问本备忘录的内部状态。 • Originator (原发器) — 原发器创建一个备忘录用以记录当前时刻它的内部状态。 — 使用备忘录恢复内部状态。 • Caretaker (负责人) — 负责保存好备忘录。 — 不能对备忘录的内容进行操作或检查。
要点总结
备忘录Memento存储原发器Originator对象的内部状态在需要时恢复原发器状态。
Memento模式的核心是信息隐藏即Originator需要向外接隐藏信息保持其封装性。但同时又需要将状态保持到外界(Memento )。
由于现代语言运行时如C#、Java等都具有相当的对象序列化支持因此往往采用效率较高、又较容易正确实现的序列化方案来实现Memento模式。
chatGPT给出的一个备忘录模式的例子
Momento 模式旨在捕获一个对象的内部状态以便稍后可以将其恢复到此状态。以下是一个简单的 C 示例演示 Momento 模式的基本概念
#include iostream
#include string
#include vector// Momento存储原发器的内部状态
class Memento {
public:Memento(const std::string state) : state_(state) {}std::string GetState() const {return state_;}private:std::string state_;
};// Originator创建并恢复到 Momento 的状态
class Originator {
public:Originator() : state_() {}void SetState(const std::string state) {state_ state;std::cout Set state to: state std::endl;}Memento CreateMemento() {return Memento(state_);}void RestoreMemento(const Memento memento) {state_ memento.GetState();std::cout Restored to state: state_ std::endl;}private:std::string state_;
};// Caretaker负责保存和恢复 Momento
class Caretaker {
public:void AddMemento(const Memento memento) {momentos_.push_back(memento);}Memento GetMemento(int index) const {return momentos_[index];}private:std::vectorMemento momentos_;
};int main() {// 创建 OriginatorOriginator originator;// 创建 CaretakerCaretaker caretaker;// 设置状态并保存 Momentooriginator.SetState(State1);caretaker.AddMemento(originator.CreateMemento());// 设置新状态并保存 Momentooriginator.SetState(State2);caretaker.AddMemento(originator.CreateMemento());// 恢复到先前状态originator.RestoreMemento(caretaker.GetMemento(0));return 0;
}在此示例中Originator 表示拥有内部状态的对象Memento 表示保存状态的 Momento而 Caretaker 负责管理 Momento。在主函数中我们创建了 Originator 和 Caretaker并演示了如何设置状态、创建 Momento、保存 Momento、设置新状态以及通过 Momento 恢复到先前状态。
后记
截至2024年1月17日20点27分完成Facade, Proxy, Adapter, Mediator, State, Memento模式的学习。后面还有6个模式需要跟进。