【iOS】工厂模式

文章目录

  • 前言
  • 设计模式的三大原则
  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式
  • 关于三兄弟的升级与降级
  • 注意


前言

上文讲完了iOS的架构模式,接下来聊一聊设计模式,设计模式有许多,主要介绍一下工厂模式

设计模式的三大原则

  • S 单一职责原则
    告诉我们实现类要职责单一;
    例如UIView负责处理事件传递响应,CALayer负责动画与视图的显示
  • O 开闭原则是总纲,它告诉我们要对扩展开放,对修改关闭;
// 基本的绘制类
@interface Shape : NSObject
- (void)draw;
@end

// 扩展新的形状,无需修改现有代码
@interface Circle : Shape
@end

@implementation Circle
- (void)draw {
    // 绘制圆形的逻辑
}
@end
  • L 里氏替换原则告诉我们不要破坏继承体系;
@interface Bird : NSObject
- (void)fly;
@end

@interface Duck : Bird
@end

@implementation Duck
- (void)fly {
    // 实现飞行
}
@end
  • D 迪米特法则。 一个对象应当对其他对象尽可能少的了解,实现高聚合、低耦合
// 班级类
@interface Class : NSObject
- (NSArray *)getAllStudents;
@end

// 学校类
@interface School : NSObject
@property (strong, nonatomic) Class *class;
- (NSArray *)getAllStudentsInClass;
@end

@implementation School
- (NSArray *)getAllStudentsInClass {
    return [self.class getAllStudents];
}
@end
  • I 接口隔离原则: 使用多个专门的协议、而不是一个庞大臃肿的协议
@protocol Printer
- (void)printDocument;
@end

@protocol Scanner
- (void)scanDocument;
@end

@interface MultiFunctionMachine : NSObject <Printer, Scanner>
@end

@implementation MultiFunctionMachine
- (void)printDocument {
    // 打印文档
}

- (void)scanDocument {
    // 扫描文档
}
@end
  • D 依赖倒置原则。抽象不应该依赖于具体实现、具体实现可以依赖于抽象。 调用接口感觉不到内部是如何操作的

定义协议(接口)

首先,我们定义一个文件读取的协议,这个协议负责声明读取文件内容的方法。

// FileReaderProtocol.h
#import <Foundation/Foundation.h>

@protocol FileReaderProtocol <NSObject>
- (NSString *)readContent;
@end

具体实现类

接下来,实现这个协议。这里我们可以有多种实现方式,例如从本地文件系统读取,或是从网络获取。

// LocalFileReader.h
#import <Foundation/Foundation.h>
#import "FileReaderProtocol.h"

@interface LocalFileReader : NSObject <FileReaderProtocol>
@property (strong, nonatomic) NSString *filePath;
- (instancetype)initWithFilePath:(NSString *)filePath;
@end

// LocalFileReader.m
#import "LocalFileReader.h"

@implementation LocalFileReader

- (instancetype)initWithFilePath:(NSString *)filePath {
    self = [super init];
    if (self) {
        _filePath = filePath;
    }
    return self;
}

- (NSString *)readContent {
    NSError *error;
    NSString *content = [NSString stringWithContentsOfFile:self.filePath
                                                  encoding:NSUTF8StringEncoding
                                                     error:&error];
    if (error) {
        NSLog(@"Error reading file: %@", error.localizedDescription);
        return nil;
    }
    return content;
}

@end

简单工厂模式

简单工厂模式并不是一个正式的设计模式,更多的是一种变成习惯
其主要通过传入参数给唯一的工厂类来创建不同类型的对象

但是这样就会出现一个问题,当我们需要添加新的类型,也就是添加新的产品时,我们必须去修改工厂类,这就违反了开闭原则

// 创建产品协议
@protocol Product <NSObject>
- (void)use;
@end

// 具体产品A
@interface ProductA : NSObject <Product>
- (void)use;
@end

@implementation ProductA
- (void)use {
    NSLog(@"Using Product A");
}
@end

// 具体产品B
@interface ProductB : NSObject <Product>
- (void)use;
@end

@implementation ProductB
- (void)use {
    NSLog(@"Using Product B");
}
@end

// 简单工厂
@interface ProductFactory : NSObject
+ (id<Product>)productWithType:(NSString *)type;
@end

@implementation ProductFactory
+ (id<Product>)productWithType:(NSString *)type {
    if ([type isEqualToString:@"A"]) {
        return [ProductA new];
    } else if ([type isEqualToString:@"B"]) {
        return [ProductB new];
    }
    return nil;
}
@end

