【iOS】—— 工厂设计模式

article/2025/8/25 0:13:43

工厂设计模式

文章目录

  • 工厂设计模式
    • 设计模式概念
    • 设计模式七大准则
      • 开闭原则
      • 单⼀职责原则
      • 里氏替换原则
      • 依赖倒转原则
      • 接口隔离原则
      • 迪米特法则
      • 合成复用原则
    • 类族模式
    • 简单工厂模式
      • 优点
      • 缺点
      • 主要作用
      • 示例
        • 文件分类
        • 实现效果:
    • 工厂方法模式
      • 优点
      • 缺点
      • 主要作用:
      • 示例:
        • 文件分类
        • 实现效果:
    • 抽象工厂方法
      • 缺点
      • 主要作用:
      • 示例:
        • 文件分类
        • 实现效果
    • git链接

设计模式概念

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

设计模式七大准则

开闭原则

开闭原则的核心是:对扩展开放,对修改关闭。在程序需要进⾏拓展的时候,不能去修改原有的代码,⽽是要扩展原有代码,实现⼀个热插拔的效果。所以⼀句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使⽤接⼝和抽象类等,大部分具体设计模式中反复应用这一原则。

单⼀职责原则

一个类只做一件事。每个类应该实现单⼀的职责,如若不然,就应该把类拆分。
比如:UIView负责事件的传递、响应,CALayer负责视图的显示、动画,他们各自都有自己的单一职责。

里氏替换原则

里氏替换原则的主要内容:任何基类可以出现的地方,子类⼀定可以出现。该原则是继承复⽤的基⽯,只有当衍⽣类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。原则上,⼦类对父类的方法尽量不要重写和重载,因为⽗类代表了定义好的结构,通过这个规范的接⼝与外界交互。除非万不得已,⼦类不应该随便破坏它。

依赖倒转原则

面向接口编程,依赖于抽象而不依赖于具体。高层模块不应该依赖底层模块,二者都应该依赖其抽象;我们可以依赖抽象类也可以依赖接口,但是不要依赖具体的子类或者实现类。依赖倒转原则是开闭原则的基础。

接口隔离原则

每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使⽤多个隔离的接口,⽐使用单个接口(多个接口⽅法集合到⼀个的接口)要好。

迪米特法则

迪米特法则的核心为:⼀个类对自己依赖的类知道的越少越好。也就是说⽆论被依赖的类多么复杂,都应该将逻辑封装在⽅法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
最少知道原则的另⼀个表达⽅式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现成员变量、⽅法参数、⽅法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。

合成复用原则

合成复用原则是尽量⾸先使用合成/聚合的⽅式,而不是使用继承。此原则和里氏替换原则氏相辅相成的,两者都是详细实现"开-闭"原则的规范。我们先看什么是合成和聚合:
合成
合成是指一个总体对依托他而存在的关系,如一个人对他的房子和家具。该关系依赖性不强,比如人没了,这个关系就自然消失了。
聚合
聚合是比合成关系更强的一种依赖关系,如有一台汽车,汽车对引擎、轮胎的关系就是聚合关系。这些关系就是带有聚合性质的。车没了,该车的引擎和轮胎自然也没了。在我们的设计中,这种关系不应该频繁出现,因为这样会增大设计的耦合度。

明确了合成和聚合关系,再来理解合成复用原则应该就清楚了:我们要尽量找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

类族模式

在之前学习小蓝书的时候我们提到过类族模式,在学习工厂模式之前我们先来看看类族模式,具体关于类族模式可以看看我之前博客:
【Effective Objective - C】—— 读书笔记(二)

其中类族模式最重要的一点作用是:把实现细节隐藏在一套简单的公共接口后面,在学习完工厂模式之后,我们会发现,工厂模式和类族模式在目的上和实现细节上有很大的相同点。

简单工厂模式

在这里插入图片描述

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

优点

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

缺点

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

主要作用

通过引入工厂类,使对象的创建和使用分离了。这样的好处是它们可以独立的变化,易维护和扩展。
客户端依赖抽象基类(接口),而不是具体的类,降低了耦合度。

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

示例

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

//  PhoneFactory.mNSArray *array = @[@"vivo", @"oppo", @"xiaomi", @"apple"];
+ (id)createPhone:(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];break;default:break;}return nil;
}

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

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

在各类中实现此方法

#import "Vivo.h"@implementation Vivo
- (void)phoneWays {NSLog(@"vivo");
}
@end

文件分类

在这里插入图片描述

实现效果:

请添加图片描述
在这里插入图片描述

