iOS开发——工厂设计模式

  • 什么是设计模式
  • 设计模式的七大原则
  • 开闭原则
  • 单⼀职责原则
  • 里氏替换原则
  • 依赖倒转原则
  • 接口隔离原则
  • 迪米特法则
  • 合成复用原则
  • 工厂的三种设计模式
  • 简单工厂模式(Simple Factory Pattern)
  • 工厂方式模式(Factory Method Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)

在学习工厂设计模式前我们需要了解两个问题,一个是什么是设计模式,一个是设计模式的七大原则。

什么是设计模式

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

设计模式的七大原则

开闭原则

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

单⼀职责原则

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

里氏替换原则

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

依赖倒转原则

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

接口隔离原则

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

迪米特法则

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

合成复用原则

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

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

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

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

工厂的三种设计模式

简单工厂模式(Simple Factory Pattern)

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

优点:

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

缺点:

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

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

抽象产品基类(接口):它的作用是降低客户端和具体产品之间的耦合度。而且符合了开闭原则,以后需要加入新车型,客户端调用的代码也基本无需修改。

具体产品类:真正实现业务逻辑的子类。

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

适用的场景:

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

简单工厂模式的简单示例:
首先创建一个工厂,工厂通过传递的NSString *类型的值进行判断所属类型的值进行判断,从而通过switch语句判断,然后进行所对应的实例创建:

核心代码

//工厂方法

+ (id) createLetterClass : (NSString *) letter {
    //NSLog(@"%@", letter);
    NSArray *letterArray = @[@"1", @"2", @"3"];
    //NSLog(@"%ld", [letterArray indexOfObject:letter]);
    switch ([letterArray indexOfObject:letter]) {
        case 0:
            return [[OneClass alloc] init];
            break;
        case 1:
            return [[TwoClass alloc] init];
            break;
        case 2:
            return [[ThreeClass alloc] init];
    }
    return NULL;
}

通过button的点击事件中调用工厂方法创建不同的类的实例

- (void)viewDidLoad {
    [super viewDidLoad];
    _testTextField = [[UITextField alloc]initWithFrame:CGRectMake(100, 100, 200, 40)];
        _testTextField.backgroundColor = [UIColor yellowColor];
        [self.view addSubview:_testTextField];
           
        _testButton = [UIButton buttonWithType:UIButtonTypeSystem];
        [_testButton setTitle:@"确定" forState:UIControlStateNormal];
        [self.view addSubview:_testButton];
        [_testButton setFrame:CGRectMake(100, 240, 200, 200)];
        [_testButton addTarget:self action:@selector(pressButton) forControlEvents:UIControlEventTouchUpInside];
        
}

- (void) pressButton {
    id letterClass = [EasyFactrory createLetterClass:_testTextField.text];
    [letterClass printClassName];
}

所有需要用到的类:

ionic ios开发模式 苹果手机开发模式_ios

效果图:

ionic ios开发模式 苹果手机开发模式_工厂类_02

ionic ios开发模式 苹果手机开发模式_设计模式_03

工厂方式模式(Factory Method Pattern)

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

使用的场景:

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

优势:
和直接创建具体对象相比,使用工厂方法创建对象算是最佳的做法。

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

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

工厂方式模式的简单示例:
核心代码:

- (void)viewDidLoad {
	ClassCenter *testA = [[AClass alloc] init];
    [testA createLetter];
    [testA printClassName];
    
    ClassCenter *testB = [[BClass alloc] init];
    [testB createLetter];
    [testB printClassName];
    
    ClassCenter *testC = [[CClass alloc] init];
    [testC createLetter];
    [testC printClassName];
}
@interface ClassCenter : NSObject
//是那些其他子类需要重写的方法
- (void) printClassName;

//是那些其他子类需要重写的另一个方法
- (void) createLetter;
@end

运行结果:

ionic ios开发模式 苹果手机开发模式_ios_04

抽象工厂模式(Abstract Factory Pattern)

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

提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。

ionic ios开发模式 苹果手机开发模式_设计模式_05

抽象工厂模式的简单示例:

核心代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _testTextField = [[UITextField alloc]initWithFrame:CGRectMake(100, 100, 200, 40)];
    _testTextField.backgroundColor = [UIColor blueColor];
    _testTextField.textColor = [UIColor whiteColor];
    [self.view addSubview:_testTextField];
    
    _testTextFieldSecond = [[UITextField alloc]initWithFrame:CGRectMake(100, 200, 200, 40)];
    _testTextFieldSecond.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:_testTextFieldSecond];
    
    _testButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [_testButton setTitle:@"确定" forState:UIControlStateNormal];
    [self.view addSubview:_testButton];
    [_testButton setFrame:CGRectMake(100, 240, 200, 200)];
    [_testButton addTarget:self action:@selector(pressButton) forControlEvents:UIControlEventTouchUpInside];

}
//按钮的事件函数
- (void) pressButton {
    ClassCenter *centerTest = [[ClassCenter alloc] init];
    //传输入框中写的参数
    [centerTest createProductWith:_testTextField.text :_testTextFieldSecond.text];
}
- (void) createProductWith: (NSString *) stringFirst : (NSString *) stringSecond {
    if ([stringFirst isEqualToString:@"A"] && [stringSecond isEqualToString:@"B"]) {
        AClass *aClass = [[AClass alloc] init];
        [aClass createAproduct];
        
        BClass *bClass = [[BClass alloc] init];
        [bClass createBproduct];
    } else if ([stringFirst isEqualToString:@"A"] && [stringSecond isEqualToString:@"C"]) {
        AClass *aClass = [[AClass alloc] init];
        [aClass createAproduct];
        
        CClass *cClass = [[CClass alloc] init];
        [cClass createCproduct];
    } else if ([stringFirst isEqualToString:@"B"] && [stringSecond isEqualToString:@"A"]) {
        BClass *bClass = [[BClass alloc] init];
        [bClass createBproduct];
        
        AClass *aClass = [[AClass alloc] init];
        [aClass createAproduct];
    } else if ([stringFirst isEqualToString:@"B"] && [stringSecond isEqualToString:@"C"]) {
        BClass *bClass = [[BClass alloc] init];
        [bClass createBproduct];
        
        CClass *cClass = [[CClass alloc] init];
        [cClass createCproduct];
    } else if ([stringFirst isEqualToString:@"C"] && [stringSecond isEqualToString:@"A"]) {
        CClass *cClass = [[CClass alloc] init];
        [cClass createCproduct];
        
        AClass *aClass = [[AClass alloc] init];
        [aClass createAproduct];
    } else {
        CClass *cClass = [[CClass alloc] init];
        [cClass createCproduct];
        
        BClass *bClass = [[BClass alloc] init];
        [bClass createBproduct];
    }
    
}

效果图:

ionic ios开发模式 苹果手机开发模式_工厂类_06

ionic ios开发模式 苹果手机开发模式_设计模式_07

此例子中就是以ClassCenter类为抽象的中心,其余那三个子类AClass、BClass、CClass相当于三个工厂模式,ClassCenter负责调度这三个子类工厂,并将实现放在了子类中。

ionic ios开发模式 苹果手机开发模式_设计模式_08