// 使用
ProductFactory *factory = [ProductFactory new];
id<Product> productA = [ProductFactory productWithType:@"A"];
[productA use];
// 首先创建一个工厂,根据传入的参数觉得工厂生产什么产品

在这里插入图片描述

工厂方法模式

工厂方法模式同样只是生产一种产品,但是一种产品可以有多个品牌

举个例子,我们的可乐工厂可以生产可乐,其即可以生产百事可乐,也可以生产可口可乐

首先我们需要定义一个创建对象的接口,但让子类决定要实例化的类是哪一个。工厂方法让类的实例化延迟到子类进行。
也就是抽象工厂是父类,具体工厂是子类,只有确定了具体工厂才能决定生产的产品是哪一种品牌的

// 定义产品接口
@protocol Logger <NSObject>
- (void)logMessage:(NSString *)message;
@end

// 具体产品类 FileLogger
@interface FileLogger : NSObject <Logger>
- (void)logMessage:(NSString *)message;
@end

@implementation FileLogger
- (void)logMessage:(NSString *)message {
    NSLog(@"File logger: %@", message);
}
@end

// 具体产品类 NetworkLogger
@interface NetworkLogger : NSObject <Logger>
- (void)logMessage:(NSString *)message;
@end

@implementation NetworkLogger
- (void)logMessage:(NSString *)message {
    NSLog(@"Network logger: %@", message);
}
@end

// 抽象工厂类
@interface LoggerFactory : NSObject
- (id<Logger>)createLogger;
@end

// 具体工厂类 FileLoggerFactory
@interface FileLoggerFactory : LoggerFactory
- (id<Logger>)createLogger;
@end

@implementation FileLoggerFactory
- (id<Logger>)createLogger {
    return [[FileLogger alloc] init];
}
@end

// 具体工厂类 NetworkLoggerFactory
@interface NetworkLoggerFactory : LoggerFactory
- (id<Logger>)createLogger;
@end

@implementation NetworkLoggerFactory
- (id<Logger>)createLogger {
    return [[NetworkLogger alloc] init];
}
@end

// 使用
LoggerFactory *factory;
if (useFileLogging) {
    factory = [[FileLoggerFactory alloc] init];
} else {
    factory = [[NetworkLoggerFactory alloc] init];
}
id<Logger> logger = [factory createLogger];
[logger logMessage:@"This is a test log."];

在这个例子中,LoggerFactory 是一个抽象类,定义了一个名为 createLogger 的方法,但并没有实现它。这个方法的具体实现由 FileLoggerFactoryNetworkLoggerFactory 这两个子类根据不同的业务逻辑来提供。这样,每个工厂子类决定了要实例化的具体 Logger 类,父类不需要知道具体的实现细节。

这种模式的优势在于,它支持易于扩展和修改的开放封闭原则,同时减少了类间的耦合。

我们再来看一个场景:
假设我们正在开发一个简单的绘图应用,该应用可以绘制不同类型的图形。我们可以使用工厂方法模式来创建不同类型的图形对象。

步骤 1: 定义图形接口和具体图形类。

// Shape.h
#import <Foundation/Foundation.h>

@protocol Shape <NSObject>
- (void)draw;
@end

// Circle.h
#import "Shape.h"

@interface Circle : NSObject <Shape>
@end

@implementation Circle
- (void)draw {
    NSLog(@"Drawing a circle.");
}
@end

// Rectangle.h
#import "Shape.h"

@interface Rectangle : NSObject <Shape>
@end

@implementation Rectangle
- (void)draw {
    NSLog(@"Drawing a rectangle.");
}
@end

步骤 2: 定义抽象工厂类和具体工厂类。

// ShapeFactory.h
#import <Foundation/Foundation.h>
#import "Shape.h"

@interface ShapeFactory : NSObject
- (id<Shape>)createShape;
@end

// CircleFactory.h
#import "ShapeFactory.h"
#import "Circle.h"

@interface CircleFactory : ShapeFactory
@end

@implementation CircleFactory
- (id<Shape>)createShape {
    return [[Circle alloc] init];
}
@end

// RectangleFactory.h
#import "ShapeFactory.h"
#import "Rectangle.h"

@interface RectangleFactory : ShapeFactory
@end

@implementation RectangleFactory
- (id<Shape>)createShape {
    return [[Rectangle alloc] init];
}
@end

步骤 3: 使用工厂类。

