突破编程_C++_设计模式(中介者模式)

1 中介者模式的基本概念

C++中的中介者模式(Mediator Pattern)是一种行为型设计模式,主要用于降低多个对象和类之间的通信复杂性。这种模式通过引入一个中介类来封装和处理不同类之间的通信,从而实现松耦合,使代码更易于维护。

在中介者模式中,各个对象不需要显式地相互引用,而是通过中介者进行通信。这样,对象之间的依赖关系被简化,降低了类的复杂度,并将一对多的依赖关系转化成了一对一的依赖关系。同时,这也符合迪米特原则,即一个对象应当对其他对象有最少的了解。

然而,需要注意的是,如果中介者变得过于庞大和复杂,可能会导致其本身的维护变得困难。因此,在职责混乱的情况下,不应使用中介者模式。

在中介者模式中,主要涉及到以下几个角色:

(1)中介者(Mediator):

  • 中介者类是整个模式的核心,它负责协调各个同事对象之间的交互,降低了同事之间的耦合度。
  • 中介者类通常知道所有的同事对象,并可以在同事对象之间传递消息。
  • 中介者可以独立地改变它们之间的交互方式,使得对象之间的交互更加灵活。

(2)同事类(Colleague):

  • 同事类(也称为关联类)是那些需要相互通信的对象。
  • 同事类不需要直接与其他同事类通信,而是通过中介者来进行交互。
  • 同事类通常知道中介者的存在,并可以通过中介者发送和接收消息。

(3)抽象中介者(AbstractMediator):

  • 这是一个中介者的抽象父类,定义了中介者的通用接口和行为。
  • 具体的中介者类会继承自这个抽象类,并实现具体的交互逻辑。

(4)具体中介者(ConcreteMediator):

  • 具体中介者类是中介者的具体实现,它包含了与同事类交互的具体逻辑。
  • 具体中介者知道所有的同事对象,并可以协调它们之间的交互。

(5)抽象同事类(AbstractColleague):

  • 这是一个同事类的抽象父类,定义了同事类的通用接口和行为。
  • 具体的同事类会继承自这个抽象类,并实现具体的交互逻辑。

(6)具体同事类(ConcreteColleague):

  • 具体同事类是同事类的具体实现,它包含了与中介者和其他同事类交互的具体逻辑。
  • 具体同事类通过中介者来发送和接收消息,实现与其他同事类的通信。

2 中介者模式的实现步骤

中介者模式的实现步骤如下:

(1)定义抽象中介者类:
创建一个抽象中介者类,定义用于同事类之间通信的方法。这些方法可能包括注册同事、接收来自同事的消息、并转发给相应的同事等。

(2)定义具体中介者类:
继承自抽象中介者类,并实现其中的方法。具体中介者类将负责存储和管理同事类对象,协调它们之间的交互。

(3)定义抽象同事类:
创建一个抽象同事类,定义一个接口用于与中介者进行通信。同事类将通过中介者来发送和接收消息,而不是直接与其他同事类交互。

(4)定义具体同事类:
继承自抽象同事类,并实现其中的方法。具体同事类将包含与中介者进行通信的代码,例如发送消息或接收来自中介者的通知。

(5)在同事类中使用中介者:
在具体同事类的实现中,创建对中介者的引用或指针,并通过中介者发送和接收消息。同事类不再直接与其他同事类通信,而是将消息传递给中介者,由中介者负责转发。

(6)初始化中介者和同事类:
在程序初始化时,创建具体中介者对象和具体的同事类对象。将同事类对象注册到中介者中,以便中介者能够管理和协调它们之间的交互。

(7)实现中介者的协调逻辑:
在具体中介者类中,实现协调同事类交互的逻辑。当接收到来自同事类的消息时,中介者根据消息类型和接收者信息,将消息转发给相应的同事类。

如下为样例代码:

