iOS -- 工厂设计模式

iOS -- 工厂设计模式

  • 设计模式概念
  • 设计模式七大准则
  • 简单工厂模式
    • 优点
    • 缺点
    • 主要作用
    • 示例
  • 工厂方法模式
    • 优点
    • 缺点
    • 主要作用:
  • 抽象工厂方法
    • 缺点
    • 主要作用:
    • 文件分类

设计模式概念

所谓设计模式(Design pattern) 是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的 高内聚 和 低耦合。可以这么说,计算机中设计模式指的是一套广为人知、被反复使用、经过分类编目的代码设计经验。使用设计模式是为了可重用代码,让代码更容易被他人理解,最重要的是保证代码可靠性。

设计模式七大准则

  1. 单一职责原则(Single Responsibility Principle):一个类应该只有一个引起它变化的原因。每个类应该负责单一的职责,这样可以提高类的内聚性和可维护性
    比如:UIView负责事件的传递、响应,CALayer负责视图的显示、动画,他们各自都有自己的单一职责。
  2. 开放封闭原则(Open-Closed Principle):软件实体(类、模块、函数等)应该**对扩展开放,对修改封闭。**通过使用抽象和接口,可以在不修改现有代码的情况下扩展系统的功能。
  3. 里氏替换原则(Liskov Substitution Principle):子类对象应该能够替换其父类对象,而程序的行为不受影响。这个原则强调了继承关系的正确使用,确保子类符合父类的接口和行为。
  4. 依赖倒置原则(Dependency Inversion Principle):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象应该依赖于细节,而不是细节依赖于抽象。
  5. 接口隔离原则(Interface Segregation Principle):客户端不应该被迫依赖它们不使用的接口。接口应该尽量小而精确,符合客户端的实际需求。
    我们在 Objective-C 中实现了接口和类的定义,遵循了拆分接口的原则。每个类只需实现相关的方法,避免了不必要的方法实现,提高了代码的可读性和灵活性。
  6. 迪米特法则(Law of Demeter):一个对象应该与其他对象保持最少的知识联系。一个类应该尽量减少对其他类的依赖,只与直接相关的类进行交互。
  7. 组合/聚合复用原则(Composition/Aggregation Reuse Principle):优先使用组合和聚合关系,而不是继承关系来实现代码的复用。通过组合和聚合,可以使得系统更灵活、可扩展和易于维护。
    合成: 表示一个类拥有其他类的实例,并且这些实例是无法独立存在的一部分。当一个对象被销毁时,其组成部分也会被销毁。
@interface Engine : NSObject
// 引擎类的定义
@end

@interface Car : NSObject
@property (nonatomic, strong) Engine *engine;
// 车类的定义,拥有一个引擎实例作为其一部分
@end

@implementation Car
// 类的实现
@end

Car类持有一个引擎(Engine)对象的实例作为其一部分。当Car对象被销毁时,引擎对象也会随之被销毁。
**聚合:**表示一个类与其他类之间存在关联关系,其中一个类是另一个类的容器或集合,但这些类之间的关系并不是强依赖的关系。当一个对象被销毁时,其聚合对象可以继续存在。

@interface Student : NSObject
// 学生类的定义
@end

@interface Classroom : NSObject
@property (nonatomic, strong) NSArray<Student *> *students;
// 教室类的定义,包含了学生对象的集合
@end

@implementation Classroom
// 类的实现
@end

Classroom类包含一个学生对象的数组作为其一部分。当Classroom对象被销毁时,学生对象仍然可以存在,因为它们可以属于其他教室或独立存在。

合成和聚合都描述了类之间的关系,合成表示一个类拥有其他类的实例作为其一部分,而聚合表示一个类与其他类之间存在关联关系,其中一个类是另一个类的容器或集合。

简单工厂模式

在这里插入图片描述
专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常具有共同的父类。(总结来说就是把一大堆if-else判断由业务层放到工厂类里面)。

优点

  • 根据约定好的参数就可以获取所需要的对象,而不需要知道其创建的细节。减少了系统的耦合度。
  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,减少开发者的记忆成本。

