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

C++设计模式——Factory Method工厂方法模式

一,工厂方法模式的定义

工厂方法模式是一种创建型设计模式,它提供了一种创建对象的方法,而无需指定具体的类,也不需要直接指定对象的类型。

工厂方法模式抽象了对象的创建过程,使得客户端只需要通过指定具体的参数,而无需关心对象的创建细节。

工厂方法模式将创建对象的逻辑封装在一个类(工厂类)中,它提供了一种通过调用工厂类的方法来创建对象的方式,而无需直接使用对象的构造函数,外部客户端通过调用工厂类的方法来获取所需的对象。

由于工厂方法模式将创建对象的逻辑封装在了一个工厂类中,这种设计可以让对象的创建与使用的解耦,提高了代码的灵活性和可维护性。

工厂方法模式在现实生活中的抽象实例:

餐厅点餐:餐厅根据顾客的下单来订做相应的食物。

汽车制造:汽车厂商通过固定的工厂流水线生产不同型号和外观的汽车。

服装定制:顾客在服装店通过选择设计、尺寸和面料等要素来定制自己的服装。

二,工厂方法模式的结构

工厂方法模式主要包含以下组件:

1.抽象工厂(Factory):

定义创建产品的接口,包含创建产品对象的抽象方法。

2.具体工厂(Concrete Factory):

是抽象工厂的具体实现,负责创建具体的产品对象,具体工厂根据调用场景来决定实例化哪一个具体产品类,并返回该产品的实例。

3.抽象产品(Product):

定义了产品的主要特性和功能。

4.具体产品(Concrete Product):

是抽象产品的具体实现。

组件之间的工作步骤如下:

1.客户端通过调用具体工厂的方法来创建产品。客户端并不知道具体的产品类,只知道抽象接口。

2.具体工厂接收到客户端的请求后,根据一定的条件来决定实例化哪一类具体产品,并调用具体产品类的构造函数。

3.具体产品类实例化后,将产品返回给客户端使用。

对应UML类图:

三,工厂方法模式代码样例

#include <iostream>

class Product {
public:
    virtual void use() = 0;
};

class ConcreteProductA: public Product{
public:
    void use() {
        std::cout << "Using ConcreteProductA" << std::endl;
    }
};

class ConcreteProductB: public Product{
public:
    void use() {
        std::cout << "Using ConcreteProductB" << std::endl;
    }
};

class Factory{
public:
    virtual Product* createProduct() = 0;
};

class ConcreteFactoryA: public Factory{
public:
    Product* createProduct() {
        return new ConcreteProductA();
    }
};

class ConcreteFactoryB: public Factory{
public:
    Product* createProduct() {
        return new ConcreteProductB();
    }
};

int main() {
    Factory* factoryA = new ConcreteFactoryA();
    Product* productA = factoryA->createProduct();
    productA->use();

    Factory* factoryB = new ConcreteFactoryB();
    Product* productB = factoryB->createProduct();
    productB->use();

    delete factoryA;
    delete productA;
    delete factoryB;
    delete productB;
    return 0;
}

运行结果:

Using ConcreteProductA
Using ConcreteProductB

四,工厂方法模式的优缺点

工厂方法模式的优点:

将对象的创建与使用分离,客户端无需知道具体的产品类,也不需要知道具体产品的实现细节。

降低了客户端和具体产品类之间的耦合度。

如果需要更换具体产品类,只需要修改工厂类的实现即可,而不需要修改客户端的代码。

可以在工厂类中实现对象的生命周期管理和对象池等功能。

可以在工厂类中实现对象的复用,如果多个客户端需要同一个对象,可以通过工厂类实现对象的共享,避免重复创建对象。

工厂方法模式的缺点:

引入工厂类之后,系统会多出一个类,增加了代码的复杂度和理解难度。

需要额外的工厂类来创建对象,会带来一定的性能开销。

只适用于创建具有相同接口或基类的对象,如果需要创建具有不同接口的对象,该模式不适用。

五,代码实战

Demo1:

#include <iostream>

class Product {
public:
    virtual ~Product() {}
    virtual void operation() = 0;
};

class Factory {
public:
    virtual ~Factory() {}
    virtual Product* createProduct() = 0;
};

class ProductA : public Product {
public:
    void operation() override {
        std::cout << "Performing Operation A..." << std::endl;
    }
};

class ProductB : public Product {
public:
    void operation() override {
        std::cout << "Performing Operation B..." << std::endl;
    }
};

class FactoryA : public Factory {
public:
    Product* createProduct() override { return new ProductA(); }
};

class FactoryB : public Factory {
public:
    Product* createProduct() override { return new ProductB(); }
};

int main() {
    Factory* factoryA = new FactoryA();
    Product* productA = factoryA->createProduct();
    productA->operation();
    Factory* factoryB = new FactoryB();
    Product* productB = factoryB->createProduct();
    productB->operation();
    return 0;
}

运行结果:

Performing Operation A...
Performing Operation B...

Demo2:

#include <iostream>
#include <string>
#include <list>

using namespace std;

//Abstract Product
class Page
{
public:
    virtual string GetPageName(void) = 0;
};

