工厂模式应用实例

引言

设计模式概念

设计模式(Design Pattern)的官方概念可以表述为:在软件设计中,设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它是针对特定问题或特定场景的解决方案,是一种经过实践验证的最佳实践。设计模式主要用于解决软件设计中的各种问题,例如代码重复、性能问题、可维护性和可扩展性等。使用设计模式可以创建出可重用的解决方案,使代码更加清晰易懂、易维护和易扩展。设计模式不是语言特性或库,而是一种思想、一种方法论,它可以被应用于各种编程语言和框架中。学习设计模式可以提高设计能力和编程水平。

工厂模式概念:

工厂模式(Factory Pattern)是最常用 的设计模式之一,这种类型的设计模式属于创建型 模式,它提供了一种创建对象的 最佳 方式。

工厂模式提供了一种创建对象的方式,而 无需指定要创建的具体类。

工厂模式属于创建型模式,它在创建对象时提供了一种封装机制,将实际创建对象的代码与使用代码分离。

特点:

创建对象时,- 会对客户端暴露创建逻辑,并且是 通过 使用同一接口(API) 来指向新创建的对象

工厂模式的优势

工厂模式的优势主要体现在以下几个方面:

  1. 解耦:工厂模式将对象的创建与使用分离,使得代码结构更加清晰。当需要创建新的对象时,无需修改使用对象的代码,只需修改工厂类即可,这大大降低了代码的耦合度。

  2. 封装性:工厂模式隐藏了对象的创建细节,使用者只需知道所需对象的接口,而无需知道具体的创建过程。这增强了系统的封装性,使得代码更加易于理解和维护。

  3. 可扩展性:当需要添加新的产品时,只需在工厂类中增加一个新的产品创建方法,并修改返回类型或添加新的工厂子类,而无需修改使用对象的代码。这使得系统更加易于扩展。

  4. 灵活性:工厂模式可以根据不同的条件创建不同的对象实例。例如,可以通过配置文件或参数化工厂方法来创建不同的对象,这使得系统更加灵活。

  5. 符合开闭原则:工厂模式符合开闭原则,即对于扩展是开放的,对于修改是封闭的。当需要添加新的产品时,只需扩展工厂类而无需修改已有代码。

  6. 简化代码:通过工厂模式,可以将复杂的创建过程封装在工厂类中,从而简化使用对象的代码。同时,工厂模式还可以减少代码中的重复部分,提高代码的可重用性。

  7. 便于测试:工厂模式可以将创建对象的代码与使用对象的代码分离,这使得在测试过程中可以更容易地模拟或替换对象实例,从而便于进行单元测试或集成测试。

总之,工厂模式通过解耦、封装、可扩展性、灵活性、开闭原则、简化代码和便于测试等优势,使得系统更加易于理解维护扩展测试

工厂模式实现

(以下代码为例):

混乱的单文件代码

cpp 复制代码
#include <stdio.h>

//  结构体实现类 -- 抽象
struct Animal
{
    //  成员属性
    char name[128];
    int age;
    int sex;
    // 成员方法
    
    // 注意这里是函数指针, if 不加上(), 就变成了返回值是 void*

    void (*peat)();
    void (*pbeat)();
};

void dogEat()
{
  puts("狗吃屎");
}

void catEat()
{
  puts("猫吃鱼");
}

void personEat()
{
  puts("人吃米");
}


void dogBeat()
{
   puts("d四你");
}

void catBeat()
{
   puts("c四你");
}

void personBeat()
{
   puts("p四你");
}

int main()
{

      //简单的赋值方式
     struct Animal dog = {
      .peat = dogEat,
      .pbeat = dogBeat
    }; 
    

    struct Animal cat = { 
      .peat = catEat,
      .pbeat = catBeat
    }; 
    

    struct Animal person = {  
      .peat = personEat,
      .pbeat = personBeat
    }; 
    

  
    dog.peat();
    cat.peat();
    person.peat();
    
    dog.pbeat();
    cat.pbeat();
    person.pbeat();
    

    
    return 0;
}