缺点

  • 如果业务上添加新产品的话,就需要修改工厂类原有的判断逻辑,这其实是违背了开闭原则的。
  • 在产品类型较多时,有可能造成工厂逻辑过于复杂。所以简单工厂模式比较适合产品种类比较少而且增多的概率很低的情况。

主要作用

它提供了一种通过一个工厂类来创建对象的方式,而不需要直接在客户端代码中实例化对象。简单工厂模式将对象的创建和使用分离,使得客户端代码更加灵活,并且可以降低代码的耦合度。

  • 有一组相似的对象,需要集中统一创建时。
  • 创建对象的过程较为复杂时。
  • 对象很多,并且有扩展需求时。
  • 客户端不需要知道创建对象的过程时。
  • 客户端使用的对象存在变动的可能,或者根本不知道使用哪一个具体对象时。

示例

工厂类中的核心代码
通过传递进来的字符串来确定生成的类。

//  phoneFactory.m
//  简单工厂模式
//
//  Created by mac on 2024/4/18.
//

#import "phoneFactory.h"
#import "Vivo.h"
#import "Oppo.h"
#import "Xiaomi.h"
#import "Apple.h"
@implementation phoneFactory

NSArray* array = @[@"Vivo", @"Oppo", @"Xiaomi", @"Apple"];
+ (nonnull id)createPhone:(nonnull NSString *)phoneType {
    switch ([array indexOfObject:phoneType]) {
        case 0:
            return [[Vivo alloc] init];
            break;
        case 1:
            return [[Oppo alloc] init];
            break;
        case 2:
            return [[Xiaomi alloc] init];
            break;
        case 3:
            return [[Apple alloc] init];
        default:
            break;
    }
    
    return nil;
}

@end

其中通过工厂类返回的各种类(各种型号手机)必须遵守以下协议:

@protocol phoneDelegate <NSObject>
- (void) phoneWays;
@end

然后在各个类中实现该方法

#import "Vivo.h"

@implementation Vivo
- (void) phoneWays {
    NSLog(@"Vivo");
}
@end

文件分类:
在这里插入图片描述
实现效果:
在这里插入图片描述
在这里插入图片描述
这样的模式就与类族模式有点相似,类族模式最重要的一点是:把实现细节隐藏在一套简单的公共接口后面,并且类之间为继承关系。学习完工厂模式之后,简单工厂方法和类族模式主要的区别是:生成的各类和工厂类并不是父子类的关系,通过协议来完成各方法。

工厂方法模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到了子类。就像Cocoa Touch中的NSNumber的numberWithBool和numberWithInt方法,他们传入不同类型的参数,获得NSNumber实例。
在这里插入图片描述

优点

  • 和直接创建具体对象相比,使用工厂方法创建对象算是最佳的做法。
  • 根据所需产品找对应工厂进行生产,不关心产品细节,也不需要知道产品类的类名。
  • 当系统中加入新产品时,不需要修改抽象工厂和抽象产品提供的接口,也无须修改客户端和其他的具体工厂和具体产品,而只要添加一个具体工厂和与其对应的具体产品就可以了,符合了“开-闭”原则。

缺点

当系统中加入新产品时,除了需要提供新的产品类之外,还要提供与其对应的具体工厂类。因此系统中类的个数将成对增加,增加了系统的复杂度。

主要作用:

  • 编译时无法准确预期需要创建对象的类。
  • 类想要其子类决定在运行时创建什么类型的实例。
  • 类有若干辅助类为其子类,而你想将返回哪个子类这种信息局部化。

示例:
这个较之前的相比,工厂类中并没有类方法,只有一些方法,简单工厂方法是通过协议的方法去令其他类完成方法,而工厂方法模式是通过继承,令其他类继承工厂类,去重写父类的这几个方法。 来看看代码:

工厂类:

#import "phoneCenter.h"

@implementation phoneCenter
- (void) beginProductionPhone {
    NSLog(@"begin");
}