分析一下,简单工厂方法和类族模式主要区别就是,类族模式实现各子类方法通过继承去重写父类方法,而简单工厂方法中生成的各类和工厂类并不是父子类关系,通过协议来完成各方法。

工厂方法模式

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

优点

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

缺点

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

主要作用:

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

示例:

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

工厂类:

//  PhoneCenter.m
@implementation PhoneCenter
- (void)beginProductionPhone {NSLog(@"begin");
}
- (void)succeedProductionPhone {NSLog(@"succeed");
}
@end

子类:

@interface Vivo : PhoneCenter@end@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厂:

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

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

//  BaseFactory.h
@interface BaseFactory : NSObject
- (BasePhone*)createPhone;
- (BaseWatch*)createWatch;
@end//  BaseFactory.m
@implementation BaseFactory
- (BasePhone*)createPhone {return nil;
}
- (BaseWatch*)createWatch {return nil;
}
@end
//  AppleFactory.h@interface AppleFactory : BaseFactory@end//  AppleFactory.m@implementation AppleFactory
- (BasePhone *)createPhone {return [[ApplePhone alloc] init];
}- (BaseWatch *)createWatch {return [[AppleWatch alloc] init];
}

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

//  BasePhone.h@interface BasePhone : NSObject
- (void)phoneCell;
@end//  BasePhone.m
@implementation BasePhone
- (void)phoneCell {NSLog(@"This is a Phone");
}
@end

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

//  ApplePhone.h@interface ApplePhone : BasePhone
- (void)applePhoneWays;
@end//  ApplePhone.m
@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];

实现效果

在这里插入图片描述

git链接

类族模式
工厂模式

这一年最后一篇博客了,希望在下一年里继续努力,早日实现梦想。


http://chatgpt.dhexx.cn/article/vysiQYpI.shtml

相关文章

【设计模式】工厂模式(Factory Pattern)

1. 概述 工厂模式&#xff08;Factory Pattern&#xff09;是最常用的设计模式之一&#xff0c;它属于创建类型的设计模式。它提供了一种创建对象的最佳方式&#xff0c;在工厂模式中&#xff0c;我们在创建对象时不会对客户端暴露创建逻辑&#xff0c;并且是通过一个共同的接…

设计模式之工厂模式,史上最强,不服来辩!

设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结&#xff0c;如果设计模式没学会&#xff0c;抽象能力肯定就不会太强。常见的设计模式有 23 种&#xff0c;今天我们只聊最简单的工厂模式。 工厂模式是属于创建型模式的&#xff0c;通过工厂获取一个一个的新对象。…

工厂设计模式

1、概念 工厂模式分三种&#xff1a;简单工厂模式、工厂方法模式、抽象工厂模式 简单工厂模式(Simple Factory Pattern)&#xff1a;属于类的创新型模式&#xff0c;又叫静态工厂方法模式(Static FactoryMethod Pattern),是通过专门定义一个类来负责创建其他类的实例&#xf…

设计模式之工厂模式(factory pattern)

工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。该模式用于封装和管理对象的创建,是一种创建型模式。本文从一个具体的例子逐步深入分析,来体会三种工厂模式的应用场景和…

sqlserver2008的SSMS连接sqlserver2016的时候提示‘索引超出了数组界限。’

解决&#xff1a;打sp3补丁。 http://www.microsoft.com/en-us/download/details.aspx?id44271

Matlab报错 :“位置 x 处的索引超出数组边界”

经常遇到“位置 x 索引超出数组边界”的报错&#xff0c;今天突然想到一个以前一直没有太留意的奇葩问题&#xff1a;这个报错里“位置x”指的是哪里&#xff1f;为什么一会是“位置3”&#xff0c;一会是“位置1”&#xff0c;有什么神秘的规矩吗&#xff1f; 善用搜索没发现…

.net reflector 反编译失败 索引超出了数组界限问题处理方法

.net reflector 反编译失败 索引超出了数组界限问题处理方法 时间&#xff1a;9个月前 作者&#xff1a;庞顺龙 浏览&#xff1a;177 [站内原创&#xff0c;转载请注明出处] 标签&#xff1a; Reflector .net reflector 反编译失败 索引超出了数组界限问题处理方法 de…

matlab索引超出数组边界且不提示数组边界的一种处理办法

问题如下。 相关代码如下&#xff1a; 问题原因如下&#xff1a; 将excel表另存为txt时选择的保存类型是Unicode文本(*.txt)。 处理办法。 将Unicode文本(*.txt)换成文本文件&#xff08;制表符分隔&#xff09;(*.txt)。 问题的表现如下&#xff1a; 利用matlab工作区发现…

