[iOS]-工厂设计模式


在了解工厂设计模式之前我们先了解一下:

设计模式的七大原则:

  • 单一职责原则: 通俗地讲就是一个类只做一件事
    CALayer:动画和视图的显示。
    UIView:只负责事件传递、事件响应。
  • 开闭原则: 对修改关闭,对扩展开放。
    要考虑到后续的扩展性,而不是在原有的基础上来回修改
  • 接口隔离原则: 使用多个专门的协议、而不是在原有的基础上来回修改
  • 依赖倒置原则: 抽象不应该依赖于具体实现、具体实现可以依赖于抽象。调用接口感觉不到内部是如何操作的。
  • 里氏替换原则: 父类可以被子类无缝替换,且原有的功能不受任何影响,例如KVO
  • 迪米特法则: 一个对象应当对其他对象尽可能少的了解,实现高聚合、低耦合
  • 合成复用原则: 原则是尽量使用合成/聚合的方式,而不是使用继承
    (1)找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代
    码混在一起。
    (2)针对接口编程,而不是针对实现编程。
    (3)为了交互对象之间的松耦合设计而努力

工厂模式是创建型模式,它的作用是创建对象,下面我们就了解:

工厂的三种设计模式:

简单工厂模式(Simple Factory Pattern):

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

逻辑如下图:
请添加图片描述

角色定义:

工厂类:
工厂类通常包含一个静态方法(类方法),由该方法根据输入类型负责创建具体的产品(对象)。
抽象产品基类(接口):
它的作用是降低客户端和具体产品之间的耦合度。而且符合了开闭原则,以后需要加入新车型,客户端调用的代码也基本无需修改。
具体产品类:
真正实现业务逻辑的子类。
解决的问题:
工厂模式的核心思想在于:

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

适用的场景:

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

请添加图片描述
首先我们所用到的类如上图。

接着我们就先创建一个简单工厂类SimpleFactrory,用来实现我们的简单工厂模式,代码如下:
.h文件中:

#import <Foundation/Foundation.h>
#import "AClass.h"
#import "BClass.h"
#import "CClass.h"

NS_ASSUME_NONNULL_BEGIN

@interface SimpleFactrory : NSObject

//根据参数创建相应类的方法;
+ (id) createLetterClass : (NSString *) letter;

@end

NS_ASSUME_NONNULL_END

.m文件中:

#import "SimpleFactrory.h"

@implementation SimpleFactrory

+ (id) createLetterClass : (NSString *) letter {
    //NSLog(@"%@", letter);
    NSArray *letterArray = @[@"A", @"B", @"C"];
    //NSLog(@"%ld", [letterArray indexOfObject:letter]);
    switch ([letterArray indexOfObject:letter]) {
        case 0:
            return [[AClass alloc] init];
            break;
        case 1:
            return [[BClass alloc] init];
            break;
        case 2:
            return [[CClass alloc] init];
    }
    return NULL;
}

@end

可以看到我们在.m文件中实现了根据传入的参数创建相应的类的方法。(这些参数我们待会儿会讲到)

然后我们还定义了一个协议,用来给那几个Class类规定同名方法(在每个类中的方法实现不同),代码如下:

#ifndef Header_h
#define Header_h


#import <Foundation/Foundation.h>

@protocol Print <NSObject>

- (void) printClassName;

@end

#endif /* Header_h */

AClass类的.h文件代码如下:

#import <Foundation/Foundation.h>
#import "Print.h"
NS_ASSUME_NONNULL_BEGIN

@interface AClass : NSObject

@end

NS_ASSUME_NONNULL_END

.m文件代码如下:

#import "AClass.h"

@implementation AClass

//Print协议中的方法
- (void) printClassName {
    NSLog(@"this is AClass!");
}

@end

BClass类的.h文件代码如下:

#import <Foundation/Foundation.h>
#import "Print.h"
NS_ASSUME_NONNULL_BEGIN

@interface BClass : NSObject

@end

NS_ASSUME_NONNULL_END

.m文件代码如下:

#import "BClass.h"

@implementation BClass

//Print协议中的方法
- (void) printClassName {
    NSLog(@"this is BClass!");
}

@end

可以看到都是引人了协议的头文件,CClass类的代码与之类似,此处就不再赘述。

接下类就是我们的客户端的代码实现了:
ViewController.h文件中的代码如下:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end

此处我们并没有定义什么
ViewController.m文件中的代码如下:

#import "ViewController.h"
#import "SimpleFactrory.h"

@interface ViewController ()
@property (nonatomic, strong) UITextField *testTextField;
@property (nonatomic, strong) UIButton *testButton;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    _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 = [SimpleFactrory createLetterClass:_testTextField.text];
    [letterClass printClassName];
}

