工厂模式应用实例

引言

设计模式概念

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

工厂模式概念:

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

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

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

特点:

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

工厂模式的优势

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

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

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

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

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

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

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

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

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

工厂模式实现

(以下代码为例):

混乱的单文件代码


#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

#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

#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

#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

#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

#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
#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 :
#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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用STM32CubeMX进行STM32F4的定时器配置

目录 1. Pin脚2. 配置2.1 时钟配置2.2 RCC配置2.3 Timer配置2.4 输出文件 3. 代码3.1 使能定时器3.2 回调函数 1. Pin脚 2. 配置 2.1 时钟配置 timer3时钟挂载在APB1上&#xff1a; 时钟配置如下&#xff1a; 外部使用8MHz晶振 开启内部16MHz晶振 使用锁相环 开启最高100MHz。…

揭秘依赖注入:软件开发人员的基本指南

Dependency injection (DI) is a design pattern and programming technique to manage dependencies between different components. 依赖注入&#xff08;DI&#xff09;是一种用于管理不同组件之间依赖关系的设计模式和编程技术。 In DI, the dependencies of a class or ot…

机台统一管理有多困难?但现在出现可行的解决方案了

机台的统一管理对于企业来说对提高生产效率、降低成本、优化资源配置以及保障生产安全等方面都具有至关重要的作用。但企业机台统一管理却存在实际的困难&#xff0c;主要体现在&#xff1a; 多样化的设备和运作模式&#xff1a;由于机台设备可能来自不同的厂商&#xff0c;其规…

如何描述自己的算法?

算法的表达 好的&#xff0c;让我们来一起探讨如何向别人介绍我们的算法。说话很简单&#xff0c;但要把话说清楚&#xff0c;说明白就不那么容易了。同样的道理&#xff0c;能够通俗易懂&#xff0c;直观清晰和严谨地描述一个算法&#xff0c;也是一项具有挑战性的任务。接下…

106短信群发平台在金融和法务行业的应用分析

一、金融行业应用 1.客户通知与提醒&#xff1a;银行、证券、保险等金融机构经常需要向客户发送各类通知和提醒&#xff0c;如账户余额变动、交易确认、扣费通知、理财产品到期提醒等。106短信群发平台可以快速、准确地将这些信息发送到客户的手机上&#xff0c;确保客户及时获…

.NET邮箱API发送邮件的步骤?怎么配置API?

.NET邮箱API发送邮件需要注意哪些&#xff1f;如何使用API发信&#xff1f; 在.NET环境中&#xff0c;使用邮箱API发送邮件是一个常见的需求。无论是企业级的邮件通知&#xff0c;还是个人项目中的邮件验证&#xff0c;都少不了.NET邮箱API的帮助。下面&#xff0c;AokSend将详…

我的创作纪念日(365天)

时间如白驹过隙&#xff0c;转眼间&#xff0c;我已经在技术写作这条路上走过了365个日夜。回望2023年5月9日&#xff0c;我敲下了第1篇技术博客的标题——《什么是代理IP&#xff1f;代理IP有什么用途》。那一天&#xff0c;平凡而又不平凡&#xff0c;因为我决定将自己的知识…

政安晨【零基础玩转各类开源AI项目】:基于Ubuntu系统本地部署使用GPT-SoVITS进行语音克隆与TTS语音生成

目录 介绍 什么是TTS 安装Miniconda 框架功能 测试通过的环境 开始 1. 安装好miniconda 2. 进入下载的GPT-SoVITS目录 3. 创建虚拟环境并执行脚本 4. 执行过程中可能会出错 5. 下载预训练模型 6. 训练过程中可能会报错 7. 使用过程中可能出错 8.以下是使用全过程…

信息安全管理体系介绍(含全套体系文档)

信息安全管理体系英文全称Information Security Management System&#xff0c;简称为ISMS&#xff0c;是1998年左右从英国发展起来的信息安全领域中的一个全新概念&#xff0c;是管理体系&#xff08;Management System&#xff0c;MS&#xff09;思想和方法在信息安全领域的应…

Flask-大体了解介绍