#include <iostream>  
#include <memory>  
#include <vector>  
#include <string>  class AbstractColleague;// 抽象中介者类  
class AbstractMediator {
public:virtual ~AbstractMediator() = default;virtual void registerColleague(const std::shared_ptr<AbstractColleague>& colleague) = 0;virtual void colleagueChanged(const std::shared_ptr<AbstractColleague>& colleague) = 0;
};// 抽象同事类  
class AbstractColleague : public std::enable_shared_from_this<AbstractColleague> {
public:AbstractColleague(const std::shared_ptr<AbstractMediator>& mediator) : mediator(mediator) {}virtual ~AbstractColleague() = default;virtual void send(const std::string& message) = 0;virtual void receive(const std::string& message) = 0;void setMediator(const std::shared_ptr<AbstractMediator>& mediator) { this->mediator = mediator; }protected:std::weak_ptr<AbstractMediator> mediator;
};// 具体中介者类  
class ConcreteMediator : public AbstractMediator {
public:void registerColleague(const std::shared_ptr<AbstractColleague>& colleague) override {colleagues.push_back(colleague);}void colleagueChanged(const std::shared_ptr<AbstractColleague>& colleague) override {for (const auto& c : colleagues) {if (c != colleague) {c->receive("Someone has changed");}}}private:std::vector<std::shared_ptr<AbstractColleague>> colleagues;
};// 具体同事类  
class ConcreteColleague : public AbstractColleague {
public:ConcreteColleague(const std::string& name, const std::shared_ptr<AbstractMediator>& mediator): AbstractColleague(mediator), name(name) {}void send(const std::string& message) override {std::cout << name << " sends: " << message << std::endl;if (auto med = mediator.lock()) {med->colleagueChanged(shared_from_this());}}void receive(const std::string& message) override {std::cout << name << " receives: " << message << std::endl;}private:std::string name;
};int main() 
{// 创建中介者对象  auto mediator = std::make_shared<ConcreteMediator>();// 创建同事类对象,并将中介者注入  auto colleagueA = std::make_shared<ConcreteColleague>("ColleagueA", mediator);auto colleagueB = std::make_shared<ConcreteColleague>("ColleagueB", mediator);// 注册同事类到中介者  mediator->registerColleague(colleagueA);mediator->registerColleague(colleagueB);// 同事类通过中介者进行通信  colleagueA->send("Hello from A");return 0;
}

上面代码的输出为:

ColleagueA sends: Hello from A
ColleagueB receives: Someone has changed

这个例子定义了一个 AbstractMediator 抽象中介者类和一个 ConcreteMediator 具体中介者类。同样地,例子中也定义了 AbstractColleague 抽象同事类和一个 ConcreteColleague 具体同事类。

ConcreteColleague 类中的 send 方法发送消息,并通过中介者通知其他同事类有变化发生。receive 方法用于接收来自中介者的消息。

在 main 函数中,创建了一个 ConcreteMediator 对象和两个 ConcreteColleague 对象,并将中介者的智能指针传递给同事类。然后,注册同事类到中介者,并通过 send 方法让同事类A发送消息。中介者接收到消息后,会通知所有其他同事类有变化发生。

3 中介者模式的应用场景

C++ 中介者模式的应用场景广泛,主要适用于以下情况:

(1)事件驱动的系统: 在事件驱动的系统中,中介者模式可以用于管理事件的分发和处理。中介者可以接收来自不同对象的事件,并根据事件的类型和接收者信息,将事件转发给相应的对象进行处理。

(2)对象间交互复杂: 当多个对象之间存在错综复杂的交互关系,并且这些交互关系导致代码难以理解和维护时,可以使用中介者模式来简化这些交互。通过引入一个中介者对象,可以将对象之间的直接交互转化为通过中介者进行间接交互,从而降低对象之间的耦合度。

(3)系统需要灵活配置: 当系统中的对象交互方式需要经常变更时,使用中介者模式可以使得这些变更更加容易实现。中介者模式允许独立地改变对象之间的交互方式,而不需要修改大量的代码。

具体示例包括但不限于聊天程序、GUI 编程、网络编程等场景。在聊天程序中,多个用户对象和一个聊天室对象可以通过中介者模式进行通信,用户对象之间不直接通信,而是通过聊天室对象进行消息的发送和接收。这样可以有效地解耦用户对象和聊天室对象,提高代码的可维护性和可扩展性。

3.1 中介者模式应用于事件驱动的系统

