22天入门设计模式(C++版)
第一、什么是设计模式?
设计模式是软件设计中常见问题的典型解决方案。 它们就像能根据需求进行调整的预制蓝图, 可用于解决代码中反复出现的设计问题。
设计模式与方法或库的使用方式不同, 你很难直接在自己的程序中套用某个设计模式。 模式并不是一段特定的代码, 而是解决特定问题的一般性概念。 你可以根据模式来实现符合自己程序实际所需的解决方案。
人们常常会混淆模式和算法, 因为两者在概念上都是已知特定问题的典型解决方案。 但算法总是明确定义达成特定目标所需的一系列步骤, 而模式则是对解决方案的更高层次描述。 同一模式在两个不同程序中的实现代码可能会不一样。
算法更像是菜谱: 提供达成目标的明确步骤。 而模式更像是蓝图: 你可以看到最终的结果和模式的功能, 但需要自己确定实现步骤。
第二、模式包含哪些内容?
大部分模式都有正规的描述方式, 以便在不同情况下使用。 模式的描述通常会包括以下部分:
- 意图部分简单描述问题和解决方案。
- 动机部分将进一步解释问题并说明模式会如何提供解决方案。
- 结构部分展示模式的每个部分和它们之间的关系。
- 在不同语言中的实现提供流行编程语言的代码, 让读者更好地理解模式背后的思想。
部分模式介绍中还列出其他的一些实用细节, 例如模式的适用性、 实现步骤以及与其他模式的关系。
第三、设计模式分类?
-
创建型模式提供创建对象的机制, 增加已有代码的灵活性和可复用性。
-
结构型模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。
-
行为模式负责对象间的高效沟通和职责委派。
第一、工厂方法模式
(虚拟构造函数、Virtual Constructor、Factory Method)
1.意图
工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
2.工厂方法模式结构
3.工厂方法模式适合应用场景
3.1 当你在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。
3.2 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。
3.3 如果你希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。
4.工厂方法模式实现方式
-
让所有产品都遵循同一接口。 该接口必须声明对所有产品都有意义的方法。
-
在创建类中添加一个空的工厂方法。 该方法的返回类型必须遵循通用的产品接口。
-
在创建者代码中找到对于产品构造函数的所有引用。 将它们依次替换为对于工厂方法的调用, 同时将创建产品的代码移入工厂方法。
你可能需要在工厂方法中添加临时参数来控制返回的产品类型。
工厂方法的代码看上去可能非常糟糕。 其中可能会有复杂的
switch
分支运算符, 用于选择各种需要实例化的产品类。 但是不要担心, 我们很快就会修复这个问题。 -
现在, 为工厂方法中的每种产品编写一个创建者子类, 然后在子类中重写工厂方法, 并将基本方法中的相关创建代码移动到工厂方法中。
-
如果应用中的产品类型太多, 那么为每个产品创建子类并无太大必要, 这时你也可以在子类中复用基类中的控制参数。
例如, 设想你有以下一些层次结构的类。 基类
邮件
及其子类航空邮件
和陆路邮件
; 运输
及其子类飞机
,卡车
和火车
。 航空邮件
仅使用飞机
对象, 而陆路邮件
则会同时使用卡车
和火车
对象。 你可以编写一个新的子类 (例如火车邮件
) 来处理这两种情况, 但是还有其他可选的方案。 客户端代码可以给陆路邮件
类传递一个参数, 用于控制其希望获得的产品。 -
如果代码经过上述移动后, 基础工厂方法中已经没有任何代码, 你可以将其转变为抽象类。 如果基础工厂方法中还有其他语句, 你可以将其设置为该方法的默认行为。
5.C++示例代码
#include <iostream> #include <string> /** * The Product interface declares the operations that all concrete products must * implement. */ class Product { public: virtual ~Product() {} virtual std::string Operation() const = 0; }; /** * Concrete Products provide various implementations of the Product interface. */ class ConcreteProduct1 : public Product { public: std::string Operation() const override { return "{Result of the ConcreteProduct1}"; } }; class ConcreteProduct2 : public Product { public: std::string Operation() const override { return "{Result of the ConcreteProduct2}"; } }; /** * The Creator class declares the factory method that is supposed to return an * object of a Product class. The Creator's subclasses usually provide the * implementation of this method. */ class Creator { /** * Note that the Creator may also provide some default implementation of the * factory method. */ public: virtual ~Creator() {}; virtual Product* FactoryMethod() const = 0; /** * Also note that, despite its name, the Creator's primary responsibility is * not creating products. Usually, it contains some core business logic that * relies on Product objects, returned by the factory method. Subclasses can * indirectly change that business logic by overriding the factory method and * returning a different type of product from it. */ std::string SomeOperation() const { // Call the factory method to create a Product object. Product* product = this->FactoryMethod(); // Now, use the product. std::string result = "Creator: The same creator's code has just worked with " + product->Operation(); delete product; return result; } }; /** * Concrete Creators override the factory method in order to change the * resulting product's type. */ class ConcreteCreator1 : public Creator { /** * Note that the signature of the method still uses the abstract product type, * even though the concrete product is actually returned from the method. This * way the Creator can stay independent of concrete product classes. */ public: Product* FactoryMethod() const override { return new ConcreteProduct1(); } }; class ConcreteCreator2 : public Creator { public: Product* FactoryMethod() const override { return new ConcreteProduct2(); } }; /** * The client code works with an instance of a concrete creator, albeit through * its base interface. As long as the client keeps working with the creator via * the base interface, you can pass it any creator's subclass. */ void ClientCode(const Creator& creator) { // ... std::cout << "Client: I'm not aware of the creator's class, but it still works.\n" << creator.SomeOperation() << std::endl; // ... } /** * The Application picks a creator's type depending on the configuration or * environment. */ int main() { std::cout << "App: Launched with the ConcreteCreator1.\n"; Creator* creator = new ConcreteCreator1(); ClientCode(*creator); std::cout << std::endl; std::cout << "App: Launched with the ConcreteCreator2.\n"; Creator* creator2 = new ConcreteCreator2(); ClientCode(*creator2); delete creator; delete creator2; return 0; }
第二、抽象工厂模式
(Abstract Factory)
1.意图
抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。
3.适合应用场景
3.1 如果代码需要与多个不同系列的相关产品交互, 但是由于无法提前获取相关信息, 或者出于对未来扩展性的考虑, 你不希望代码基于产品的具体类进行构建, 在这种情况下, 你可以使用抽象工厂。
3.2 如果你有一个基于一组抽象方法的类, 且其主要功能因此变得不明确, 那么在这种情况下可以考虑使用抽象工厂模式。
4.实现方式
-
以不同的产品类型与产品变体为维度绘制矩阵。
-
为所有产品声明抽象产品接口。 然后让所有具体产品类实现这些接口。
-
声明抽象工厂接口, 并且在接口中为所有抽象产品提供一组构建方法。
-
为每种产品变体实现一个具体工厂类。
-
在应用程序中开发初始化代码。 该代码根据应用程序配置或当前环境, 对特定具体工厂类进行初始化。 然后将该工厂对象传递给所有需要创建产品的类。
-
找出代码中所有对产品构造函数的直接调用, 将其替换为对工厂对象中相应构建方法的调用。
5.C++ 示例代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * Each distinct product of a product family should have a base interface. All * variants of the product must implement this interface. */ class AbstractProductA { public: virtual ~AbstractProductA(){}; virtual std::string UsefulFunctionA() const = 0; }; /** * Concrete Products are created by corresponding Concrete Factories. */ class ConcreteProductA1 : public AbstractProductA { public: std::string UsefulFunctionA() const override { return "The result of the product A1."; } }; class ConcreteProductA2 : public AbstractProductA { std::string UsefulFunctionA() const override { return "The result of the product A2."; } }; /** * Here's the the base interface of another product. All products can interact * with each other, but proper interaction is possible only between products of * the same concrete variant. */ class AbstractProductB { /** * Product B is able to do its own thing... */ public: virtual ~AbstractProductB(){}; virtual std::string UsefulFunctionB() const = 0; /** * ...but it also can collaborate with the ProductA. * * The Abstract Factory makes sure that all products it creates are of the * same variant and thus, compatible. */ virtual std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const = 0; }; /** * Concrete Products are created by corresponding Concrete Factories. */ class ConcreteProductB1 : public AbstractProductB { public: std::string UsefulFunctionB() const override { return "The result of the product B1."; } /** * The variant, Product B1, is only able to work correctly with the variant, * Product A1. Nevertheless, it accepts any instance of AbstractProductA as an * argument. */ std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override { const std::string result = collaborator.UsefulFunctionA(); return "The result of the B1 collaborating with ( " + result + " )"; } }; class ConcreteProductB2 : public AbstractProductB { public: std::string UsefulFunctionB() const override { return "The result of the product B2."; } /** * The variant, Product B2, is only able to work correctly with the variant, * Product A2. Nevertheless, it accepts any instance of AbstractProductA as an * argument. */ std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override { const std::string result = collaborator.UsefulFunctionA(); return "The result of the B2 collaborating with ( " + result + " )"; } }; /** * The Abstract Factory interface declares a set of methods that return * different abstract products. These products are called a family and are * related by a high-level theme or concept. Products of one family are usually * able to collaborate among themselves. A family of products may have several * variants, but the products of one variant are incompatible with products of * another. */ class AbstractFactory { public: virtual AbstractProductA *CreateProductA() const = 0; virtual AbstractProductB *CreateProductB() const = 0; }; /** * Concrete Factories produce a family of products that belong to a single * variant. The factory guarantees that resulting products are compatible. Note * that signatures of the Concrete Factory's methods return an abstract product, * while inside the method a concrete product is instantiated. */ class ConcreteFactory1 : public AbstractFactory { public: AbstractProductA *CreateProductA() const override { return new ConcreteProductA1(); } AbstractProductB *CreateProductB() const override { return new ConcreteProductB1(); } }; /** * Each Concrete Factory has a corresponding product variant. */ class ConcreteFactory2 : public AbstractFactory { public: AbstractProductA *CreateProductA() const override { return new ConcreteProductA2(); } AbstractProductB *CreateProductB() const override { return new ConcreteProductB2(); } }; /** * The client code works with factories and products only through abstract * types: AbstractFactory and AbstractProduct. This lets you pass any factory or * product subclass to the client code without breaking it. */ void ClientCode(const AbstractFactory &factory) { const AbstractProductA *product_a = factory.CreateProductA(); const AbstractProductB *product_b = factory.CreateProductB(); std::cout << product_b->UsefulFunctionB() << "\n"; std::cout << product_b->AnotherUsefulFunctionB(*product_a) << "\n"; delete product_a; delete product_b; } int main() { std::cout << "Client: Testing client code with the first factory type:\n"; ConcreteFactory1 *f1 = new ConcreteFactory1(); ClientCode(*f1); delete f1; std::cout << std::endl; std::cout << "Client: Testing the same client code with the second factory type:\n"; ConcreteFactory2 *f2 = new ConcreteFactory2(); ClientCode(*f2); delete f2; return 0; }