初识Flask Flask是使用 Python编写的Web微框架。Web框架可以让我们不用关心底层的请求响应处理&#xff0c;更方便高效地编写Web程序。 Flask主要有两个依赖&#xff0c;一个是WSGI&#xff08;Web Server Gateway Interface&#xff0c;Web服务器网关接口&#xff09;工具集…

探索大语言模型在信息提取中的应用与前景

随着人工智能技术的快速发展&#xff0c;大语言模型&#xff08;LLMs&#xff09;在自然语言处理&#xff08;NLP&#xff09;领域取得了显著的进展。特别是在信息提取&#xff08;IE&#xff09;任务中&#xff0c;LLMs展现出了前所未有的潜力和优势。信息提取是从非结构化文本…

ChatGPT-Next-Web漏洞利用分析(CVE-2023-49785)

1. 漏洞介绍 ​ 日常网上冲浪&#xff0c;突然粗看以为是有关Chat-GPT的CVE披露出来了&#xff0c;但是仔细一看原来是ChatGPT-Next-Web的漏洞。漏洞描述大致如下&#xff1a;&#xff08;如果有自己搭建了还没更新的速速修复升级防止被人利用&#xff0c;2.11.3已经出来了&am…

解决ModuleNotFoundError: No module named ‘skfuzzy‘,这个库全名可不叫skfuzzy哦,否则直接报错!!

ModuleNotFoundError: No module named skfuzzy 在这里插入图片描述在这里插入图片描述如何解决 ModuleNotFoundError: No module named skfuzzy 的问题&#xff1f;skfuzzy 模块介绍什么是模糊C均值聚类&#xff1f;skfuzzy 的应用如何使用 skfuzzy 进行模糊聚类 结论 如何解决…

数据结构-线性表-应用题-2.2-14

1&#xff09;算法基本设计思想&#xff1a; 2&#xff09;c语言描述&#xff1a; #define INT_MAX 0X7FFFFFFF int abs_(int a) {//绝对值if(a<0) return -a;else return a; } bool min(int a,int b,int c){if(a<b&&a<c) return true;else return false; } …

JAVA随记——集合篇

注意&#xff1a;作者之前的Java基础没有打牢&#xff0c;有一些知识点没有记住&#xff0c;所以在学习中出现了许多零散的问题。现在特地写一篇笔记总结一下&#xff0c;所以有些知识点不是很齐全。 集合中各类名词的关系 Collection集合为单列集合。 集合存储数据类型的特点…

案例导入说明.md

案例导入说明 为了演示多级缓存&#xff0c;我们先导入一个商品管理的案例&#xff0c;其中包含商品的CRUD功能。我们将来会给查询商品添加多级缓存。 1.安装MySQL 后期做数据同步需要用到 MySQL 的主从功能&#xff0c;所以需要大家在虚拟机中&#xff0c;利用 Docker 来运行一…

即将开幕,邀您共赴创新之旅“2024上海国际消费者科技及创新展览会”

备受期待的2024上海国际消费者科技及创新展览会&#xff08;以下简称“CTIS”&#xff09;即将于6月13日至15日亮相上海新国际博览中心N1-N3馆。 2024上海国际消费者科技及创新展览会总面积达40,000平方米&#xff0c;涵盖600余家展商&#xff0c;预计吸引40,000多位观众莅临现…

js原生写一个小小轮播案例

先上示例&#xff1a; 附上代码 html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content…

陪诊系统|陪诊小程序成品|陪诊系统功能

随着人们对健康的日益关注以及医疗技术的不断进步&#xff0c;陪诊小程序应运而生&#xff0c;通过提供陪同就医、医疗服务和健康管理等功能为患者和家庭成员提供了更多的便利和选择。本文将分析陪诊小程序的关键功能&#xff0c;以便更好地理解其在医疗领域的作用。 在陪诊小程…

SpringBoot过滤器简单构建详细教程以及与拦截器区别解释

作用范围&#xff1a;过滤器基于Servlet规范&#xff0c;作用于更广泛的层面&#xff0c;不仅限于Spring MVC&#xff0c;它可以拦截进入Web应用的所有请求&#xff0c;包括静态资源请求。过滤器可以对请求和响应的内容进行预处理和后处理。实现方式&#xff1a;过滤器需要实现…
最新文章