在事件驱动的系统中,中介者模式非常适合用于管理事件的分发和处理。下面是一个中介者模式应用于事件驱动系统的样例,这个样例模拟了一个简单的 GUI 系统,其中按钮(Button)和文本框(TextBox)通过事件中介者(EventManager)来交互。

首先,定义事件和事件监听器的接口:

#include <iostream>  
#include <memory>  
#include <functional>  
#include <unordered_map>  
#include <string>  
#include <algorithm>  // 事件类型  
enum class EventType {BUTTON_CLICKED,TEXT_CHANGED
};// 事件基类  
class Event {
public:virtual EventType getType() const = 0;virtual ~Event() = default;
};// 点击事件  
class ButtonClickedEvent : public Event {
public:ButtonClickedEvent(const std::string& buttonName) : buttonName(buttonName) {}EventType getType() const override { return EventType::BUTTON_CLICKED; }const std::string& getButtonName() const { return buttonName; }private:std::string buttonName;
};// 文本改变事件  
class TextChangedEvent : public Event {
public:TextChangedEvent(const std::string& textBoxName, const std::string& newText): textBoxName(textBoxName), newText(newText) {}EventType getType() const override { return EventType::TEXT_CHANGED; }const std::string& getTextBoxName() const { return textBoxName; }const std::string& getNewText() const { return newText; }private:std::string textBoxName;std::string newText;
};// 事件监听器接口  
class EventListener {
public:virtual void handleEvent(const std::shared_ptr<Event>& event) = 0;virtual ~EventListener() = default;
};

接着,定义事件中介者(EventManager)和可以注册监听器并触发事件的组件(如 Button 和 TextBox):

// 事件中介者  
class EventManager {
public:using EventListenerPtr = std::shared_ptr<EventListener>;using EventPtr = std::shared_ptr<Event>;void registerListener(const std::string& eventName, const EventListenerPtr& listener) {listeners[eventName].push_back(listener);}void unregisterListener(const std::string& eventName, const EventListenerPtr& listener) {auto it = listeners.find(eventName);if (it != listeners.end()) {auto it2 = std::remove_if(it->second.begin(), it->second.end(), [&](const EventListenerPtr& listener_) {return listener_ == listener;   });it->second.erase(it2, it->second.end());}}void triggerEvent(const EventPtr& event) {std::string eventName = getEventName(event->getType());for (const auto& listener : listeners[eventName]) {listener->handleEvent(event);}}private:static std::string getEventName(EventType type) {switch (type) {case EventType::BUTTON_CLICKED: return "BUTTON_CLICKED";case EventType::TEXT_CHANGED: return "TEXT_CHANGED";default: return "UNKNOWN_EVENT";}}std::unordered_map<std::string, std::vector<EventListenerPtr>> listeners;
};// 按钮类  
class Button {
public:Button(const std::string& name, const EventManager::EventListenerPtr& listener): name(name), clickedListener(listener) {}void onClick() {auto event = std::make_shared<ButtonClickedEvent>(name);clickedListener->handleEvent(event);}private:std::string name;EventManager::EventListenerPtr clickedListener;
};// 文本框类  
class TextBox {
public:TextBox(const std::string& name, const EventManager::EventListenerPtr& listener): name(name), textChangedListener(listener) {}void setText(const std::string& newText) {auto event = std::make_shared<TextChangedEvent>(name, newText);textChangedListener->handleEvent(event);}private:std::string name;EventManager::EventListenerPtr textChangedListener;
};

最后,创建具体的监听器实现,并展示如何使用这些组件和中介者:

