iOS——设计模式之工厂模式

谈谈设计模式

什么是设计模式?

维基百科中的设计模式 (计算机)

在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。
设计模式并不直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案。面向对象设计模式通常以类别或对象来描述其中的关系和相互作用,但不涉及用来完成应用程序的特定类别或对象。设计模式能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免会引起麻烦的紧耦合,以增强软件设计面对并适应变化的能力。
并非所有的软件模式都是设计模式,设计模式特指软件“设计”层次上的问题。还有其他非设计模式的模式,如架构模式。同时,算法不能算是一种设计模式,因为算法主要是用来解决计算上的问题,而非设计上的问题。

可以这么说,计算机中设计模式指的是一套广为人知、被反复使用、经过分类编目的代码设计经验。使用设计模式是为了可重用代码,让代码更容易被他人理解,最重要的是保证代码可靠性。

设计模式七大原则

一篇很好的iOS设计模式原则介绍:iOS开发 - 面向对象设计的六大设计原则(附 Demo & UML类图)

开闭原则

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

单⼀职责原则

一个类只做一件事。每个类应该实现单⼀的职责,如若不然,就应该把类拆分。

eg:UIView负责事件的传递、响应,CALayer负责视图的显示、动画,他们各自都有自己的单一职责。

里氏替换原则

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

eg:
父类

- (void)carName {
NSLog(@"小汽车");
}

子类

//遵循里氏替换原则
- (void)carColor {
  NSLog(@"红色的");
}

//违背里氏替换原则,影响到了父类原有功能
- (void)carName {
  NSLog(@"小汽车,红色的");
}

依赖倒转原则

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

eg:
违背依赖倒转原则

@implementation LiteraryClassic

- (void)read {
    NSLog(@"读文学经典");
}

@end

@implementation XiaoMing

- (void)read:(LiteraryClassic *)literaryClassic {
    [literaryClassic read];
}

@end

这样设计的XiaoMing类没有遵循依赖倒转原则,没有中间接口,导致只能实现“读文学经典”一种功能。

遵循依赖倒转原则

//IRead协议
@protocol IRead<NSObject>

- (void)read;

@end

//LiteraryClassicNew类遵循IRead协议
@interface LiteraryClassic : NSObject<IRead>

@end

@implementation LiteraryClassic

- (void)read {
    NSLog(@"读文学经典");
}

@end

//NovelNew类遵循IRead协议
@interface Novel : NSObject<IRead>

@end

@implementation Novel

- (void)read {

    NSLog(@"读小说");

}

@end

//IReader协议
@protocol IReader<NSObject>

- (void)read:(id<IRead>)iread;

@end

//XiaoMing类遵循IReader协议
@interface XiaoMing : NSObject<IReader>

@end

@implementation XiaoMing

- (void)read:(id<IRead>)iread {

    [iread read];

}

@end

测试功能

id <IReader> ireader = [[XiaoMing alloc] init];
id <IReader> read = [[Novel alloc] init];
[ireader read:read];
read = [[LiteraryClassic alloc] init];
[ireader read:read];

输出

读小说
读文学经典

这样的设计遵循了依赖倒转原则,有了中间接口,依赖于抽象而不依赖于具体,可以实现“读文学经典”、“读小说”两种功能,当有更多需求时还能进行扩展。

接口隔离原则

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

eg:

//违背依赖倒转原则,无法单独控制行驶和开空调
- (void)usedCar {
  NSLog(@"行驶");
  NSLog(@"开空调");
}

//遵循依赖倒转原则,单独控制行驶和开空调
- (void)usedCar {
  NSLog(@"行驶");
}

- (void)openTheAircondition {
  NSLog(@"开空调");
}

迪米特法则

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

eg:

//违背迪米特法则
//类A
- (void)isUseCar {
  if ([B.person isEqualToString: @"男人"]) {
    NSLog(@"开车");
  } else if ([B.person isEqualToString: @"女人"]) {
    NSLog(@"不开车");
  }
}

//遵循迪米特法则
//类A
- (void)useCar {
  if (B.isUseCar) {
    NSLog(@"开车");
  } else {
     NSLog(@"不开车");
  }
}

//类B
- (BOOL)isUseCar {
  if ([B.person isEqualToString: @"男人"]) {
    return YES;
  } else if ([B.person isEqualToString: @"女人"]) {
    return NO;
  }
}