//Concrete Product
class SkillsPage : public Page
{
public:
    string GetPageName(void)
    {
        return "SkillsPage";
    }
};

//Concrete Product
class EducationPage : public Page
{
public:
    string GetPageName(void)
    {
        return "EducationPage";
    }
};

//Concrete Product
class ExperiencePage : public Page
{
public:
    string GetPageName(void)
    {
        return "ExperiencePage";
    }
};

//Concrete Product
class IntroductionPage : public Page
{
public:
    string GetPageName(void)
    {
        return "IntroductionPage";
    }
};

//Concrete Product
class ResultsPage : public Page
{
public:
    string GetPageName(void)
    {
        return "ResultsPage";
    }
};

//Concrete Product
class ConclusionPage : public Page
{
public:
    string GetPageName(void)
    {
        return "ConclusionPage";
    }
};

//Concrete Product
class SummaryPage : public Page
{
public:
    string GetPageName(void)
    {
        return "SummaryPage";
    }
};

//Concrete Product
class BibliographyPage : public Page
{
public:
    string GetPageName(void)
    {
        return "BibliographyPage";
    }
};

//Abstract Factory
class Document
{
public:
    Document()
    {
    }
    void AddPages(Page* page)
    {
        pages_.push_back(page);
    }
    const list<Page*>& GetPages(void)
    {
        return pages_;
    }
    //Factory Method
    virtual void CreatePages(void) = 0;
private:
    list<Page*> pages_;
};

//Concrete Factory
class Resume : public Document
{
public:
    Resume()
    {
        CreatePages();
    }
    void CreatePages(void)
    {
        AddPages(new SkillsPage());
        AddPages(new EducationPage());
        AddPages(new ExperiencePage());
    }
};

//Concrete Factory
class Report : public Document
{
public:
    Report()
    {
        CreatePages();
    }
    void CreatePages(void)
    {
        AddPages(new SummaryPage());
        AddPages(new IntroductionPage());
        AddPages(new ResultsPage());
        AddPages(new ConclusionPage());
        AddPages(new BibliographyPage());
    }
};

int main()
{
    Document* doc1 = new Resume();
    Document* doc2 = new Report();
    //Get and print the pages of the first document
    list<Page*>& doc1Pages = const_cast<list<Page*>&> (doc1->GetPages());
    cout << "\nResume Pages -------------" << endl;
    for (list<Page*>::iterator it = doc1Pages.begin(); it != doc1Pages.end(); it++)
    {
        cout << "\t" << (*it)->GetPageName() << endl;
    }
    //Get and print the pages of the second document
    list<Page*>& doc2Pages = const_cast<list<Page*>&> (doc2->GetPages());
    cout << "\nReport Pages -------------" << endl;
    for (list<Page*>::iterator it = doc2Pages.begin(); it != doc2Pages.end(); it++)
    {
        cout << "\t" << (*it)->GetPageName() << endl;
    }
    return 0;
}

运行结果:


Resume Pages -------------
        SkillsPage
        EducationPage
        ExperiencePage


Report Pages -------------
        SummaryPage
        IntroductionPage
        ResultsPage
        ConclusionPage
        BibliographyPage

六,参考阅读

https://www.codeproject.com/Articles/570183/Factory-Method-Pattern

https://artofcoding.dev/design-patterns-factory-and-factory-method

https://sourcemaking.com/design_patterns/factory_method

https://www.geeksforgeeks.org/factory-method-for-designing-pattern/

https://advancedcppwithexamples.blogspot.com/2010/08/c-example-for-factory-method-design.html


http://www.kler.cn/news/368066.html

相关文章:

  • 什么是 VolTE 中的 Slient Redial?它和 CSFB 什么关系?
  • 基于huggingface训练数据处理(load_dataset、map、data_loader等内容)
  • Qt:QtCreator使用
  • Ajax:XMLHttpRequest
  • 逻辑回归与神经网络
  • 15分钟学 Go 小项目:Web API
  • C#文件内容检索的功能
  • P11232 [CSP-S 2024] 超速检测(民间数据)
  • ES6:let和const命令解读以及变量的解构赋值
  • PostgreSQL(十三)pgcrypto 扩展实现 AES、PGP 加密,并自定义存储过程
  • Flink CDC系列之:学习理解核心概念——Transform
  • Elasticsearch 解析:倒排索引机制/字段类型/语法/常见问题
  • 双击热备和负载均衡的区别
  • 头歌数据库实验 MySQL
  • Redis 哨兵 总结
  • Vue3学习:番茄钟案例的实现及打包遇到的几个问题
  • Python 自动化运维:Python基础知识
  • Vuejs设计与实现 — 渲染器核心:挂载与更新
  • 【C++单调栈 贡献法】907. 子数组的最小值之和|1975
  • 闯关leetcode——171. Excel Sheet Column Number
  • Unity3D 自动化资源打AB包详解
  • Vue项目GET请求正常,POST请求却失效?揭秘Mock服务背后的故事
  • hass docker openwrt配置
  • C++,STL 050(24.10.27)
  • 【uni-app学习-2】
  • Golang | Leetcode Golang题解之第504题七进制数