// 具体的监听器实现  
class TextBoxListener : public EventListener {
public:void handleEvent(const std::shared_ptr<Event>& event) override {if (event->getType() == EventType::BUTTON_CLICKED) {auto clickedEvent = std::static_pointer_cast<ButtonClickedEvent>(event);std::cout << "TextBoxListener: Button " << clickedEvent->getButtonName()<< " was clicked!" << std::endl;// 响应按钮点击事件,比如更新文本框内容等}}
};int main() 
{// 创建事件中介者EventManager eventManager;// 创建监听器  auto textBoxListener = std::make_shared<TextBoxListener>();// 创建按钮并注册监听器  Button button("MyButton", textBoxListener);// 注册事件到中介者  eventManager.registerListener("BUTTON_CLICKED", textBoxListener);// 模拟按钮点击事件  button.onClick();return 0;
}

上面代码的输出为:

TextBoxListener: Button MyButton was clicked!

3.2 中介者模式应用于对象间交互复杂

以一个聊天室应用为例,其中多个用户(对象)通过聊天室(中介者)进行交互。以下是一个简化版的C++样例,展示了如何使用中介者模式来处理用户之间的消息传递。

首先,定义用户(User)类,它能够发送消息:

#include <iostream>  
#include <memory>  
#include <string>  
#include <vector>  class AbstractChatRoom
{
public:virtual void sendMessage(const std::string& sender, const std::string& message) = 0;
};class User {
public:User(const std::string& name, std::shared_ptr<AbstractChatRoom> chatRoom): m_name(name), m_chatRoom(chatRoom) {}void sendMessage(const std::string& message) {m_chatRoom->sendMessage(m_name, message);}void receiveMessage(const std::string& sender, const std::string& message) {std::cout << sender << ": " << message << std::endl;}std::string getName() {return m_name;}private:std::string m_name;std::shared_ptr<AbstractChatRoom> m_chatRoom;
};

接下来,定义聊天室(ChatRoom)类,作为中介者:

class ChatRoom : public AbstractChatRoom {
public:void registerUser(const std::shared_ptr<User>& user) {m_users.push_back(user);}void sendMessage(const std::string& sender, const std::string& message) override {for (const auto& user : m_users) {if (user->getName() != sender) {user->receiveMessage(sender, message);}}}private:std::vector<std::shared_ptr<User>> m_users;
};

现在,可以创建一个主程序来模拟多个用户通过聊天室进行交互:

int main() 
{// 创建聊天室中介者  std::shared_ptr<ChatRoom> chatRoom = std::make_shared<ChatRoom>();// 创建用户并注册到聊天室  std::shared_ptr<User> user1 = std::make_shared<User>("Alice", chatRoom);std::shared_ptr<User> user2 = std::make_shared<User>("Bob", chatRoom);std::shared_ptr<User> user3 = std::make_shared<User>("Charlie", chatRoom);chatRoom->registerUser(user1);chatRoom->registerUser(user2);chatRoom->registerUser(user3);// 用户发送消息  user1->sendMessage("Hello, everyone!");user2->sendMessage("Hi, Alice. What's up?");user3->sendMessage("I'm good, thanks. Nice to meet you all.");return 0;
}

上面代码的输出为:

Alice: Hello, everyone!
Alice: Hello, everyone!
Bob: Hi, Alice. What's up?
Bob: Hi, Alice. What's up?
Charlie: I'm good, thanks. Nice to meet you all.
Charlie: I'm good, thanks. Nice to meet you all.

这个样例中创建了三个用户(Alice、Bob 和 Charlie),并将它们注册到聊天室中。每个用户都可以发送消息,消息通过聊天室中介者传递给其他所有用户。这样,用户之间不需要直接交互,而是通过中介者进行通信,从而简化了对象间的交互。

4 中介者模式的优点与缺点

C++ 中介者模式的优点主要包括:

(1)降低对象间的耦合度: 通过中介者对象进行通信,减少了对象之间的直接依赖,降低了系统的耦合度。这使得对象可以更加独立地改变和复用,提高了系统的灵活性和可维护性。

(2)简化对象间的交互: 中介者模式将多个对象之间的复杂交互关系转换为中介者与对象之间的一对多关系,使得对象之间的交互变得简单和清晰。这有助于降低代码的复杂性,提高代码的可读性和可维护性。

(3)集中控制交互: 通过中介者对象,可以对对象间的交互进行集中控制和管理。这使得系统更容易进行协调、监控和扩展。

(4)易于添加新对象: 当系统中需要添加新的对象时,只需要将该对象与中介者对象建立联系,而不需要修改其他对象。这降低了添加新对象的难度和成本。

然而,C++ 中介者模式也存在一些缺点:

(1)可能导致中介者对象过于复杂: 如果系统中对象间的交互关系非常复杂,中介者对象可能会变得庞大和复杂,难以管理和维护。这可能会抵消掉降低对象间耦合度带来的优势。

(2)可能产生过多的中介者: 如果系统中有多个不同类型的交互关系,可能需要创建多个中介者对象来分别处理这些关系。这会增加系统的复杂性和管理成本。

(3)对中介者的依赖: 系统中的对象都需要依赖于中介者对象进行交互,这增加了对中介者的依赖。如果中介者对象出现问题或失效,整个系统可能会受到影响。

(4)不适用于所有场景: 虽然中介者模式在某些场景下非常有用,但并不是所有场景都适合使用中介者模式。在一些简单的系统中,对象间的交互关系并不复杂,使用中介者模式可能会引入不必要的复杂性和开销。

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

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

相关文章

每天一个数据分析题(二百零九)

某销售服饰的电商店铺想要通过漏斗模型分析转化率数据&#xff0c;关于单一漏斗模型的局限性&#xff0c;下列说法不正确的是&#xff08;&#xff09; A.不能反映趋势&#xff0c;单一漏斗模型只能反映某一时期的情况,不能看出转化率是在上升还是下降。需要增加时间维度上的比…

【unity与android的交互(一)】安卓打包相关的常见参数详解

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

【解读】Synopsys发布2024年开源安全和风险分析报告OSSRA

软件供应链管理中&#xff0c;许可证和安全合规性至关重要。开源组件和库可降低风险&#xff0c;但需了解许可证内容。Synopsys 2023年审计发现&#xff0c;超过一半的代码库存在许可证冲突。MIT许可证是最常用的宽松许可证&#xff0c;但也与其他许可证存在不兼容风险。点此获…

AI新工具(20240315) 用户通过点击图像的特定部分实现域区图像动画; 先进的机器人操作框架

✨ 1: Follow-Your-Click 用户通过点击图像的特定部分实现域区图像动画 Follow-Your-Click 是一个开放领域的区域性图像动画创作工具&#xff0c;它可以通过简短的提示语实现图像中特定区域的动画效果。这种功能允许用户通过点击图像的特定部分并输入简短的指令&#xff08;例…

ViT学习笔记

一、Embedding层 对于标准的Transformer 模块&#xff0c;要求输入的是 token (向量 ) 序列&#xff0c;即二维矩阵 [num_token, token_dim] 在代码实现中&#xff0c;直接通过一个卷积层来实现以ViT- B/16为例&#xff0c;使用卷积核大小为 16x16 &#xff0c; stride 为 16 …

【Streamlit学习笔记】实现包含多个sheet的excel文件下载

1、什么是Streamlit Streamlit是一个免费的开源框架&#xff0c;用于快速构建和共享漂亮的机器学习和数据科学Web应用程序&#xff0c;官网链接 Streamlit Streamlit API链接 API reference 实际项目中遇到的问题&#xff1a;包含多个sheet的excel文件下载&#xff0c;下面将给…

二维数组_矩阵交换行

任务描述 给定一个5*5的矩阵&#xff08;数学上&#xff0c;一个rc的矩阵是一个由r行c列元素排列成的矩形阵列&#xff09;&#xff0c;将第n行和第m行交换&#xff0c;输出交换后的结果。 输入格式: 输入共6行&#xff0c;前5行为矩阵的每一行元素,元素与元素之间以一个空格…

传输层的UDP协议

1. UDP协议报文格式 1.1 16位端口号 UDP协议报文中&#xff0c;端口号占2个字节&#xff0c;包括 源端口号 和 目的端口号。 1.2 16位UDP长度 UDP报文长度为2个字节 &#xff0c;即UDP数据报长度为0~65535&#xff0c;也就是64kb。 1.3 16位UDP检验和 数据在网络传输的…

k8s helm 删除 tiller

kuberneter 上面装了 helm 想卸载还并不是那么简单, 参考 stackoverflow 回复 kubectl get -n kube-system secrets,sa,clusterrolebinding -o name|grep tiller|xargs kubectl -n kube-system delete kubectl get all -n kube-system -l apphelm -o name|xargs kubectl dele…

DHCP在企业网的部署及安全防范

学习目标&#xff1a; 1. DHCP能够解决什么问题&#xff1f; 2. DHCP服务器如何部署&#xff1f; 3. 私接设备会带来什么问题以及如何防范&#xff1f; 给DHCP服务器配置地址&#xff1a; 地址池&#xff1a; DHCP有2种分配模式&#xff1a;全局分配和接口分配 DHCP enable

element el-table 表格限制多选个数

本次的功能的要求是&#xff1a; 原本的引入的elment 的表格&#xff0c;去除全选框&#xff0c;版本对比的按钮&#xff0c;需要在选择版本&#xff0c;并且版本个数为2的时候&#xff0c;可点击&#xff0c;进行版本对比操作每次选择版本的时候&#xff0c;目前已有两个选择…

酷轻松气囊按摩护膝全新上线,科技呵护膝部健康

在快节奏的现代生活中&#xff0c;膝部健康问题逐渐引起人们的重视。长时间的站立、行走或运动&#xff0c;都可能对膝部造成不同程度的压力和损伤。 特别是家里有老人一直被老寒腿、关节发凉疼痛困扰的&#xff0c;经常一遇到下雨天&#xff0c;膝盖就不舒服&#xff1b;尤其到…

HDC2010+STM32读取数据发送到onenet平台

第一次用HDC2010用stm32l051单片机读取数据看了2天的datasheet都没看明白&#xff0c;好在在老板的帮助下里面的数据读取出来。之后的工作一个人好在顺利完成。以下记录一下写的代码 /* USER CODE BEGIN Header */ /********************************************************…

Linux下非阻塞IO实验二

一. 简介 前面一篇文章编写Linux驱动代码&#xff08;轮询函数的实现&#xff09;&#xff0c;来处理 Linux下应用程序以非阻塞方式访问设备。文章地址&#xff1a; Linux下非阻塞IO实验一-CSDN博客 本文编写另外一种驱动代码实现方式&#xff0c;与上面实现的区别主要是阻…

Selenium WebDriver类的常用属性和方法汇总

WebDriver类是 Selenium WebDriver 提供的用于控制浏览器的核心类之一&#xff0c;它提供了许多属性和方法来管理浏览器会话、导航到不同的网页、定位和操作页面元素等。下面分别归纳其属性和方法&#xff1a; **属性&#xff1a;** 1. capabilities: 返回当前会话的浏览器的…

rsync+inotify-tools文件传输

目录 rsync rsync概述 rsync优缺点 rsync参数 rsync命令 rsync同步源 linux 安装rsync 安装rsync服务端 安装rsync客户端 windows 安装rsync rsync实践 inotify-tools inotify-tools概述 innotify-tools 命令、参数、事件 rsync客户端安装inotify-tools innotif…

鸿蒙-自定义组件的生命周期

目录 自定义组件的生命周期 1.aboutToAppear 2.aboutToDisappear 3.onPageShow 4.onPageHide 5.onBackPress 日志输出 1.显示页面 2.页面点击返回按钮 3.页面跳转 4.页面返回 自定义组件的生命周期 先来一段列子 import router from ohos.router Entry Component…

虚拟机VMware上 centos7 的网络配置

第一步&#xff1a;权限的切换 由普通用户切换到超级用户 用户名为&#xff1a;root 密码为&#xff1a;自己安装 linux 时第一次设置的密码 su -root超级用户的命令提示符是“#”&#xff0c;普通用户的命令提示符是“$”。当看到你的命令提示符为“$”时&#xff0c;证明切…

华为配置敏捷分布式SFN漫游实验

配置敏捷分布式SFN漫游示例 组网图形 图1 配置敏捷分布式SFN漫游示例组网图 组网需求数据规划配置思路配置注意事项操作步骤配置文件 组网需求 某医院通过部署敏捷分布式网络给医护人员提供WLAN接入服务&#xff0c;以满足医护人员办公的最基本需求。管理员希望终端在覆盖区域内…

pytorch之诗词生成6--eval

先上代码&#xff1a; import tensorflow as tf from dataset import tokenizer import settings import utils# 加载训练好的模型 model tf.keras.models.load_model(r"E:\best_model.h5") # 随机生成一首诗 print(utils.generate_random_poetry(tokenizer, model)…