C++常见设计模式

设计模式

设计模式的六大原则:https://zhuanlan.zhihu.com/p/110130347

适配器模式

假设Client想要一个正方形(client Interface),但是提供的服务(service)是个圆形,那么我就把这个圆通过适配器(adapter)装到正方形里,adapter继承了正方形的接口,你以为你用的是个正方形,实际上里面是个圆。
优点:
单一职责原则:可以将接口或数据转换代码从程序主要业务逻辑中分离。
开闭原则: 只要客户端代码通过客户端接口与适配器进行交互,你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
缺点:
代码整体复杂度增加

#include<iostream>
using namespace std;//Target:客户端需要的目标接口,适配器你往哪转
class JsonInfo
{
public:virtual ~JsonInfo() {}//这个就是读取的json信息,我们要实现的其实就是xml转为jsonvirtual string resquest() const {return "Some Json msg.......";}
};//Adaptee: 需要转换的目标,客户端不兼容的接口,需要转换为Target
class XmlInfo
{
public:string specialResquest() const{return "Some Xml msg.......";}
};//Adapter: 适配器,把Adaptee包装起来,让他看上去像是Target
//我们是要转为json,所以需要继承,这样才可以看上去像json
class XmlJsonAdapter :public JsonInfo
{public:XmlJsonAdapter(XmlInfo* adptee) :m_adptee(adptee) {}virtual string resquest() const override{string string = m_adptee->specialResquest();return "Json->XmlJsonAdapter->XmlInfo";}
private://通过对象的方式把XmlInfo内容给包住XmlInfo* m_adptee;
};//表示长图的client
void clientCode(const JsonInfo* info)
{cout << "股票分析软件Json: " << info->resquest()<< endl;cout << "-----------------------" << endl;
}int main()
{JsonInfo jsonInfo;clientCode(&jsonInfo);XmlInfo xmlInfo;XmlJsonAdapter jsonAdapter(&xmlInfo);clientCode(&jsonAdapter);return 0;
}

经典的“方钉圆孔问题”
在这里插入图片描述

/*
有一个圆孔,可以把圆钉放进去,但现在只能提供方钉,所以需要一个适配器,把方钉裹成圆钉,就可以被适配器调用了。
获得裹后圆钉的半径,方钉的宽度除2再乘根号2圆孔就相当于client,圆钉表示客户端可以处理的类型,方钉相当于服务器提供的类型
*/
#include<iostream>
using namespace std;//定义圆钉
class RoundPeg
{
public:RoundPeg(){}virtual ~RoundPeg() {}RoundPeg(const double& r) :m_radius(r) {};virtual int getRadius() const{return this->m_radius;}
private:int m_radius;
};//定义方钉
class SqurePeg
{
public:virtual ~SqurePeg() {};SqurePeg(const int& wid) :m_wid(wid) {};int getWidth(){return m_wid;}
private:int m_wid;
};//定义圆孔
class RoundHole
{
public:RoundHole(const int& r) :radius(r) {}int getRadius(){return this->radius;}bool fits(RoundPeg* roundPeg){if (radius < roundPeg->getRadius())return false;elsereturn true;}
private:int radius;
};//定义适配器,继承自圆钉
class Adapter : public RoundPeg
{
public:Adapter(SqurePeg* adptee) :m_adptee(adptee) {}int getRadius() const override{int r = m_adptee->getWidth() * sqrt(2) / 2;return r;}private:SqurePeg* m_adptee;
};int main()
{//定义圆孔RoundHole hole =  RoundHole(5);//定义圆钉RoundPeg r_peg1 = RoundPeg(3);RoundPeg r_peg2 = RoundPeg(10);//定义方钉SqurePeg s_peg1 = SqurePeg(2);SqurePeg s_peg2 = SqurePeg(10);bool res = hole.fits(&r_peg1);if (res) cout << "r_peg1 畅通无阻" << endl;else cout << "r_peg1 堵死" << endl;res = hole.fits(&r_peg2);if (res) cout << "r_peg2 畅通无阻" << endl;else cout << "r_peg2 堵死" << endl;Adapter adapter(&s_peg1);res = hole.fits(&adapter);if (res) cout << "s_peg1 畅通无阻" << endl;else cout << "s_peg1 堵死" << endl;Adapter adapter2(&s_peg2);res = hole.fits(&adapter2);if (res) cout << "s_peg2 畅通无阻" << endl;else cout << "s_peg2 堵死" << endl;return 0;
}

单例模式

单例模式:一个类不管创建多少次对象,永远只能得到该类型的一个实例。像日志模块,数据库模块就会用到。
个人理解,单例本质上就是全局变量,只不过是又包装了一层。

饿汉式单例模式:

饿汉式单例,即使没调用对象,static Singleton instance;也会实例化,因为存在数据段,会在main执行前执行初始化。
一定是线程安全的,线程函数启动前实例就会初始化好。
缺点:对象的实例化会调用构造函数,但是这个构造函数在实际应用时,会构造很多东西,比如加载配置文件什么的,要是不使用这些东西,构造的不就浪费了,白费时间

 #include<iostream>using namespace std;class Singleton{public://3 获取类的唯一实例对象的接口方法static Singleton* getInstance(){return &instance;}private://2 定义一个唯一类的实例对象static Singleton instance;//1 构造函数私有化,并禁止拷贝构造和赋值构造Singleton(){}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;};Singleton Singleton::instance;int main(){Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();return 0;}

懒汉式单例实现模式:
把对象的实例化延迟到第一次获取该实例对象的时候
线程安全的单例模式-使用互斥锁加双重判断

#include<iostream>#include<mutex>using namespace std;mutex mtx;class Singleton{public://3 获取类的唯一实例对象的接口方法//锁加双重判断static Singleton* getInstance(){//lock_guard<mutex> guard(mtx); //锁的粒度太大了,单线程完全不用这样if (instance == nullptr){lock_guard<mutex> guard(mtx);/*开辟内存构造对象给instance赋值*/if (instance == nullptr){instance = new Singleton();}}//出括号释放锁return instance;}private://2 定义一个唯一类的实例对象 存放在数据段static Singleton volatile instance;  //加上volatile后,当一个线程对instance改变,其他线程立马就能看到改变了,看的都是内存里的值//1 构造函数私有化,并禁止拷贝构造和赋值构造Singleton(){}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;};Singleton* volatile Singleton::instance=nullptr;int main(){Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();return 0;}

上面的代码可以关注下 volatile 的用法。

观察者模式

主要关注的是对象之间的一对多通信,也就是多个对象都依赖一个对象,当该对象状态发生改变的时候,其他对象都能够接收到相应的通知。

#include<iostream>
#include<unordered_map>
#include<list>
using namespace std;class Observer
{
public://处理消息的接口virtual void handle(int msgid) = 0;
};//观察者1
class Observer1 :public Observer
{
public:void handle(int msgid){switch (msgid){case 1:cout << "Observer1 recv 1 msg!" << endl;break;case 2:cout << "Observer1 recv 2 msg!" << endl;break;default:cout << "Observer1 recv unknown msg!" << endl;break;}}
};//观察者2
class Observer2 :public Observer
{
public:void handle(int msgid){switch (msgid){case 2:cout << "Observer2 recv 2 msg!" << endl;break;default:cout << "Observer2 recv unknown msg!" << endl;break;}}
};//观察者3
class Observer3 :public Observer
{
public:void handle(int msgid){switch (msgid){case 1:cout << "Observer3 recv 1 msg!" << endl;break;case 3:cout << "Observer3 recv 3 msg!" << endl;break;default:cout << "Observer3 recv unknown msg!" << endl;break;}}
};class Subject
{
public:void addObserver(Observer* obs, int msgid){//使用unordered_map,重载的[]运算符,如果找到了就直接插入,找不到会重新创建subMap[msgid].push_back(obs);}//主题检测发生变化,通知相应的观察者对象处理事件void dispatch(int msgid){auto it = subMap.find(msgid);if (it != subMap.end()){for (auto pObs : it->second){pObs->handle(msgid);}}}
private://使用一个map存储哪个观察者对哪个消息感兴趣unordered_map<int, list<Observer*>> subMap;
};int main()
{Subject subject;Observer* p1 = new Observer1();Observer* p2 = new Observer2();Observer* p3 = new Observer3();subject.addObserver(p1, 1);subject.addObserver(p1, 2);subject.addObserver(p2, 2);subject.addObserver(p3, 1);subject.addObserver(p3, 3);int msgid = 0;for (;;){cout << "输入消息ID:";cin >> msgid;if (msgid == -1)break;subject.dispatch(msgid);}return 0;
}

代理模式

这里主要说的是保护代理,即给不同的用户提供不同的对象访问权限。
通过代理类,来控制实际对象的访问权限。
下面的代码,主要是实现了用户观看普通电影,VIP电影,用券影的相关权限。

#include<iostream>
using namespace std;//抽象类
class VideoSite
{
public:virtual void freeVideo() = 0;virtual void vipVideo() = 0;virtual void ticketVideo() = 0;
};//委托类
class FixBugVideoSite :public VideoSite
{virtual void freeVideo(){cout << "观看免费电影" << endl;}virtual void vipVideo(){cout << "观看免费电影" << endl;}virtual void ticketVideo(){cout << "观看免费电影" << endl;}
};//代理类 代理委托类
class FreeVideoSite :public VideoSite
{
public:FreeVideoSite() { pVideo = new FixBugVideoSite(); }~FreeVideoSite() { delete pVideo; }//必须全部重写这些方法,要是不重写,代理类本身也成抽象类了void freeVideo(){pVideo->freeVideo();}void vipVideo(){cout << "您是普通用户,请升级VIP后观看" << endl;}void ticketVideo(){cout << "您未购买券,请购买后观看" << endl;}
private:VideoSite* pVideo;
};class VipVideoSite :public VideoSite
{
public:VipVideoSite() { pVideo = new FixBugVideoSite(); }~VipVideoSite() { delete pVideo; }//必须全部重写这些方法,要是不重写,代理类本身也成抽象类了void freeVideo(){cout << "观看普通电影" << endl;}void vipVideo(){pVideo->vipVideo();}void ticketVideo(){cout << "您未购买券,请购买后观看" << endl;}
private:VideoSite* pVideo;
};class ticketVideoSite :public VideoSite
{
public:ticketVideoSite() { pVideo = new FixBugVideoSite(); }~ticketVideoSite() { delete pVideo; }//必须全部重写这些方法,要是不重写,代理类本身也成抽象类了void freeVideo(){cout << "观看普通电影" << endl;}void vipVideo(){cout << "观看VIP电影" << endl;}void ticketVideo(){pVideo->ticketVideo();}
private:VideoSite* pVideo;
};void showMovie(VideoSite* ptr)
{ptr->freeVideo();ptr->vipVideo();ptr->ticketVideo();
}int main()
{VideoSite* p1 = new FreeVideoSite();VideoSite* p2 = new ticketVideoSite();showMovie(p1);showMovie(p2);delete p1;delete p2;return 0;
}

简单工厂模式

在创建的类比较多的时候可以使用工厂模式,主要是封装了对象的创建。
把这些对象都封装到一个工厂类里面,不需要知道这个对象是怎么创建的,就比如士兵上战场,只需要给他一把枪就可以了,不需要让他再new一个枪出来。
不符合开闭原则

#include<iostream>
#include<memory>
using namespace std;//工厂模式主要是封装了对象的创建
/*
简单工厂模式
不符合开放封闭原则
如果要多加几个工厂,就得修改switch
*/
class Car
{
public:Car(string name) :m_name(name) {};virtual void show() = 0;
protected:string m_name;
};class Bmw : public Car
{
public:Bmw(string name) :Car(name) {}void show(){cout << "购买了一辆宝马:" << m_name << endl;}
};class Audi : public Car
{
public:Audi(string name) :Car(name) {}void show(){cout << "购买了一辆奥迪:" << m_name << endl;}
};enum CarType
{BMW,AUDI
};class SimpleFactory
{
public:Car* createCar(CarType ct){switch (ct){case BMW:return new Bmw("X1");case AUDI:return new Audi("A8");default:cerr << "传入参数不正确" << endl;break;}return nullptr;}
};int main()
{unique_ptr<SimpleFactory> factory(new SimpleFactory());unique_ptr<Car> p1(factory->createCar(BMW));unique_ptr<Car> p2(factory->createCar(AUDI));p1->show();p2->show();return 0;
}

工厂方法

可以弥补简单工厂方法的缺点,符合开闭原则。以本例来看,如果想生产其他的汽车,只需要添加对应的工厂类即可。

#include<iostream>
#include<memory>
using namespace std;/*
工厂方法可以解决简单工厂模式遇到的问题
一个基类,包含一个虚工厂函数,实现多态
多个子类,重写父类的工厂函数,每个工厂负责生产一种汽车,如果想生产其他的汽车,只需要添加对应的工厂类
*/class Car
{
public:Car(string name) :m_name(name) {};virtual void show() = 0;
protected:string m_name;
};class Bmw : public Car
{
public:Bmw(string name) :Car(name) {}void show(){cout << "购买了一辆宝马:" << m_name << endl;}
};class Audi : public Car
{
public:Audi(string name) :Car(name) {}void show(){cout << "购买了一辆奥迪:" << m_name << endl;}
};//工厂类
class Factory
{
public:virtual Car* createCar(string name) = 0;
};//宝马工厂
class BmwFty : public Factory
{Car* createCar(string name){return new Bmw(name);}
};//奥迪工厂
class AudiFty : public Factory
{Car* createCar(string name){return new Audi(name);}
};int main()
{unique_ptr<Factory> bmwfty(new BmwFty());unique_ptr<Factory> audifty(new AudiFty());unique_ptr<Car> p1(bmwfty->createCar("X1"));unique_ptr<Car> p2(audifty->createCar("A8"));p1->show();p2->show();return 0;
}

抽象工厂方法

一个工厂可能不止是生产汽车,也要生产相关的配件。

#include<iostream>
#include<memory>
using namespace std;/*
一个工厂不只生产车,还会生产相关的零件
*///产品系列1
class Car
{
public:Car(string name) :m_name(name) {};virtual void show() = 0;
protected:string m_name;
};class Bmw : public Car
{
public:Bmw(string name) :Car(name) {}void show(){cout << "购买了一辆宝马:" << m_name << endl;}
};class Audi : public Car
{
public:Audi(string name) :Car(name) {}void show(){cout << "购买了一辆奥迪:" << m_name << endl;}
};//系列产品2
class Light
{
public:virtual void show() = 0;
};class BmwLight :public Light
{void show(){cout << "BMW LIght" << endl;}
};class AudiLight :public Light
{void show(){cout << "Audi LIght" << endl;}
};//工厂类 -> 抽象工厂类(对有一组关联关系的产品,提供产品对象的统一创建)
//如果新建灯也写一个类,那类太多了,太麻烦了
class AbsFactory
{
public:virtual Car* createCar(string name) = 0; //工厂方法 创建汽车virtual Light* createLight() = 0; //工厂方法 创建汽车关联的产品,车灯
};//宝马工厂
class BmwFty : public AbsFactory
{Car* createCar(string name){return new Bmw(name);}Light* createLight(){return new BmwLight();}
};//奥迪工厂
class AudiFty : public AbsFactory
{Car* createCar(string name){return new Audi(name);}Light* createLight(){return new AudiLight();}
};int main()
{unique_ptr<AbsFactory> bmwfty(new BmwFty());unique_ptr<AbsFactory> audifty(new AudiFty());unique_ptr<Car> p1(bmwfty->createCar("X1"));unique_ptr<Car> p2(audifty->createCar("A8"));unique_ptr<Light> l1(bmwfty->createLight());unique_ptr<Light> l2(audifty->createLight());p1->show();l1->show();p2->show();l2->show();return 0;
}

工厂模式总结:

  1. 简单工厂:把对象的创建封装到一个接口函数里,通过传入不同的标识,返回创建的对象。客户不用自己负责 new 一个对象,不用了解对象创建的详细过程。但是不符合设计模式的开闭原则。
  2. 工厂方法:定义一个基类,提供一个纯虚函数(创建产品),定义派生类(具体产品的工厂)负责创建爱你对应的产品,可以做到不同的产品在不同的工厂里创建,能够实现对现有的工厂,以及产品的修改关闭。缺点是:实际上,很多产品都会有关联关系,不应该放在不同的工厂里去创建。
  3. 抽象工厂方法:把有关联关系的的产品放在一个抽象工厂里面AbsFactory,派生类负责创建该系列所有的产品。

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

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

相关文章

java-decompiler

Java Decompiler GitHub F:\Document_JD-GUI\jd-gui-windows-1.4.0 jd-gui-windows-1.4.0 JDK 1.7 jd-gui-windows-1.6.6 JDK 1.8 Releases java-decompiler/jd-gui GitHub

flutter web 优化和flutter_admin_template

文章目录 Flutter Admin TemplateLive demo: https://githubityu.github.io/live_flutter_adminWeb 优化 Setup登录注册英文 亮色主题 中文 暗黑主题管理员登录权限 根据权限动态添加路由 第三方依赖License最后参考学习 Flutter Admin Template Responsive web with light/da…

【操作系统笔记十四】科普:POSIX 是什么

注&#xff1a;本文转载自该文章posix是什么都不知道&#xff0c;还好意思说你懂Linux&#xff1f; Linux开发者越来越多&#xff0c;但是仍然有很多人整不明白POSIX是什么。本文就带着大家来了解一下到底什么是POSIX&#xff0c;了解他的历史和重要性。 一、什么是 POSIX&…

虹科分享 | 网络保险:有效承保网络风险解决方案

文章来源&#xff1a;虹科网络安全 点击阅读原文&#xff1a;https://mp.weixin.qq.com/s/myCFPYtVVz5TPSFQaKqvLg 网络风险似乎往往很难量化&#xff0c;这使得保险公司很难适当地承保其网络风险政策。威胁载体的数量和不断发展的威胁&#xff0c;如新型恶意软件/勒索软件&…

RFID技术:钢条加工现场的智能化管理利器

RFID技术&#xff1a;钢条加工现场的智能化管理利器 RFID&#xff08;Radio Frequency Identification&#xff09;技术作为一种非接触式自动识别技术&#xff0c;近年来在工业领域得到广泛应用。本文将探讨RFID在钢条加工现场的应用&#xff0c;包括材料追踪与管理、生产过程…

安卓系列机型 框架LSP 安装步骤 支持多机型 LSP框架通用安装步骤【二】

​​​​​​安卓玩机教程---全机型安卓4----安卓12 框架xp edx lsp安装方法【一】 低版本可以参考上个博文了解相关安装方法。 LSP框架优点 简单来说装lsp框架的优点在于可以安装各种模块。包括 但不限于系统优化 加速 游戏开挂等等的模块。大致相当于电脑的扩展油猴 Lspos…

SAP服务器文件管理

SAP服务器文件管理 文件说明&#xff1a;对于SAP服务器的文件管理&#xff0c;系统给出3个事物码&#xff0c;分别是显示目录的AL11&#xff0c;下载文件的 CG3Y和上传文件的CG3Z。 AL11显示目录:以查找系统参数文件为例&#xff0c;在前台执行事物码AL11进入&#xff0c;如图…

word中使用latex多行公式,矩阵公式

\eqarray{H& [h(x_1)^T,\cdots,h(x_N)^T]^T \\ & [\matrix{g(w_1 x_1b_1) & \cdots & g(w_L x_1b_L) \\ \vdots & \ddots & \vdots \\ g(w_1 x_Nb_1) & \cdots & g(w_L x_Nb_L)}]_{N \times L}}&的引起的那条竖线可以通过backspace或者del…

网络基础面试题

1. ISO/OSI的七层模型 ISO国际标准化组织 OSI开放系统互连 TCP和UDP都会进行差错校验&#xff0c;TCP会告诉A包发错了&#xff0c;但UDP不会告诉A发错了会把包丢弃。 静态路由不需要路由器做任何的计算&#xff0c;对路由器的消耗是最小的&#xff0c;效率最高但是缺点是…

SQLyog 连接 MySQL8.0+ 报错2058

问题如下&#xff1a; 解决方案&#xff1a; 1.首先用命令窗口进入user表 2.使用有mysql.user表权限的用户连接mysql并执行如下命令&#xff1a; ALTER USER sqlyoglocalhost IDENTIFIED WITH mysql_native_password BY root23456; 注&#xff1a;使用mysql_native_password…

亿图脑图新版本支持思维导图一键生成PPT、音视频等格式,办公提效再升级

近日&#xff0c;国产思维导图软件——亿图脑图MindMaster发布了全新版本V10.9.0&#xff0c;本次亿图脑图的升级给用户带来了极大的惊喜。全新升级的亿图脑图MindMaster不仅支持20格式的文件智能解析成思维导图&#xff0c;还支持思维导图一键生成PPT、音频、视频等内容形式&a…

Excel实现只针对某项字符第一次出现的位置分列

取第一次出现左边数值 B1LEFT(A1,SEARCH(".",A1)-1) 取第一次出现右边数值 C1RIGHT(A1,LEN(A1)-SEARCH(".",A1)) 公式如图&#xff1a;

AI智能视频监控技术如何助力美好乡村建设?

随着城市化发展&#xff0c;很多乡村设施也在逐渐完善&#xff0c;智能监控也成了乡村发展必不可少的一环&#xff0c;智能视频监控应该在乡村建设里如何发挥作用呢&#xff1f; 1、有效提升安全意识 通过在乡村重要区域、公共场所、道路等设置智能视频监控设备&#xff0c;可…

第六次面试、第一次复试

第六面&#xff1a; hr迟到&#xff0c;说是搞错了以为线下&#xff0c;我打电话过去才开始&#xff0c;问我想电话面还是视频&#xff0c;果断电话面 自我介绍 介绍了一下公司的工作 ................. 项目拷打&#xff1a; grpc数据如何传输的如何调用两个接口如何获取…

CasaOS:一个docker容器应用的可视化Portal

CasaOS 官网声称他是一个家庭云操作系统&#xff0c;但我实际使用后感觉称之为“docker容器的可视化Portal”更合适。因为它本身不具备IAAS、PAAS、或SAAS的开箱即用能力&#xff0c;更像是一个把OS上的docker Container集中管理并展示的索引目录&#xff0c;各个docker Contai…

uni-app:点击图片进行图片旋转(可自定义旋转次数)

效果 代码 <template><view><view class"top_line"><view class"top_img"><image src"../../../static/bg/index.png" mode""></image></view><view class"top_button">…

3D 视觉市场空间广阔,3D 感知龙头全技术路线布局

3D 视觉市场尚处在发展早期,空间广阔 人类 70%以上信息通过眼睛获取,对于机器而言,视觉感知也是其“智能化”升级的重要基础。3D 成像让每一个像素除 x、y 轴数据外,还有 z 轴(深度/距离)数据。围绕着人体、物体、空间扫描一圈,就能得到点云图和精准的“1:1”还原的 3D …

踩坑 | vue项目运行后使用require()图片也不显示

文章目录 踩坑 | vue项目运行后使用require()图片也不显示问题描述解决办法1&#xff1a;src属性直接传入地址解决办法2 踩坑 | vue项目运行后使用require()图片也不显示 问题描述 在网上查阅之后&#xff0c;发现结论是在使用vue动态加载图片时&#xff0c;必须使用require。…

B. Sets and Union

题目&#xff1a; 样例&#xff1a; 输入 4 3 3 1 2 3 2 4 5 2 3 4 4 4 1 2 3 4 3 2 5 6 3 3 5 6 3 4 5 6 5 1 1 3 3 6 10 1 9 2 1 3 3 5 8 9 1 2 4 28输出 4 5 6 0 思路&#xff1a; 这里题目的意思是&#xff0c;要求合并尽可能多的集合&#xff0c;使它的集合大小最大&…

Spring面试题23:Spring支持哪些事务管理类型?Spring框架的事务管理有哪些优点?你更倾向用哪种事务管理类型?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring支持哪些事务管理类型? Spring 支持以下几种事务管理类型: 编程式事务管理:通过在代码中显式地使用事务管理 API(如 TransactionTempla…