@end

可以看到我们只需要引入简单工厂SimpleFactrory.h的头文件,然后实现了一个输入框和一个按钮的UI,其中向简单工厂类SimpleFactrory+ (id) createLetterClass : (NSString *) letter;类中所传递的参数就是用户在输入框中输入的内容,然后当用户点击按钮的时候就会利用简单工厂模式执行创建用户需要的相应实例,便于用户使用。
运行效果如下:
请添加图片描述
输入A,打印出:
请添加图片描述请添加图片描述
输入C,打印出:
请添加图片描述
我们可以看到简单工厂模式会根据客户端的输入自定创建出相应的实例,然后用户就可以调用相应实例的方法进行想要进行的操作。

简单工厂方法有两个缺点:

  1. 工厂类只有一个,且静态的工厂方法无法由子类继承,所以工厂的扩展受到限制。
  2. 工厂方法里除了创建对象的代码,还有大量的判断逻辑(if-else)混在里面,方法会变得越来越臃肿。

工厂方法模式(Factory Method Pattern)

角色定义:

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

具体逻辑如图:
请添加图片描述

使用的场景:

  • 编译时无法准确预期需要创建对象的类。
  • 类想要其子类决定在运行时创建什么类型的实例。
  • 类有若干辅助类为其子类,而你想将返回哪个子类这种信息局部化。
    优势:
    和直接创建具体对象相比,使用工厂方法创建对象算是最佳的做法。

工厂方法让客户端可以要求由工厂方法创建的对象拥有一组共同的行为。

因此,向类层次结构中引入新的具体产品并不需要修改客户端代码,因为返回的任何具体对象的接口跟客户端一直使用的接口相同。

代码示例:

请添加图片描述
以上就是我们所用到的全部类与文件。
我们采用的方法是先实现一个ClassCenter的中心类,然后其余的三个AClass、BClass、CClass类都继承与ClassCenter类,并重写了其中定义的方法(子类重写父类方法)的形式进行了工厂模式的实现。
先看ClassCenter.h文件的代码:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ClassCenter : NSObject

//是那些其他子类需要重写的方法
- (void) printClassName;

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

@end

NS_ASSUME_NONNULL_END

ClassCenter.m文件的代码:

#import "ClassCenter.h"

@implementation ClassCenter

//是那些其他子类需要重写的方法
- (void) printClassName {
    NSLog(@"我们将要打印字母!");
}

//是那些其他子类需要重写的另一个方法
- (void) createLetter {
    NSLog(@"我们将要生产字母!");
}

@end

我们看到,该类中定义了两个方法,后续都会交给子类去进行重写。

接着我们看AClass.h文件的代码:

#import "ClassCenter.h"


NS_ASSUME_NONNULL_BEGIN

@interface AClass : ClassCenter

@end

NS_ASSUME_NONNULL_END

可以看到,该类继承自ClassCenter类
再看AClass.m文件的代码:

#import "AClass.h"

@implementation AClass

//重写父类中的方法
- (void) printClassName {
    NSLog(@"打印了 A!");
}

//重写父类中的另一个方法
- (void) createLetter {
    NSLog(@"生产了 A!");
}

@end

我们看到该类重写了其父类定义的两个方法。
接着再看BClass.h文件的代码:

#import "ClassCenter.h"


NS_ASSUME_NONNULL_BEGIN

@interface BClass : ClassCenter

@end

NS_ASSUME_NONNULL_END

BClass.m文件的代码:

#import "BClass.h"

@implementation BClass

//重写父类中的方法
- (void) printClassName {
    NSLog(@"打印了 B!");
}

//重写父类中的另一个方法
- (void) createLetter {
    NSLog(@"生产了 B!");
}

@end

我们看到和AClass类的实现基本类似,就是继承自父类的同名方法中的具体实现不同
此处的CClass类的代码实现与以上基本类似,此处就不多赘述。
最后再看客户端的代码实现:
ViewController.h文件中:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end

此处也没有定义什么
ViewController.m文件中:

#import "ViewController.h"
#import "ClassCenter.h"
#import "AClass.h"
#import "BClass.h"
#import "CClass.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    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];
}

@end

可以看到我们将这些重写父类方法的子类的头文件都引用到了ViewController.m文件中来实现工厂模式功能,这样做显得有些不妥且很臃肿,其实此处也可以结合简单工厂模式让用户创建需要的实例的接口变得更加简洁,但是此处就先用此方法来做演示。

运行效果如下:
请添加图片描述
可以看到我们可以在客户端创建自己所需的实例并使用该实例进行操作。