遵循迪米特法则进行设计,后面对B类中的判断条件进行更改,不会影响到A类,只需更改B类中代码即可。这样达到了高内聚、低耦合的目标。

合成复用原则

合成复用原则是尽量⾸先使用合成/聚合的⽅式,而不是使用继承。此原则和里氏替换原则氏相辅相成的,两者都是详细实现"开-闭"原则的规范。我们先看什么是合成和聚合:

  • 合成
    合成是指一个总体对依托他而存在的关系,如一个人对他的房子和家具。该关系依赖性不强,比如人没了,这个关系就自然消失了。

  • 聚合
    聚合是比合成关系更强的一种依赖关系,如有一台汽车,汽车对引擎、轮胎的关系就是聚合关系。这些关系就是带有聚合性质的。车没了,该车的引擎和轮胎自然也没了。在我们的设计中,这种关系不应该频繁出现,因为这样会增大设计的耦合度。

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

设计模式三大分类

iOS开发常用的23种设计模式简介

创建型模式

通常和对象的创建有关,涉及到对象实例化的方式。

主要模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。

结构型模式

描述的是如何组合类和对象以获得更大的结构。

主要模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

行为型模式

用来对类或对象怎样交互和怎样分配职责进行描述。

主要模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

工厂模式

工厂模式就是我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式分为简单工厂模式,工厂方法模式,抽象工厂模式。

简单工厂模式(Simple Factory Pattern)

定义

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

成员分工

工厂类

工厂类通常包含一个静态方法(类方法),由该方法根据输入类型负责创建具体的产品(对象)。

抽象产品基类(接口)

它的作用是降低客户端和具体产品之间的耦合度。而且符合了“开-闭”原则,以后需要加入新的产品线,客户端调用的代码也基本无需修改。

具体产品类

真正实现业务逻辑的子类。

适用场景

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

举例

在这里插入图片描述
可口可乐和百事可乐继承于可乐抽象类

// 简单工厂实现 
@implementation SimpleFactory

+ (Cola *)createColaWithType:(NSInteger)type { 
	switch (type) { 
	case 0: 
		//可口可乐
		return [[CocaCola alloc] init]; 
	case 1: 
		//百事可乐
		return [[PesiCola alloc] init]; 
	default: 
		return nil; 
		break; 
	} 
} 

@end

使用工厂产品

id<ColaDelegate> cocaCola = [SimpleFactory createColaWithType:@"0"];
[cocaCola sell];
    
id<ColaDelegate> pesiCola = [SimpleFactory createColaWithType:@"1"];
[pesiCola sell];

优点

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

缺点

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

工厂方法模式(Factory Method Pattern)

定义

工厂方法模式又称为工厂模式,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,即通过不同的工厂子类来创建不同的产品对象。
在这里插入图片描述

适用场景

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

举例

在这里插入图片描述
工厂方法和简单工厂有一些区别,简单工厂是由一个代工厂生产不同的产品,而工厂方法是对工厂进行抽象化,不同产品都由专门的具体工厂来生产。如可口可乐工厂专门生产可口可乐,百事可乐工厂专门生产百事可乐。

//工厂抽象类 
@implementation Factory 
+ (Cola *)createCola {
 	return [[Cola alloc] init];
 }
 @end
 
//可口可乐工厂
@implementation CocaColaFactory
+ (Cola *)createCola {
	return [[CocaCola alloc] init];
}
@end

//百事可乐工厂
@implementation PesiColaFactory
+ (Cola *)createCola {
	return [[PesiCola alloc] init];
}
@end

优点

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

缺点

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

抽象工厂模式(Abstract Factory Pattern)

定义

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
在这里插入图片描述

适用场景

  • 将一个系列的产品族统一到一起创建
  • 容易改变产品的系列

抽象工厂和工厂方法的区别(举例)

在这里插入图片描述
生产产品的工厂是抽象的。主题工厂不仅仅生产可口可乐或百事可乐,瓶子和箱子也需要专属定制,专门生产同一主题的不同商品。

优点

  • 不需要生产产品细节,只需要知道产品属于那个工厂就行,当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。

缺点

  • 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
  • 代码结构比较复杂
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值