// main.m
#import <Foundation/Foundation.h>
#import "CircleFactory.h"
#import "RectangleFactory.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 创建圆形工厂和矩形工厂
        ShapeFactory *circleFactory = [[CircleFactory alloc] init];
        ShapeFactory *rectangleFactory = [[RectangleFactory alloc] init];

        // 使用工厂方法创建形状
        id<Shape> circle = [circleFactory createShape];
        id<Shape> rectangle = [rectangleFactory createShape];

        // 绘制形状
        [circle draw];
        [rectangle draw];
    }
    return 0;
}

主程序依赖于抽象接口(Shape 协议和 ShapeFactory 类),而不是具体的类

我们来理解一下这句话

想象我们正在开发一个图形软件,软件需要支持多种类型图形,我们此时不需要知道操作的图形具体是什么,使用接口来定义这些操作,可以让你的软件支持任意类型的图形

定义抽象接口(如 Shape)

@protocol Shape <NSObject>
- (void)draw;
@end

实现具体类(如 Circle 和 Rectangle):

@interface Circle : NSObject <Shape>
@end

@implementation Circle
- (void)draw {
    NSLog(@"Drawing a circle.");
}
@end

@interface Rectangle : NSObject <Shape>
@end

@implementation Rectangle
- (void)draw {
    NSLog(@"Drawing a rectangle.");
}
@end

CircleRectangle 类都实现了 Shape 协议,但具体的绘制逻辑根据图形的类型不同而不同。

抽象工厂模式

抽象工厂模式与工厂模式关键区别在于抽象工厂是用来创建一系列产品的,每个产品可以属于不同的产品类别,而工厂方法一般只创建一种产品。

场景:
假设我们有一个应用程序,需要支持多种界面风格,如“暗黑模式”和“亮色模式”,每种风格都有一套不同的界面组件(如按钮、文本框等)。我们可以使用抽象工厂模式来根据用户的选择动态提供相应的组件。

  • 步骤 1: 定义抽象产品和具体产品

首先,我们定义两种产品接口:Button 和 TextField,以及它们的具体实现。

// Button.h
@protocol Button <NSObject>
- (void)display;
@end

// DarkButton.h
#import "Button.h"

@interface DarkButton : NSObject <Button>
@end

@implementation DarkButton
- (void)display {
    NSLog(@"Displaying dark button");
}
@end

// LightButton.h
#import "Button.h"

@interface LightButton : NSObject <Button>
@end

@implementation LightButton
- (void)display {
    NSLog(@"Displaying light button");
}
@end

// TextField.h
@protocol TextField <NSObject>
- (void)display;
@end

// DarkTextField.h
#import "TextField.h"

@interface DarkTextField : NSObject <TextField>
@end

@implementation DarkTextField
- (void)display {
    NSLog(@"Displaying dark text field");
}
@end

// LightTextField.h
#import "TextField.h"

@interface LightTextField : NSObject <TextField>
@end

@implementation LightTextField
- (void)display {
    NSLog(@"Displaying light text field");
}
@end
  • 步骤 2: 定义抽象工厂和具体工厂

定义一个抽象工厂接口,GUIFactory,以及为每种界面风格实现一个具体工厂。

// GUIFactory.h
#import "Button.h"
#import "TextField.h"

@protocol GUIFactory <NSObject>
- (id<Button>)createButton;
- (id<TextField>)createTextField;
@end

// DarkGUIFactory.h
#import "GUIFactory.h"
#import "DarkButton.h"
#import "DarkTextField.h"

@interface DarkGUIFactory : NSObject <GUIFactory>
@end

@implementation DarkGUIFactory
- (id<Button>)createButton {
    return [DarkButton new];
}
- (id<TextField>)createTextField {
    return [DarkTextField new];
}
@end

// LightGUIFactory.h
#import "GUIFactory.h"
#import "LightButton.h"
#import "LightTextField.h"

@interface LightGUIFactory : NSObject <GUIFactory>
@end

@implementation LightGUIFactory
- (id<Button>)createButton {
    return [LightButton new];
}
- (id<TextField>)createTextField {
    return [LightTextField new];
}
@end
  • 步骤 3: 使用抽象工厂

客户端代码现在可以根据用户的偏好选择使用合适的工厂,而不需要关心具体的产品如何创建。