最短路径算法详细介绍

据 Drew 所知最短路经算法现在重要的应用有计算机网络路由算法&#xff0c;机器人探路&#xff0c;交通路线导航&#xff0c;人工智能&#xff0c;游戏设计等等。美国火星探测器核心的寻路算法就是采用的D*&#xff08;D Star&#xff09;算法。 最短路经计算分静态最短路计算和…

[算法]-最短路径算法总结

Dijkstra最短路径算法 按路径长度的递增次序&#xff0c;逐步产生最短路径的贪心算法 基本思想&#xff1a;首先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径&#xff0c;依次类推&#xff0c;直到从顶点v 到其它各顶点的最短路径全部求出为止。 时间复杂…

c++实现最短路径算法(Dijkstra算法)

0简介 Dijkstra算法是一种计算某一顶点到其余顶点最短路径的算法。它采用了贪心的策略&#xff0c;实现时使用了广度优先。这个算法常学常忘&#xff0c;因此写篇博客彻底解决它。 1计算步骤介绍 Dijkstra的计算步骤不难&#xff0c;如下 (1)设置一个集合表示已经标记过的节…

最短路径算法——dijkstra

dijkstra 前提&#xff1a;在一张图里&#xff0c;不能有权值为负数的边。 给你一个出发点&#xff0c;求出发点到所有结点的最短距离是多少&#xff1f;如果无法到达某个点&#xff0c;则到这个点的距离是正无穷&#xff08;一般出现在有向图里面&#xff09;。 举个&#…

最短路径算法详解

前言&#xff1a; 最短路径算法&#xff1a;用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展&#xff0c;直到扩展到终点为止。 例子&#xff1a; 暑期&#xff0c;你想要出门去旅游&#xff0c;但是在你出发之前&#xff0c;你想知道任意…

基于java最短路径算法的简单实现

一、我们先画一个可达路径图表&#xff1a; -1表示不可直达&#xff0c;0表示自己&#xff0c;其他整数表示长度或者权重 ABCDA012-1B-1015C-1-101D-1-1-10 用图形可表示为&#xff1a; 二、算法思路 1、先列出所有保存所有节点到可达节点的路径二维表 2、逐步寻找从起始节点到…

最短路径算法及应用

乘汽车旅行的人总希望找出到目的地的尽可能的短的行程。如果有一张地图并在图上标出每对十字路口之间的距离&#xff0c;如何找出这一最短行程&#xff1f;  一种可能的方法就是枚举出所有路径&#xff0c;并计算出每条路径的长度&#xff0c;然后选择最短的一条。那么我们很…

关于最短路径算法的理解

“最短路径算法:Dijkstra算法&#xff0c;Bellman-Ford算法&#xff0c;Floyd算法和SPFA算法等。​从某顶点出发&#xff0c;沿图的边到达另一顶点所经过的路径中&#xff0c;各边上权值之和最小的一条路径叫做最短路径。” 我们解决最短路径问题&#xff0c;常用的是Dijkstra…

最短路径算法-----Dijkstra迪杰斯特拉算法

最近巩固一下算法&#xff0c;提高自己内力&#xff0c;网上看到查看到这篇介绍很详细的《Dijkstra迪杰斯特拉算法》&#xff0c;在这里转载记录一下。 1 前言 本章介绍迪杰斯特拉算法。和以往一样&#xff0c;本文会先对迪杰斯特拉算法的理论论知识进行介绍&#xff0c;然后给…

Dijkstra 最短路径算法 Python 实现

原文链接 问题描述 使用 Dijkstra 算法求图中的任意顶点到其它顶点的最短路径&#xff08;求出需要经过那些点以及最短距离&#xff09;。 以下图为例&#xff1a; 算法思想 可以使用二维数组来存储顶点之间边的关系 首先需要用一个一维数组 dis 来存储 初始顶点到其余各个顶…

神经网络最短路径算法,最短路径算法的原理

节约里程法求解最短路问题 你只要记住2点之间直线最短。节约里程法是用来解决运输车辆数目不确定的问题的最有名的启发式算法。1、节约里程法优化过程分为并行方式和串行方式两种。 核心思想是依次将运输问题中的两个回路合并为一个回路&#xff0c;每次使合并后的总运输距离…

最短路径算法及Python实现

最短路径问题 在图论中&#xff0c;最短路径问题是指在一个有向或无向的加权图中找到从一个起点到一个终点的最短路径。这个问题是计算机科学中的一个经典问题&#xff0c;也是许多实际问题的基础&#xff0c;例如路线规划、通信网络设计和交通流量优化等。在这个问题中&#…