- (void) succeedProductionPhone {
    NSLog(@"succeed");
}
@end

子类:

@interface Vivo : phoneCenter

@end

#import "Vivo.h"

@implementation Vivo
- (void) beginProductionPhone {
    NSLog(@"begin vivo");
}

- (void) succeedProductionPhone {
    NSLog(@"succeed vivo");
}
@end

在viewController中初始化时以父类编译,子类运行

        PhoneCenter *a = [[Vivo alloc] init];
        [a beginProductionPhone];
        [a succeedProductionPhone];

文件分类
在这里插入图片描述
效果实现:
在这里插入图片描述

抽象工厂方法

工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个抽象产品类。抽象工厂模式里每个工厂都会生产多种产品,但不同工厂生产的产品属于不同的系列。抽象工厂模式可以用来解决多产品族的问题。
在这里插入图片描述

缺点

  • 增加新的产品种类困难,它需要修改抽象工厂的接口。
  • 代码结构比较复杂。

主要作用:

  • 类想让其子类决定在运行时创建什么,无法在编译时准确确定。
  • 类有若干个辅助类为其子类,而你想将返回某个子类这一信息局部化。

文件分类

在这里插入图片描述
这次的分类就较之前复杂了很多,我们来一步步看:
首先Manager相当于最大的工厂类,通过这个函数来确定是哪个工厂,apple厂还是google厂:

#import "FactoryManager.h"
#import "GooleFactory.h"
#import "AppleFactory.h"
@implementation FactoryManager

+ (BaseFactory *)factoryWithType:(KFactoryType)factoryType {
    if (factoryType == KApple) {
        return [[AppleFactory alloc] init];
    } else if (factoryType == KGoogle) {
        return [[GooleFactory alloc] init];
    }
    return nil;
}

@end

接下来到了工厂这步:
首先有一个作为两家工厂的父类,两家工厂继承于此类,并重写此基础类的各方法,以此来展示不同的效果:

#import <Foundation/Foundation.h>
#import "BasePhone.h"
#import "BaseWatch.h"
NS_ASSUME_NONNULL_BEGIN

@interface BaseFactory : NSObject
- (BasePhone*)createPhone;
- (BaseWatch*)createWatch;
@end

#import "BaseFactory.h"

@implementation BaseFactory

- (BasePhone *)createPhone {
    return nil;
}

- (BaseWatch *)createWatch {
    
    return nil;
}

@end


@interface AppleFactory : BaseFactory

@end


@implementation AppleFactory
- (BasePhone *)createPhone {
    return [[ApplePhone alloc] init];
}

- (BaseWatch *)createWatch {
    return [[AppleWatch alloc] init];
}
@end

在下面的一步我们让apple和google厂分别可以生产手机和手表两种产品,这时候和上一步一样,有一个基础手机类和基础手表类:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface BasePhone : NSObject
- (void) phoneCell;
@end

@implementation BasePhone
- (void)phoneCell {
    NSLog(@"This is a Phone");
}
@end

它的子类,除了继承并重写父类的方法,还添加了自己的方法:它的子类,除了继承并重写父类的方法,还添加了自己的方法:

@interface ApplePhone : BasePhone
- (void)applePhoneWays;
@end

@implementation ApplePhone
- (void)phoneCell {
    NSLog(@"This an apple phone");
}

- (void)applePhoneWays {
    NSLog(@"We can eat apple (phone)");
}
@end

其他类也和此相似
在ViewController中初始化,并执行各个方法:

    GooglePhone *googlePhone = (GooglePhone *)[googleFactory createPhone];
    //执行方法(1)
    [googlePhone phoneCell];
    [googlePhone googlePhoneWays];
    //确定商品(2)
    GoogleWatch *googleWatch = (GoogleWatch *)[googleFactory createWatch];
    //执行方法(2)
    [googleWatch watchCell];
    [googleWatch googleWatchWays];
    
    NSLog(@"-------------------------------------");
    
    //确定工厂
    BaseFactory *appleFactory = [FactoryManager factoryWithType:KApple];
    //确定商品(1)
    ApplePhone *applePhone = (ApplePhone *)[appleFactory createPhone];
    //执行方法(1)
    [applePhone phoneCell];
    [applePhone applePhoneWays];
    //确定商品(2)
    AppleWatch *appleWatch = (AppleWatch *)[appleFactory createWatch];
    //执行方法(2)
    [appleWatch watchCell];
    [appleWatch appleWatchWays];