// main.m
#import <Foundation/Foundation.h>
#import "DarkGUIFactory.h"
#import "LightGUIFactory.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        id<GUIFactory> factory;
        BOOL useDarkMode = YES;  // 用户选择使用暗黑模式

        if (useDarkMode) {
            factory = [[DarkGUIFactory alloc] init];
        } else {
            factory = [[LightGUIFactory alloc] init];
        }

        id<Button> button = [factory createButton];
        id<TextField> textField = [factory createTextField];

        [button display];
        [textField display];
    }
    return 0;
}

关于三兄弟的升级与降级

  • 单一类型产品比较少时,用简单工厂模式。
  • 单一类型产品各种定制比较多时,用工厂模式。
  • 多种类型产品时,使用抽象工厂模式。

注意

我们注意到定义产品类前我们通常会先定义一个抽象产品接口,也就是协议,例如:

// 定义产品接口
@protocol Logger <NSObject>
- (void)logMessage:(NSString *)message;
@end

// 具体产品类 FileLogger
@interface FileLogger : NSObject <Logger>
- (void)logMessage:(NSString *)message;
@end

同时还有在主程序中创建对象

// 使用工厂方法创建形状
        id<Shape> circle = [circleFactory createShape];
        id<Shape> rectangle = [rectangleFactory createShape];

这就符合我们的依赖倒置原则

主程序依赖于抽象接口(Shape 协议和 ShapeFactory 类),而不是具体的类

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

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

相关文章

vue一个简易时钟

<template><div class"">时间{{ time }}<div class"base1"><div class"move-to-center line"></div><div class"move-to-center line line2"></div><div class"move-to-center lin…

Stable Diffusion入门使用技巧及个人实例分享--大模型及lora篇

大家好&#xff0c;近期使用Stable Diffusion比较多&#xff0c;积累整理了一些内容&#xff0c;得空分享给大家。如果你近期正好在关注AI绘画领域&#xff0c;可以看看哦。 本文比较适合已经解决了安装问题&#xff0c;&#xff08;没有安装的在文末领取&#xff09; 在寻找合…

wireshark_概念