工厂模式设计图:

封装各个函数

记得把 main 函数需要 用到 .c(源文件)里面的函数,在需要包含的.h(头文件)中声明,不然 在main里面不能调用到

====================================

以下是全部文件

main.c

cpp 复制代码
#include <string.h>

#include "animal.h"


// 链表的查找
struct Animal*findUtilByName(char *str,struct Animal*phead)
{
    struct Animal* p = phead;
    if(NULL == phead){
        puts("空链表");
        return NULL;
    }
    else{
    while(NULL != p)
    {
        if(strcmp(p->name,str) == 0){
            return  p;
        }
        p = p->next;
    }
    }
    return NULL;

}

int main()
{

    char buf[128] ={'\0'};
    struct Animal* phead = NULL;
    struct Animal* ptmp;
    //将我们需要的三种对象加入到链表中 
    phead = putCatToLink(phead);
    phead = putdogToLink(phead);
    phead = putpersonToLink(phead);

    while (1) // 这就算我们需要实现的业务
    {
        puts("请选择你需要的对象,包括:Tom, huang, likui");
        scanf("%s",buf);
        ptmp = findUtilByName(buf,phead);
        if(NULL != ptmp){
        ptmp->peat();
        ptmp->pbeat();
        }
        memset(buf,'\0',sizeof(buf));
    }
    
    return 0;
}

==============================================

animal.h

cpp 复制代码
#ifndef __ANIMAL_H_
#define __ANIMAL_H_

#include <stdio.h>

struct Animal
{
    //  成员属性
    char name[128];
    int age;
    int sex;
    // 成员方法
    
    // 注意这里是函数指针, if 不加上(), 就变成了返回值是 void*

    void (*peat)();
    void (*pbeat)();
    struct Animal * next; //我们使用链表来遍历所有对象
};


struct  Animal *putpersonToLink(struct  Animal *phead);
struct  Animal * putdogToLink(struct  Animal *phead);
struct  Animal * putCatToLink(struct  Animal *phead);


#endif

==============================================

cat.c

cpp 复制代码
#include "animal.h"

void catEat()
{
    puts("猫吃鱼");
}

void catBeat()
{
    puts("挠四你");
}
// 这里面的赋值 用到的函数 需要在前面先定义, 注意位置不能错
struct Animal cat = {
    .name = "Tom",
    .peat = catEat,
    .pbeat = catBeat};


// 头插法  -- 向链表中添加猫对象

struct  Animal * putCatToLink(struct  Animal *phead)
{
//    if(NULL == phead)
//    {
//     phead = &cat; 
//    }
//    else
//    {
//     cat.next = phead;
//     phead = &cat;
//    }

if(NULL != phead)    //if链表里面已经有数据的话
   cat.next = phead; // 把cat插入头节点的后面
phead = &cat; //空链表 cat 就作为头 | 非空 cat 插入到头节点后面之后也会成为新的头
    
return phead;
}

==============================

dog.c

cpp 复制代码
#include "animal.h"


void dogEat()
{
    puts("狗吃屎");
}

void dogBeat()
{
    puts("咬四你");
}
struct Animal dog = {
    .name = "huang",
    .peat = dogEat,
    .pbeat = dogBeat};

// 头插法  -- 向链表中添加猫对象

struct  Animal * putdogToLink(struct  Animal *phead)
{

if(NULL != phead)    //if链表里面已经有数据的话
   dog.next = phead; // 把dog插入头节点的后面
phead = &dog; //空链表 dog 就作为头 | 非空 dog 插入到头节点后面之后也会成为新的头
    
return phead;
}

person.c

cpp 复制代码
#include "animal.h"
#include <stdio.h>

void personEat()
{
    puts("人吃米");
}

void personBeat()
{
    puts("骂四你");
}

struct Animal person = {
    .name = "likui",
    .peat = personEat,
    .pbeat = personBeat};

// 头插法  -- 向链表中添加猫对象