运行的结果:
在这里插入图片描述

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

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

相关文章

nginx installed inLinux

yum install nginx [rootmufeng ~]# yum install nginx CentOS系列&#xff1a;【Linux】CentOS7操作系统安装nginx实战&#xff08;多种方法&#xff0c;超详细&#xff09; ———————————————— 版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC …

Maven通过flatten-maven-plugin插件实现多模块版本统一管理

正文 起因是公司开始推代码版本管理的相关制度&#xff0c;而开发过程中经常使用多模块构建项目&#xff0c;每次做版本管理时都需要对每个模块及子模块下的pom文件中parent.version和模块下依赖中的version进行修改&#xff0c;改的地方非常多&#xff0c;且非常容易漏。为此…

Pytorch 学习路程

目录 下载Pytorch 入门尝试 几种常见的Tensor Scalar Vector Matrix AutoGrad机制 线性回归尝试 使用hub模块 Pytorch是重要的人工智能深度学习框架。既然已经点进来&#xff0c;我们就详细的介绍一下啥是Pytorch PyTorch 希望将其代替 Numpy 来利用 GPUs 的威力&…

深度学习pytorch实战4---猴逗病识别·

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊](https://mtyjkh.blog.csdn.net/)** 引言 1.复习上周并反思 K同学针对大家近…

【数据结构】图论(图的储存方式,图的遍历算法DFS和BFS、图的遍历算法的应用、图的连通性问题)

目录 图论一、 图的基本概念和术语二、图的存储结构1. 数组(邻接矩阵)存储表示无向图的数组(邻接矩阵)存储表示有向图的数组(邻接矩阵)存储表示 邻接表存储表示有向图的十字链表存储表示无向图的邻接多重表存储表示 三、图的遍历算法图的遍历——深度优先搜索&#xff08;DFS&a…

FPGA_verilog语法整理

FPGA_verilog语法整理 verilog的逻辑值 verilog的常数表达 位宽中指定常数的宽度&#xff08;表示成二进制数的位数&#xff09;&#xff0c;单引号加表示该常数为几进制的底数符号。 二进制底数符号为b&#xff0c;八进制为 o&#xff0c;十进制为d&#xff0c;十六进制为 h…

递归、搜索与回溯算法——穷举vs暴搜vs深搜

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享递归、搜索与回溯算法关于穷举vs暴搜vs深搜的专题 如果有不足的或者错误的请您指出! 目录 1.全排列1.1解析1.2题解 2.子集2.1解析2.1.1解法12.1.2解法1代码2.1.3解法22.1.4解法…

vscode微博发布案例

样例: CSS代码: * {margin: 0;padding: 0; }ul{list-style: none; }.w {width: 900px;margin: 0 auto; }.controls textarea {width: 878px;height: 100px;resize: none;border-radius: 10px;outline: none;padding-left: 20px;padding-top: 10px;font-size: 18px; }.controls…

yolov8 区域计数

yolov8 区域计数 1. 基础2. 计数功能2.1 计数模块2.2 判断模块 3. 主代码4. 实验结果5. 源码 1. 基础 本项目是在 WindowsYOLOV8环境配置 的基础上实现的&#xff0c;测距原理可见上边文章 2. 计数功能 2.1 计数模块 在指定区域内计数模块 def count_objects_in_region(bo…

Redis: 在项目中的应用

文章目录 一、Redis的共享session应用二、分布式缓存1、缓存2、缓存一致性问题解决方案&#xff08;缓存更新策略&#xff09;&#xff08;1&#xff09;作用&#xff08;2&#xff09;三种策略&#xff08;3&#xff09;主动更新策略&#xff08;数据库、缓存不一致解决方案&a…

SSL证书在HTTP与HTTPS中的角色差异是什么?

在互联网的广泛应用背景下&#xff0c;随着网络攻击和数据泄露事件频发&#xff0c;保障用户的数据安全已成为至关重要的议题。传统的HTTP协议在传输数据时不进行加密处理&#xff0c;导致数据在传输过程中暴露于潜在的窃听和篡改风险中&#xff0c;安全性薄弱。而通过引入SSL/…

【HC32L110】华大低功耗单片机启动文件详解

本文主要记录华大低功耗单片机 HC32L110 的 汇编启动过程&#xff0c;包括startup_hc32l110启动文件详细注释 目录 1.启动文件的作用2.堆栈定义2.1 栈2.2堆 3.向量表4.复位程序5.中断服务程序6.堆栈初始化启动过程详解7.1从0地址开始7.2在Reset_Handler中干了啥&#xff1f; 8.…

危险场景智能运维巡检系统

在石油、天然气、煤炭和化工等行业&#xff0c;特别是在I/IIC级防爆区场景中&#xff0c;存在着诸如易燃、易爆、高温、有毒有害以及粉尘等危险因素。例如&#xff0c;油气转运站、催化裂化装置、煤化工甲醇车间以及制氢站等地点&#xff0c;都面临着这些潜在的危险。传统的人工…

VOJ 网页跳转 题解 STL栈

网页跳转 用例输入 10 VISIT https://www.jisuanke.com/course/476 VISIT https://www.taobao.com/ BACK BACK FORWARD FORWARD BACK VISIT https://www.jisuanke.com/course/429 FORWARD BACK用例输出 https://www.jisuanke.com/course/476 https://www.taobao.com/ https…

echart实现排名列表

function createHorizontalBarChart(chartId, data) {if (typeof echarts undefined) {console.error(请先引入 ECharts 库);return;}// 初始化echarts实例var myChart echarts.init(document.getElementById(chartId));// 对数据按照 value 进行降序排序var sortedData dat…

k8s配置configmap指定到容器的指定文件

我们需要将名称为walletkey.properties的文件做成configmap&#xff0c;然后将walletkey.properties文件单独挂载出来到/data/walletkey.properties&#xff0c;且不能覆盖/data目录&#xff0c;具体如下 1、创建configmap configmap文件内容 其中walletkey.properties: >-引…

课时100:正则表达式_基础实践_基础知识

3.1.1 基础知识 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习 基础知识 需求 我们之前的一些操作&#xff0c;很大程度上都是基于特定的关键字来进行实践的&#xff0c;尤其是面对一些灵活的场景&#xff0c;我们因为过于限定一些关键字&am…

【配电网故障定位】基于二进制矮猫鼬优化算法的配电网故障定位 33节点配电系统故障定位【Matlab代码#82】

文章目录 【获取资源请见文章第6节&#xff1a;资源获取】1. 配电网故障定位2. 二进制矮猫鼬优化算法3. 算例展示4. 部分代码展示5. 仿真结果展示6. 资源获取 【获取资源请见文章第6节&#xff1a;资源获取】 1. 配电网故障定位 配电系统故障定位&#xff0c;即在配电网络发生…

Tensorflow2.0笔记 - 使用卷积神经网络层做CIFA100数据集训练(类VGG13)

本笔记记录CNN做CIFAR100数据集的训练相关内容&#xff0c;代码中使用了类似VGG13的网络结构&#xff0c;做了两个Sequetial&#xff08;CNN和全连接层&#xff09;&#xff0c;没有用Flatten层而是用reshape操作做CNN和全连接层的中转操作。由于网络层次较深&#xff0c;参数量…

在 Node.js 中配置代理 IP 采集文章

不说废话&#xff0c;直接上代码&#xff1a; const http require(http); const https require(https);// 之后可以使用 http 或 https 模块发起请求&#xff0c;它们将自动使用配置的代理 // 代理ip&#xff1a;https://www.kuaidaili.com/?refrg3jlsko0ymg const proxy …