ARP (Address Resolution Protocol&#xff09;协议&#xff0c;即地址解析协议。该协议的功能就是将IP地址解析成MAC地址。 混杂模式 抓取经过网卡的所有数据包&#xff0c;包括发往本网卡和非发往本网卡的。 非混杂模式 只抓取目标地址是本网卡的数据包&#xff0c;对于发往…

鲁大师2023两轮电动车行业调研报告

自2021年3月起&#xff0c;鲁大师已经连续两年发布涵盖了电动自行车、轻便电动摩托、中高端电动摩托等品类的《电动两轮车行业报告》。如今&#xff0c;在持续进军两轮电动车评测的基础上&#xff0c;通过线上线下多维度深入调研&#xff0c;鲁大师拟于近期发布《2023两轮电动车…

修改远程服务器Nginx默认端口

目录 前言 正文 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1f46f; I’m studying in University of Nottingham Ningbo China&#x1f4eb; You can reach me by url below:My Blo…

【python数据预处理系列】使用Pandas的factorize()函数进行类别编码(整数编码)

在Pandas中&#xff0c;factorize()函数主要用于将分类变量转换为整数编码&#xff0c;这对于减少内存使用或准备数据进行某些统计分析非常有用。 它实际上是将列的唯一值映射到从0开始的整数序列上。 假设有一个DataFrame&#xff0c;其中一列包含一些类别值&#xff0c;你希望…

关于电源1

电源的定义 广义定义&#xff1a;电源是将其它形式的能转换成电能的装置。 例如&#xff1a;发电机&#xff1a;将热能、水能、风能、核能、光照、震动等转化为电能的装置。 电池&#xff1a;将化学能转换为电能。 狭义定义&#xf…

哪个品牌led灯好?五大公认最好用的护眼台灯推荐!

哪个品牌led灯好&#xff1f;经过查找信息之后可以明确地看到市面上受好评比较多的护眼台灯是书客、松下、飞利浦等品牌&#xff0c;我也精心挑选了五款公认最优秀的护眼台灯进行推荐&#xff01;在现代生活中&#xff0c;护眼台灯不仅是照明工具&#xff0c;更是关乎眼部健康的…

GPT-4o API 全新版本发布:提升性能,增加性价比

5月13日&#xff0c;OpenAI 发布了全新ChatGPT模型 GPT-4o&#xff0c;它在响应速度和多媒体理解上都有显著提升。在这篇文章中&#xff0c;我们将介绍 GPT-4o 的主要特点及其 API 集成方式。 什么是 GPT-4o&#xff1f; GPT-4o 是 OpenAI 于5月13日发布的最新多模态 AI 模型…

嵌入式详细教程:基于STM32实现语音识别系统

目录 文章主题环境准备语音识别系统基础代码示例&#xff1a;实现语音识别系统应用场景&#xff1a;智能家居与便携设备问题解决方案与优化 1. 文章主题 文章主题 本教程将详细介绍如何在STM32嵌入式系统中使用C语言实现语音识别系统&#xff0c;特别是如何通过STM32与麦克风…

HTTP代理可以应用在那些领域呢

HTTP代理是IP代理领域中一个重要组成部分&#xff0c;它基于HTTP协议传输&#xff0c;使用海外服务器帮助用户绕开访问限制&#xff0c;浏览查看海外资讯信息。 HTTP代理可以应用在哪些领域呢&#xff1f; 1.保护使用者隐私 当今越来越数据被上传到网络云端上&#xff0c;用户…

网关过滤器实现接口签名检验

背景 往往项目中的开放接口可能被别有用心者对其进行抓包然后对请求参数进行篡改&#xff0c;或者重复请求占用系统资源为此我们行业内使用比较多的策略是接口签名校验。签名校验的实现可以用注解aop的形式实现&#xff0c;也可以使用过滤器统一拦截校验实现&#xff0c;此篇文…

从新手到高手,教你如何改造你的广告思维方式!

想要广告震撼人心又让人长时间记住&#xff1f;答案肯定是“创意”二字。广告创意&#xff0c;说白了就是脑洞大开&#xff0c;想法新颖。那些很流行的广告&#xff0c;都是因为背后的想法特别、新颖。做广告啊&#xff0c;就得不停地思考&#xff0c;创新思维是关键。 广告思…

智能数据提取:在严格数据治理与安全标准下的实践路径

一、引言 随着信息技术的飞速发展&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;数据量的爆炸式增长和数据格式的多样化&#xff0c;使得传统的数据提取方法变得效率低下且难以满足业务需求。智能数据提取技术应运而生&#xff0c;它通过应用人工智能和机器学…

基于Springboot汽车租赁预约管理系统

一&#xff1a;功能介绍 本系统是Springboot项目采用的技术栈主要有Spring、mybaits、springboot、mysql数据库 功能角色主要分为管理员、超级管理员、用户等几个角色 二&#xff1a;功能截图 三&#xff1a;源码获取

CheckStyle静态样式之道

优质博文&#xff1a;IT-BLOG-CN 在标准化的统一样式检查规范里&#xff0c;最为常用的统一样式工具是checkstyle插件&#xff0c;而不是国内阿里的代码规约插件。 【1】下载插件 【2】配置生效 配置生效及告警设置 【3】配置checkstyle.xml 官网地址 官网最新Releases 下面…

2024年京东618红包领取口令是什么?2024年618京东红包活动时间是从什么时候开始到几号结束?

2024年京东618红包活动时间 京东618红包活动时间是从2024年5月28日开始&#xff0c;一直持续到6月18日结束。 2024年京东618红包领取方式 在2024年京东618活动时间内&#xff0c;每天都可以打开手机京东APP&#xff0c;输入框搜索红包领取口令「 天降红包882 」&#xff0c;搜…

253 基于matlab的液压位置控制源代码

基于matlab的液压位置控制源代码&#xff0c;有摩擦补偿&#xff0c;利用滑模控制器实现&#xff0c;神经网络逼近。最后实现位置角度和速度的控制。输出控制误差。程序已调通&#xff0c;可直接运行。 253 液压位置控制 滑模控制器 控制误差 - 小红书 (xiaohongshu.com)

Excel 每 N 列内容填成一行

Excel表格从第 2 列起&#xff0c;每 N 列为一组&#xff0c;以 N2 为例&#xff1a; ABCDEFG1IDType 1Count 1Type 2Count 2Type 3Count 321a640d290a32d12000a1900f600043f48000f3600e160054c46000e3100b120065e47000c3400d140076b64000b3600c1200 现在要进列转行&#xff…

5G技术相关部分图解

1、面向5G商用网络的全系列解决方案 面向5G商用网络的全系列解决方案涵盖了从核心网到接入网的各个方面&#xff0c;确保网络的高性能、高可靠性和高安全性 2、2\3\4\5G带宽图解 G带宽的提升将推动许多新型应用的发展&#xff0c;并提供更快速、更可靠的移动通信体验。然而…