抽象工厂模式(Abstract Factory Pattern)

角色定义:

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

提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。
请添加图片描述
解决的问题:

  • 将一个系列的产品族统一到一起创建。
  • 容易改变产品的系列。
    适用的场景:
    需要创建一组对象,并需要切换不同的系列时。
    缺点:
  • 增加新的产品种类困难,它需要修改抽象工厂的接口。
  • 代码结构比较复杂。
代码示例:

(该例旨在体现父类ClassCenter类的调度作用,故没有采用子类重写父类的方法的形式去表现,导致父类ClassCenter和子类AClass、BClass等的联系看起来不够密切)
请添加图片描述
以上就是我们所用到的所有的类和文件。
首先来看一下AClass.h文件的代码:

#import "ClassCenter.h"

NS_ASSUME_NONNULL_BEGIN

@interface AClass : ClassCenter
//生产A产品
- (void) createAproduct;

@end

NS_ASSUME_NONNULL_END

可以看到里面定义自己的方法。

AClass.m文件的代码:

#import "AClass.h"

@implementation AClass

- (void) createAproduct {
    NSLog(@"生产了A产品!");
}

@end

可以看到里面实现了自己定义的方法。
然后AClass类是ClassCenter类的子类。

然后我们来看BClass.h文件的代码:

#import "ClassCenter.h"

NS_ASSUME_NONNULL_BEGIN

@interface BClass : ClassCenter
//生产B产品
- (void) createBproduct;

@end

NS_ASSUME_NONNULL_END

BClass.m文件的代码:

#import "BClass.h"

@implementation BClass

//生产B产品
- (void) createBproduct {
    NSLog(@"生产了B产品!");
}

@end

我们看到其大致与AClass类类似,就是其中各自有各自要操作的方法而已
至此,CClass类的实现代码与AClass类和BClass类类似,此处就不多赘述。

然后我们看一下核心的ClassCenter类的代码,首先是ClassCenter.h文件的代码:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ClassCenter : NSObject

//创造用户需求的产品
- (void) createProductWith: (NSString *) stringFirst : (NSString *) stringSecond;

@end

NS_ASSUME_NONNULL_END

我们看到了它定义了一个可以根据需求创建相应产品的方法

接着我们看ClassCenter.m文件的代码:

#import "ClassCenter.h"
#import "AClass.h"
#import "BClass.h"
#import "CClass.h"

@implementation ClassCenter

//创造用户需求的产品(包含所有的情况)
- (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];
    }
    
}

@end

可以看到我们根据所传进的参数进行了对应的操作(此处的参数待会儿就会讲到),比如创建对应的实例并用相应实例调用相应的方法进行操作。

最后我们看一下客户端的代码实现:
首先看ViewController.h文件中的代码:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end

仍然是没有定义什么
接着看ViewController.m文件中的代码:

#import "ViewController.h"
#import "ClassCenter.h"

@interface ViewController ()
@property (nonatomic, strong) UITextField *testTextField;
@property (nonatomic, strong) UITextField *testTextFieldSecond;
@property (nonatomic, strong) UIButton *testButton;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _testTextField = [[UITextField alloc]initWithFrame:CGRectMake(100, 100, 200, 40)];
    _testTextField.backgroundColor = [UIColor yellowColor];
    [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];
}

@end

我们可以看到,我们是在客户端定义了两个输入框和一个按钮。我们实现的功能是组合形式的,比如生产塑料瓶和酸奶,或者玻璃瓶和可乐,这样类似的组合形式,所以需要从两个输入框输入用户需求,然后点击按钮利用抽象工厂模式进行相应实例的创建的方法的调用操作,最终实现用户的需求,且客户端的接口非常简洁,后续维护扩展都无需修改客户端的代码,只需要添加ClassCenter类的相关子类和向ClassCenter类中添加对其子类的操作即可。

此例子中就是以ClassCenter类为抽象的中心,其余那三个子类AClass、BClass、CClass相当于三个工厂模式,ClassCenter负责调度这三个子类工厂,并将实现放在了子类中(本例的这三个子类其实也可以直接写在ClassCenter类的文件中更方便与管理,本例实现仅供参考)

运行效果:
输入A和C:
请添加图片描述
打印:请添加图片描述
输入C和B:
请添加图片描述
打印:
请添加图片描述
提示: 在本例中,后续若需要添加其他需要生产的产品类型的时候,就再创建一个ClassCenter的子类,然后通过重写父类的方法,添加自己的方法等操作(由于本例没有选择重写父类的方法,所以子类父类的联系看似不密切),然后将与该新建类的操作相关的代码添加到ClassCenter类中即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值