工厂模式应用实例

引言

设计模式概念

设计模式(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.hqwc.cn/news/685759.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sybase数据库分页查询(指定起始位置)

针对单表数据量过大的场景&#xff0c;分页查询必不可少。针对sybase数据库分页查询的案例全网稀少&#xff0c;特别是指定起始页的分页查询实现。 本文依靠实际开发场景&#xff0c;特此总结Sybase数据库分页查询&#xff08;指定起始位置&#xff09;。 目录 一、 SQL实现分…

小红书搞钱美学课-6.0升级版,账号搭建/爆款创作/工具实战/账号变现篇

让我们用视觉撬动流量 课程体系 334253课程权益(5周服务期) 3节账号运营基础课3节自媒体笔记创作课。4节封面设计实操课2次实操加餐分享5次作业指导(一对一)3次答疑直播 课程大纲 一、账号搭建篇 变现模板、精准定位 二、爆款创作篇爆款选题、首图、文案与脚本、快速涨粉…

ros 学习记录(二)URDF小车运动控制

URDF小车运动控制 准备工作创建 robot_xacro.launch 接上文&#xff0c;想用键盘控制小车在Gazebo中移动。 准备工作 名称版本ROSNoeticGazebo11.11.0 创建 robot_xacro.launch 通过运行这个launch文件&#xff0c;可以启动Gazebo仿真环境&#xff0c;并在仿真环境中加载和…

drawio 网页版二次开发(1):源码下载和环境搭建

目录 一 说明 二 源码地址以及下载 三 开发环境搭建 1. 前端工程地址 2. 配置开发环境 &#xff08;1&#xff09;安装 node.js &#xff08;2&#xff09;安装 serve 服务器 3. 运行 四 最后 一 说明 应公司项目要求&#xff0c;需要对drawio进行二次开发&…

Linux -- 日志

一 日志的重要性 在之前的编程经历中&#xff0c;如果我们的程序运行出现了问题&#xff0c;都是通过 标准输出 或 标准错误 将 错误信息 直接输出到屏幕上&#xff0c;以此来排除程序中的错误。 这在我们以往所写的程序中使用没啥问题&#xff0c;但如果出错的是一个不断在运行…

大模型常用微调数据集

文章目录 指令微调数据集人类对齐数据集 为了增强模型的任务解决能力&#xff0c;大语言模型在预训练之后需要进行适应性微调&#xff0c;通常涉及两个主要步骤&#xff0c;即指令微调&#xff08;有监督微调&#xff09;和对齐微调。 指令微调数据集 在预训练之后&#xff0c…

博特激光:355nm高精度紫外激光打标机带来极致工艺

紫外激光打标机在现代制造业和技术中的应用&#xff0c;的确在准确度和精密度方面带来了革命性的提高。特别是在微电子、半导体、医疗器械、高端消费品等需要高精度、高清晰打标的行业&#xff0c;紫外激光打标机以其独特的优势&#xff0c;赋予产品极致的工艺品质。 以下是UV激…

瑞友天翼应用虚拟化系统appsave SQL注入漏洞

网络测绘 fofa:title"瑞友应用虚拟化系统" 漏洞描述 瑞友天翼应用虚拟化系统是西安瑞友信息技术资讯有限公司研发的具有自主知识产权&#xff0c;基于服务器计算架构的应用虚拟化平台。 瑞友天翼应用虚拟化系统中的/Home/Controller/AdminController 存在 appsav…

PyQt6--Python桌面开发(6.QLineEdit单行文本框)

QLineEdit单行文本框 import sys import time from PyQt6.QtGui import QValidator,QIntValidator from PyQt6.QtWidgets import QApplication,QLabel,QLineEdit from PyQt6 import uicif __name__ __main__:appQApplication(sys.argv)uiuic.loadUi("./QLine单行文本框.u…

Kubernetes核心概念基本操作

1.1 Namespace命名空间 1.1.1 Namespace核心概念 Kubernetes 的 Namespace&#xff08;命名空间&#xff09;是一种用于创建逻辑隔离分区的机制&#xff0c;它的主要作用是用来实现多套环境的资源隔&#xff0c;它允许用户在同一个物理集群中模拟出多个虚拟集群的效果。以下是…

性能监测--jemeter

过年时相亲&#xff0c;遇到了一个很好的女生&#xff0c;生活的中心重心有所改变&#xff0c;好久没上线了。今天有时间&#xff0c; 公司让做性能&#xff0c;用到jemeter&#xff0c;所以简单记录一下 部署环境&#xff1a; 安装java 设置环境变量&#xff1a; 找到jdk…

Microsoft Project使用简明教程

一.认识Microsoft Project Microsoft Project 是微软公司开发的项目管理软件&#xff0c;用于规划、协调和跟踪项目的进度、资源和预算&#xff0c;如下图所示&#xff0c;左边是任务的显示&#xff0c;右边是一个日程的显示图&#xff0c;最上方的长方形处在我们项目设定日程…