struct  Animal *putpersonToLink(struct  Animal *phead)
{

if(NULL != phead)    //if链表里面已经有数据的话
   person.next = phead; // 把person插入头节点的后面
phead = &person; //空链表 person 就作为头 | 非空 person 插入到头节点后面之后也会成为新的头
    
return phead;
}

// if 我们需要 扩展功能 --> 比如添加对象

这时候工厂 模式优势就体现出来了

常见问题:

fish.c:22:18: error: conflicting types for 'putfishToLink'; have 'struct Animal *(struct Animal *)'

22 | struct Animal * putfishToLink(struct Animal *phead) --> 这种报错通常是 源文件改了,但是头文件没有改

==========================

扩展

添加 fish.c
cpp 复制代码
#include "animal.h"


void fishEat()
{
    puts("大鱼小鱼");
}

void fishBeat()
{
    puts("吐泡泡");
}
struct Animal fish = {
    .name = "dayu",
    .peat = fishEat,
    .pbeat = fishBeat};

// 头插法  -- 向链表中添加猫对象

struct  Animal * putfishToLink(struct  Animal *phead)
{

if(NULL != phead)    //if链表里面已经有数据的话
   fish.next = phead; // 把fish插入头节点的后面
phead = &fish; //空链表 fish 就作为头 | 非空 fish 插入到头节点后面之后也会成为新的头
    
return phead;
}

//然后我们只需要修改.h 头文件中包含的函数, 和main里面的逻辑

// 我们发现添加一次动物我们就需要去选项输出一次名字不太友好,那么我们可以优化遍历链表输出

新的main.c :
cpp 复制代码
#include <string.h>

#include "animal.h"


// 链表的查找
struct Animal*findUtilByName(char *str,struct Animal*phead)
{
    struct Animal* p = phead;
    if(NULL == phead){
        puts("空链表");
        return NULL;
    }
    else{
    while(NULL != p)
    {
        if(strcmp(p->name,str) == 0){
            return  p;
        }
        p = p->next;
    }
    }
    return NULL;

}

 void getAllName(struct Animal*phead)
 {
    struct Animal* p = phead;
    if(NULL == phead){
        puts("空链表");
    }
    else{
    while(NULL != p)
    {
        printf("%s ",p->name);
        p = p->next;
    }
    }
 }

int main()
{

    char buf[128] ={'\0'};
    struct Animal* phead = NULL;
    struct Animal* ptmp;
    //将我们需要的三种对象加入到链表中 
    phead = putCatToLink(phead);
    phead = putdogToLink(phead);
    phead = putpersonToLink(phead);
    phead = putfishToLink(phead);

    while (1) // 这就算我们需要实现的业务
    {
        puts("请选择你需要的对象,包括:");
        getAllName(phead);
        scanf("%s",buf);
        ptmp = findUtilByName(buf,phead);
        if(NULL != ptmp){
        ptmp->peat();
        ptmp->pbeat();
        }
        memset(buf,'\0',sizeof(buf));
    }
    
    return 0;
}
相关推荐
quaer5 分钟前
Open-Sora全面开源?
开发语言·算法·机器学习·matlab·矩阵
sky丶Mamba8 分钟前
Spring Boot中获取application.yml中属性的几种方式
java·spring boot·后端
hakesashou9 分钟前
python如何比较字符串
linux·开发语言·python
yufei-coder34 分钟前
C#基础语法
开发语言·c#·.net
数据龙傲天34 分钟前
1688商品API接口:电商数据自动化的新引擎
java·大数据·sql·mysql
长天一色34 分钟前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
玄奕子1 小时前
GPT对话知识库——在STM32的平台下,通过SPI读取和写入Flash的步骤。
stm32·单片机·gpt·嵌入式·嵌入式驱动
_.Switch1 小时前
Python机器学习模型的部署与维护:版本管理、监控与更新策略
开发语言·人工智能·python·算法·机器学习
醉颜凉1 小时前
银河麒麟桌面操作系统修改默认Shell为Bash
运维·服务器·开发语言·bash·kylin·国产化·银河麒麟操作系统
NiNg_1_2341 小时前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript