手机网站维护费wordpress 获取全部评论

当前位置: 首页 > news >正文

手机网站维护费,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设计模式李建忠8 工厂方法Factory MethodMotivation工厂方法定义工厂方法结构 9 抽象工厂Abstract FactoryMotivation抽象工厂定义抽象工厂结构 10 原型模式Prototype原型方法定义原型方法结构原型模式和工厂方法的区别 11 构建器BuilderMotivationBuilder定义Builder结构 12 单例模式Singleton单例模式定义单例模式结构 13 享元模式Flyweight享元模式定义享元模式结构 后记 “对象创建”模式 通过“对象创建”模式绕开new来避免对象创建new过程 中所导致的紧耦合依赖具体类从而支持对象创建的稳定。它是接口抽象之后的第一步工作。 典型模式 ·Factory Method ·Abstract Factory ·Prototype ·Builder 8 工厂方法Factory Method Motivation 在软件系统中经常面临着创建对象的工作由于需求的变化需要创建的对象的具体类型经常变化。 如何应对这种变化如何绕过常规的对象创建方法new提供一种封装机制来避免客户程序和这种具体对象创建工作的紧耦合 代码示例 MainForm方法调用Splitter将东西进行切分有二进制文件切分、txt文件切分、图片文件切分、视频文件切分。 第一版ISplitter * splitter new BinarySplitter();这种写法依赖于具体类违反依赖倒置原则高层模块不应该依赖于低层模块而是应该依赖于抽象。同时抽象不应该依赖于具体实现具体实现应该依赖于抽象。 // 抽象基类 class ISplitter{ public:virtual void split()0;virtual ~ISplitter(){} };class BinarySplitter : public ISplitter{};class TxtSplitter: public ISplitter{};class PictureSplitter: public ISplitter{};class VideoSplitter: public ISplitter{};class MainForm : public Form {TextBox* txtFilePath;TextBox* txtFileNumber;ProgressBar* progressBar;public:void Button1_Click(){ISplitter * splitter new BinarySplitter();//依赖具体类splitter-split();} }; 第二版具体调用工厂MainForm不依赖于具体的类而是依赖于抽象类和工厂基类。 class MainForm : public Form {SplitterFactory* factory;//工厂public:MainForm(SplitterFactory* factory){this-factoryfactory;}void Button1_Click(){ISplitter * splitterfactory-CreateSplitter(); //多态newsplitter-split();} }; ISplitterFactory //抽象类 class ISplitter{ public:virtual void split()0;virtual ~ISplitter(){} };//工厂基类 class SplitterFactory{ public:virtual ISplitter* CreateSplitter()0; // 创建对象virtual ~SplitterFactory(){} }; FileSplitter文件:几个具体的工厂继承自SplitterFactory实现虚函数实现具体类的创建 //具体类 class BinarySplitter : public ISplitter{};class TxtSplitter: public ISplitter{};class PictureSplitter: public ISplitter{};class VideoSplitter: public ISplitter{};//具体工厂 class BinarySplitterFactory: public SplitterFactory{ public:virtual ISplitter* CreateSplitter(){return new BinarySplitter();} };class TxtSplitterFactory: public SplitterFactory{ public:virtual ISplitter* CreateSplitter(){return new TxtSplitter();} };class PictureSplitterFactory: public SplitterFactory{ public:virtual ISplitter* CreateSplitter(){return new PictureSplitter();} };class VideoSplitterFactory: public SplitterFactory{ public:virtual ISplitter* CreateSplitter(){return new VideoSplitter();} }; 工厂方法定义 定义一个用于创建对象的接口让子类决定实例化哪一个类。 Factory Method使一个类的实例化延迟到其子类。 具体到上面的代码用于创建对象的接口SplitterFactory让子类BinarySplitterFactoryTxtSplitterFactory等具体工厂决定实例化哪一个类。 在下列情况下可以使用Factory Method模式 • 当一个类不知道它所必须创建的对象的类的时候。 • 当一个类希望由它的子类来指定它所创建的对象的时候。 • 当类将创建对象的职责委托给多个帮助子类中的某一个并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate. 工厂方法结构 参与者 • Product(ISplitter) — 定义工厂方法所创建的对象的接口。 • ConcreteProduct(BinarySplitterTxtSplitterPictureSplitterVideoSplitter) — 实现Product接口。 • Creator(SplitterFactory) — 声明工厂方法该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现它返回一个缺省的ConcreteProduct对象。 — 可以调用工厂方法以创建一个Product对象。 • ConcreteCreator(BinarySplitterFactoryTxtSplitterFactoryPictureSplitterFactoryVideoSplitterFactory) — 重定义工厂方法以返回一个ConcreteProduct实例。 工厂方法总结 Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型紧耦合关系new会导致软件的脆弱。 Factory Method模式通过面向对象多态的手法将所要创建的具体对象工作延迟到子类从而实现一种扩展而非更改的策略较好地解决了这种紧耦合关系。 Factory Method模式解决单个对象的需求变化缺点在于创建方法或者传入的参数要相同。 9 抽象工厂Abstract Factory Motivation 在软件系统中经常面临着一系列相互依赖的对象的创建工作同时由于需求的变化往往存在更多系列对象的创建工作。如何应对这种变化如何绕过常规的对象创建方法new提供一种封装机制来避免客户程序和这种多系列具体对象创建工作的紧耦合 代码示例 创建数据访问层支持多种数据库 第一版:不好的设计绑定到具体的类 class EmployeeDAO{public:vectorEmployeeDO GetEmployees(){SqlConnection* connection new SqlConnection();connection-ConnectionString …;SqlCommand* command new SqlCommand();command-CommandText…;command-SetConnection(connection);SqlDataReader* reader command-ExecuteReader();while (reader-Read()){}} }; 第二版使用工厂方法但是会遇到问题使用的时候工厂之间有关联性必须是同一系列的比如都是oracle数据库的连接、命令、datareader等不能是mysql的连接、orable的命令这种交叉使用。 // 三个对象必须是同系列同组有关联性IDBConnectionFactory* dbConnectionFactory;IDBCommandFactory* dbCommandFactory;IDataReaderFactory* dataReaderFactory;工厂方法的代码如下 //数据库访问有关的基类 class IDBConnection{}; class IDBConnectionFactory{ public:virtual IDBConnection* CreateDBConnection()0; };class IDBCommand{}; class IDBCommandFactory{ public:virtual IDBCommand* CreateDBCommand()0; };class IDataReader{}; class IDataReaderFactory{ public:virtual IDataReader* CreateDataReader()0; };//支持SQL Server class SqlConnection: public IDBConnection{}; class SqlConnectionFactory:public IDBConnectionFactory{};class SqlCommand: public IDBCommand{}; class SqlCommandFactory:public IDBCommandFactory{};class SqlDataReader: public IDataReader{}; class SqlDataReaderFactory:public IDataReaderFactory{};//支持Oracle class OracleConnection: public IDBConnection{};class OracleCommand: public IDBCommand{};class OracleDataReader: public IDataReader{};class EmployeeDAO{// 三个对象必须是同系列同组有关联性IDBConnectionFactory* dbConnectionFactory;IDBCommandFactory* dbCommandFactory;IDataReaderFactory* dataReaderFactory;public:vectorEmployeeDO GetEmployees(){IDBConnection* connection dbConnectionFactory-CreateDBConnection();connection-ConnectionString(…);IDBCommand* command dbCommandFactory-CreateDBCommand();command-CommandText(…);command-SetConnection(connection); //关联性IDBDataReader* reader command-ExecuteReader(); //关联性while (reader-Read()){}} }; 第三版有关联性的创建行为放在一起合成一个工厂这就是抽象工厂Abstract Factory //数据库访问有关的基类 class IDBConnection{};class IDBCommand{};class IDataReader{};// 创建一个工厂有关联性的放在一起 class IDBFactory{ public:virtual IDBConnection* CreateDBConnection()0;virtual IDBCommand* CreateDBCommand()0;virtual IDataReader* CreateDataReader()0;};//支持SQL Server class SqlConnection: public IDBConnection{}; class SqlCommand: public IDBCommand{}; class SqlDataReader: public IDataReader{};class SqlDBFactory:public IDBFactory{ public:virtual IDBConnection* CreateDBConnection()0;virtual IDBCommand* CreateDBCommand()0;virtual IDataReader* CreateDataReader()0;};//支持Oracle class OracleConnection: public IDBConnection{};class OracleCommand: public IDBCommand{};class OracleDataReader: public IDataReader{};class EmployeeDAO{IDBFactory* dbFactory;public:vectorEmployeeDO GetEmployees(){IDBConnection* connection dbFactory-CreateDBConnection();connection-ConnectionString(…);IDBCommand* command dbFactory-CreateDBCommand();command-CommandText(…);command-SetConnection(connection); //关联性IDBDataReader* reader command-ExecuteReader(); //关联性while (reader-Read()){}} }; 抽象工厂定义 提供一个创建一系列相关或相互依赖对象的接口而无需指定它们具体的类。 抽象工厂结构 参与者 • AbstractFactory (IDBFactory) — 声明一个创建抽象产品对象的操作接口。 • ConcreteFactory (SqlDBFactoryOracleDBFactory等) — 实现创建具体产品对象的操作。 • AbstractProduct (IDBConnectionIDBCommand等) — 为一类产品对象声明一个接口。 • ConcreteProduct (SqlConnection, OracleCommand等) — 定义一个将被相应的具体工厂创建的产品对象。实现AbstractProduct接口。 • Client — 仅使用由AbstractFactory和AbstractProduct类声明的接口。 协作 • 通常在运行时刻创建一个ConcreteFactory类的实例。这一具体的工厂创建具有特定实现的产品对象。为创建不同的产品对象客户应使用不同的具体工厂。 • AbstractFactory将产品对象的创建延迟到它的ConcreteFactory子类。 要点总结 如果没有应对多系列对象构建的需求变化则没有必要使用Abstract Factory模式这时候使用简单的工厂即可。 系列对象指的是在某一特定系列下的对象之间有相互依赖或相互作用的关系。不同系列的对象之间不能相互依赖。 10 原型模式Prototype Motivation 在软件系统中经常面临着某些结构复杂的对象的创建由于需求的变化这些对象经常面临着剧烈的变化但是它们却拥有比较稳定一致的接口。 如何应对这种变化如何向客户程序隔离出这些易变对象从而使得依赖这些易变对象的客户程序不随着需求改变而改变 代码示例 //抽象类 class ISplitter{ public:virtual void split()0;virtual ISplitter* clone()0; //通过克隆自己来创建对象virtual ~ISplitter(){}};//具体类 class BinarySplitter : public ISplitter{ public:virtual ISplitter* clone(){return new BinarySplitter(this);} };class TxtSplitter: public ISplitter{ public:virtual ISplitter clone(){return new TxtSplitter(this);} };class PictureSplitter: public ISplitter{ public:virtual ISplitter clone(){return new PictureSplitter(this);} };class VideoSplitter: public ISplitter{ public:virtual ISplitter clone(){return new VideoSplitter(this);} }; Client怎么用原型方法 class MainForm : public Form {ISplitter prototype;//原型对象public:MainForm(ISplitter* prototype){this-prototypeprototype;}void Button1_Click(){ISplitter * splitterprototype-clone(); //克隆原型splitter-split();} }; 原型方法定义 用原型实例指定创建对象的种类并且通过拷贝这些原型创建新的对象。 原型方法结构 参与者 • PrototypeISplitter — 声明一个克隆自身的接口。 • ConcretePrototypeBinarySplitter、TxtSplitter、PictureSplitter等 — 实现一个克隆自身的操作。 • ClientMainForm — 让一个原型克隆自身从而创建一个新的对象。 再给出chatGPT生成的原型方法的示例 原型方法Prototype Pattern是一种创建型设计模式其主要目的是通过复制现有对象来创建新对象而不是通过实例化类。这样做可以避免直接使用构造函数创建对象提高对象的创建效率同时也使得对象的创建更加灵活。原型方法通常包含一个克隆方法用于复制对象。 以下是一个简单的C代码示例演示了原型方法的实现 #include iostream #include string// 抽象原型类 class Prototype { public:virtual Prototype* clone() const 0;virtual void printInfo() const 0;virtual ~Prototype() {} };// 具体原型类 A class ConcretePrototypeA : public Prototype { public:Prototype* clone() const override {return new ConcretePrototypeA(this);}void printInfo() const override {std::cout Concrete Prototype A std::endl;} };// 具体原型类 B class ConcretePrototypeB : public Prototype { public:Prototype clone() const override {return new ConcretePrototypeB(this);}void printInfo() const override {std::cout Concrete Prototype B std::endl;} };int main() {// 创建原型对象Prototype prototypeA new ConcretePrototypeA();Prototype* prototypeB new ConcretePrototypeB();// 克隆对象Prototype* cloneA prototypeA-clone();Prototype* cloneB prototypeB-clone();// 打印信息prototypeA-printInfo();cloneA-printInfo();prototypeB-printInfo();cloneB-printInfo();// 释放资源delete prototypeA;delete cloneA;delete prototypeB;delete cloneB;return 0; }在上述示例中Prototype 是抽象原型类其中包含了 clone 和 printInfo 纯虚函数。ConcretePrototypeA 和 ConcretePrototypeB 是具体的原型类分别实现了这两个函数。在 main 函数中通过创建原型对象然后克隆对象最后打印信息展示了原型方法的基本用法。 适用性 原型方法适用于那些需要频繁创建对象且对象的创建成本较高或创建过程复杂的情况。通过克隆现有对象来创建新对象可以提高效率、简化代码并且灵活应对动态配置和变化。 原型模式和工厂方法的区别 创建方式 原型模式通过克隆现有对象来创建新对象。工厂方法模式通过实现工厂方法来由子类决定创建的具体产品。 关注点 原型模式关注对象的复制。工厂方法模式关注对象的创建。 灵活性 原型模式更加灵活允许在运行时动态选择复制的对象。工厂方法模式更加固定创建的对象由具体的子类工厂决定。
在实际应用中选择使用哪种模式取决于需求和设计目标。原型模式更适合对象创建时变化不大、但需要频繁创建的情况而工厂方法模式更适合在创建对象的类层次结构变化频繁时。 11 构建器Builder Motivation 在软件系统中有时候面临着“一个复杂对象”的创建工作其通常由各个部分的子对象用一定的算法构成由于需求的变化这个复杂对象的各个部分经常面临着剧烈的变化但是将它们组合在一起的算法却相对稳定。 如何应对这种变化如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化从而保持系统中的“稳定构建算法”不随着需求改变而改变 代码示例建立各种房子比如砖瓦房高楼别墅等房子 class House{//…. };class HouseBuilder { public:House* GetResult(){return pHouse;}virtual ~HouseBuilder(){} protected:House* pHouse;virtual void BuildPart1()0;virtual void BuildPart2()0;virtual void BuildPart3()0;virtual void BuildPart4()0;virtual void BuildPart5()0;};class StoneHouse: public House{};// 石头房子的创建override HouseBuilder里面的流程 class StoneHouseBuilder: public HouseBuilder{ protected:virtual void BuildPart1(){//pHouse-Part1 …;}virtual void BuildPart2(){}virtual void BuildPart3(){}virtual void BuildPart4(){}virtual void BuildPart5(){}};// 房子具体构建过程 class HouseDirector{public:// 有一个HouseBuilder的指针HouseBuilder* pHouseBuilder;HouseDirector(HouseBuilder* pHouseBuilder){this-pHouseBuilderpHouseBuilder;}House* Construct(){ // 整个构建流程是稳定的pHouseBuilder-BuildPart1();for (int i 0; i 4; i){pHouseBuilder-BuildPart2();}bool flagpHouseBuilder-BuildPart3();if(flag){pHouseBuilder-BuildPart4();}pHouseBuilder-BuildPart5();return pHouseBuilder-GetResult();} };int main() {// 创建石头房子的建造者StoneHouseBuilder stoneHouseBuilder;// 创建房子Director并指定建造者HouseDirector houseDirector(stoneHouseBuilder);// 构建房子House* stoneHouse houseDirector.Construct();// 输出结果if (stoneHouse ! nullptr) {// 假设 House 类有适当的输出方法// 这里只是简单示例实际使用时需要根据具体类的实现调用相应的方法std::cout Stone House constructed. std::endl;} else {std::cout Failed to construct Stone House. std::endl;}// 释放资源delete stoneHouse;return 0; } 上述代码涉及了Builder设计模式包括以下几个类 House 类: 表示待构建的产品即房子。在示例中StoneHouse 类是 House 的具体实现。 HouseBuilder 抽象类: 定义了构建产品房子的抽象接口。具体的建造者例如 StoneHouseBuilder需要继承这个抽象类并实现接口中的方法以完成具体产品的构建。 StoneHouseBuilder 类: 是 HouseBuilder 的具体实现负责构建石头房子。它通过实现抽象接口中的方法来完成具体的建造流程。 HouseDirector 类: 起到指导建造的作用通过构造函数接收一个具体的建造者对象。通过调用建造者的方法按照一定的构建流程组织建造者完成产品的构建。 main 函数: 在 main 函数中创建了 StoneHouseBuilder 的实例然后创建了一个 HouseDirector 的实例并将建造者传递给导演。通过导演的 Construct 方法按照一定的构建流程构建了石头房子并输出结果。
总体而言Builder设计模式的目的是将一个复杂对象的构建与它的表示分离使得同样的构建过程可以创建不同的表示。在这个示例中HouseBuilder 抽象类定义了构建房子的接口StoneHouseBuilder 实现了具体的石头房子构建流程而 HouseDirector 则负责协调建造者完成整个构建流程。 Builder定义 将一个复杂对象的构建与它的表示分离使得同样的构建过程可以创建不同的表示。 Builder结构 参与者 • BuilderHouseBuilder — 为创建一个Product对象的各个部件指定抽象接口。 • ConcreteBuilderStoneHouseBuilder等 — 实现Builder的接口以构造和装配该产品的各个部件。 — 定义并明确它所创建的表示。 — 提供一个检索产品的接口。 • DirectorHouseDirector — 构造一个使用Builder接口的对象。 • Product这里没表现出来 — 表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程。 — 包含定义组成部件的类包括将这些部件装配成最终产品的接口。 Builder要点总结 Builder 模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个稳定的算法而复杂对象的各个部分则经常变化。 变化点在哪里封装哪里—— Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。 在Builder模式中要注意不同语言中构造器内调用虚函数的差别C vs. C#) 。 chatGPT给出的builder的例子 以下是一个简单的C示例代码演示Builder设计模式 #include iostream #include string// 产品类 class Product { public:void AddPart(const std::string part) {parts part ;}void Show() const {std::cout Product Parts: parts std::endl;}private:std::string parts; };// 抽象建造者类 class Builder { public:virtual void BuildPart1() 0;virtual void BuildPart2() 0;virtual Product* GetResult() 0; };// 具体建造者类 class ConcreteBuilder : public Builder { public:ConcreteBuilder() {product new Product();}void BuildPart1() override {product-AddPart(Part1);}void BuildPart2() override {product-AddPart(Part2);}Product* GetResult() override {return product;}private:Product* product; };// 指导者类 class Director { public:Director(Builder* builder) : builder(builder) {}void Construct() {builder-BuildPart1();builder-BuildPart2();}private:Builder* builder; };int main() {// 创建具体建造者ConcreteBuilder concreteBuilder;// 创建指导者并传入具体建造者Director director(concreteBuilder);// 指导者构建产品director.Construct();// 获取最终产品Product* product concreteBuilder.GetResult();// 展示产品if (product ! nullptr) {product-Show();delete product;}return 0; }在这个示例中Product 表示最终构建的复杂对象Builder 是抽象建造者类ConcreteBuilder 是具体建造者类Director 是指导者类。通过指导者调用具体建造者的方法最终得到构建完成的产品。这个例子中的产品是简单的包含两个部分的字符串实际应用中可以根据需求定义更复杂的产品结构。 “对象性能”模式 面向对象很好地解决了“抽象”的问题但是必不可免地要付出 一定的代价。对于通常情况来讲面向对象的成本大都可以忽略不计。但是某些情况面向对象所带来的成本必须谨慎处理。 典型模式 ·Singleton ·Flyweight 12 单例模式Singleton Motivation 在软件系统中经常有一些特殊的类必须保证它们在系统中只存在一个实例才能确保它们的逻辑正确性以及良好的效率。 如何绕过常规的构造器提供一种机制来保证一个类只有一个实例 这应该是类设计者的责任而不是使用者的责任。 单例模式代码举例把构造函数设置为private class Singleton{ private:Singleton();Singleton(const Singleton other); public:static Singleton* getInstance();static Singleton* m_instance; };Singleton* Singleton::m_instancenullptr;//线程非安全版本 Singleton* Singleton::getInstance() {if (m_instance nullptr) {m_instance new Singleton();}return m_instance; }//线程安全版本但锁的代价过高 Singleton* Singleton::getInstance() {Lock lock;if (m_instance nullptr) {m_instance new Singleton();}return m_instance; }//双检查锁但由于内存读写reorder不安全 Singleton* Singleton::getInstance() {if(m_instancenullptr){Lock lock;if (m_instance nullptr) {m_instance new Singleton();}}return m_instance; }//C 11版本之后的跨平台实现 (volatile) std::atomicSingleton* Singleton::m_instance; std::mutex Singleton::m_mutex;Singleton* Singleton::getInstance() {Singleton* tmp m_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acquire);//获取内存fenceif (tmp nullptr) {std::lock_guardstd::mutex lock(m_mutex);tmp m_instance.load(std::memory_order_relaxed);if (tmp nullptr) {tmp new Singleton;std::atomic_thread_fence(std::memory_order_release);//释放内存fencem_instance.store(tmp, std::memory_order_relaxed);}}return tmp; } 单例模式定义 保证一个类仅有一个实例并提供一个访问它的全局访问点。 单例模式结构 参与者 • Singleton — 定义一个 Instance 操作允许客户访问它的唯一实例。 Instance 是一个类操作即 Smalltalk 中的一个类方法和 C 中的一个静态成员函数。 — 可能负责创建它自己的唯一实例。 协作 • 客户只能通过 Singleton 的 Instance 操作访问一个 Singleton 的实例。 下面是对单例模式双检查锁的介绍 在单例模式中使用双检查锁(DCLDouble-Checked Locking)时可能会遇到一些问题特别是在多线程环境下。在C11之前的版本中由于缺少内存栅栏(memory barrier)的支持可能导致DCL失效从而造成线程安全性问题。 主要问题包括 内存写入和读取的重排序问题 在一些编译器和硬件架构中对于指令的执行顺序可能会发生重排序导致在实例化对象时成员变量的内存分配可能在对象构造之前从而使其他线程在获取到非空但尚未构造完全的对象。 缺乏内存栅栏 在C11之前C标准库并没有提供内存栅栏的概念而DCL依赖于内存栅栏来保证写入和读取的顺序性。缺少内存栅栏可能导致指令乱序执行使得双检查锁失效。 编译器优化问题 一些编译器可能会对代码进行优化可能会在保证正确性的情况下对代码进行调整从而导致DCL失效。
在C11及以后的版本中引入了std::atomic等特性可以更好地保证内存可见性和顺序性从而避免了上述问题。因此如果在C11及以后的环境中使用std::atomic可以解决DCL的一些问题。 // Singleton.h #pragma once#include atomic #include mutexclass Singleton { public:// 获取单例实例的静态方法static Singleton* Instance();private:// 私有构造函数确保单例不能通过其他方式实例化Singleton() {}private:// 使用std::atomic提供的原子操作确保多线程环境下的安全性static std::atomicSingleton* m_instance;// 用于保护实例化过程的互斥锁static std::mutex m_mutex; };// Singleton.cpp #include Singleton.h// 初始化静态成员变量 std::atomicSingleton* Singleton::m_instance(nullptr); std::mutex Singleton::m_mutex;Singleton* Singleton::Instance() {// 使用std::atomic加载实例确保内存可见性Singleton* tmp m_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acquire);if (tmp nullptr) {// 使用互斥锁保护实例化过程std::lock_guardstd::mutex lock(m_mutex);tmp m_instance.load(std::memory_order_relaxed);if (tmp nullptr) {// 实例化单例对象tmp new Singleton;// 使用内存顺序和std::atomic_thread_fence确保内存可见性和顺序性std::atomic_thread_fence(std::memory_order_release);// 存储单例对象的指针m_instance.store(tmp, std::memory_order_relaxed);}}return tmp; } 这里使用std::atomic提供的内存顺序和std::atomic_thread_fence来保证正确的内存可见性和顺序性。在C11及以后的环境中这种方式是相对安全的。 13 享元模式Flyweight Motivation 在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中从而带来很高的运行时代价——主要指内存需求方面的代价。 如何在避免大量细粒度对象问题的同时让外部客户程序仍然能够透明地适用面向对象的方式来进行操作 代码举例创建字体对象时同一个key的字体对象只有一个而不是有很多 // Font.h #pragma once#include stringclass Font { private:// 字体对象的唯一标识符std::string key;// 字体对象的状态可能包含其他属性这里未具体展示// ….public:// 构造函数根据传入的 key 初始化字体对象Font(const std::string key) : key(key) {// …} };// FontFactory.h #pragma once#include map #include Font.hclass FontFactory { private:// 存储已创建的字体对象key 是字体对象的唯一标识符value 是对应的字体对象指针std::mapstd::string, Font* fontPool;public:// 获取字体对象Font* GetFont(const std::string key);// 清理字体池void clear(); };// FontFactory.cpp #include FontFactory.hFont* FontFactory::GetFont(const std::string key) {// 查找字体池中是否已存在对应 key 的字体对象auto iter fontPool.find(key);if (iter ! fontPool.end()) {// 如果存在则直接返回已有的字体对象return iter-second;} else {// 如果不存在则创建新的字体对象并加入字体池Font* font new Font(key);fontPool[key] font;return font;} }void FontFactory::clear() {// 清理字体池中的所有字体对象for (auto entry : fontPool) {delete entry.second;}fontPool.clear(); } 享元模式定义 运用共享技术有效地支持大量细粒度的对象。 享元模式结构 参与者 • Flyweight — 描述一个接口通过这个接口 flyweight 可以接受并作用于外部状态。 • ConcreteFlyweight — 实现 Flyweight 接口并为内部状态如果有的话增加存储空间。 ConcreteFlyweight 对象必须是可共享的。它所存储的状态必须是内部的即它必须独立于 ConcreteFlyweight 对象的场景。 • UnsharedConcreteFlyweight — 并非所有的 Flyweight 子类都需要被共享。 Flyweight 接口使共享成为可能但它并不强制共享。在 Flyweight 对象结构的某些层次 UnsharedConcreteFlyweight 对象通常将 ConcreteFlyweight 对象作为子节点。 • FlyweightFactory — 创建并管理 flyweight 对象。 — 确保合理地共享 flyweight。当用户请求一个 flyweight 时 FlyweightFactory 对象提供一个已创建的实例或者创建一个如果不存在的话。 • Client — 维持一个对 flyweight 的引用。 — 计算或存储一个多个 flyweight 的外部状态。 下图说明如何共享flyweight chatGPT给出的享元模式的例子 以下是一个简单的 C 代码例子演示了享元模式的实现。这个例子以文本编辑器中的字符格式化为场景。 #include iostream #include map// 享元接口 class CharacterFormat { public:virtual void applyFormat() const 0; };// 具体享元类代表不同的字符格式 class BoldFormat : public CharacterFormat { public:void applyFormat() const override {std::cout Applying Bold Format\n;} };class ItalicFormat : public CharacterFormat { public:void applyFormat() const override {std::cout Applying Italic Format\n;} };class UnderlineFormat : public CharacterFormat { public:void applyFormat() const override {std::cout Applying Underline Format\n;} };// 享元工厂类负责创建和管理享元对象 class CharacterFormatFactory { private:std::mapstd::string, CharacterFormat* formatPool;public:CharacterFormat* getFormat(const std::string key) {auto iter formatPool.find(key);if (iter ! formatPool.end()) {return iter-second;} else {CharacterFormat* format nullptr;if (key Bold) {format new BoldFormat();} else if (key Italic) {format new ItalicFormat();} else if (key Underline) {format new UnderlineFormat();}formatPool[key] format;return format;}} };// 客户端代码 int main() {CharacterFormatFactory formatFactory;// 用户输入的字符格式std::string userInput Bold;// 通过享元工厂获取或创建字符格式对象CharacterFormat* format formatFactory.getFormat(userInput);// 应用字符格式if (format) {format-applyFormat();} else {std::cout Invalid Format\n;}// 释放资源delete format;return 0; }在这个例子中CharacterFormat 是享元接口而 BoldFormat、ItalicFormat、UnderlineFormat 是具体的享元类。CharacterFormatFactory 是享元工厂类负责创建和管理享元对象。在客户端代码中用户输入字符格式的信息通过享元工厂获取或创建相应的字符格式对象并应用该格式。这样相同的字符格式对象可以被多次共享减少了资源的重复创建。 要点总结 面向对象很好地解决了抽象性的问题但是作为一个运行在机器 中的程序实体我们需要考虑对象的代价问题。Flyweight主要解 决面向对象的代价问题一般不触及面向对象的抽象性问题。 Flyweight采用对象共享的做法来降低系统中对象的个数从而降 低细粒度对象给系统带来的内存压力。在具体实现方面要注意对 象状态的处理。 对象的数量太大从而导致对象内存开销加大——什么样的数量才 算大这需要我们仔细的根据具体应用情况进行评估而不能凭空 臆断。 后记 截至2024年1月17日14点48分学习了Factory Method, Abstract Factory, Prototype, Builder, Singleton, Flyweight模式。下午继续学习。