iOS 简单工厂模式


什么是简单工厂模式?

简单工厂模式中定义一个抽象类,抽象类中声明公共的特征及属性,抽象子类继承自抽象类,去实现具体的操作。工厂类根据外界需求,在工厂类中创建对应的抽象子类实例并传给外界,而对象的创建是由外界决定的。外界只需要知道抽象子类对应的参数即可,而不需要知道抽象子类的创建过程,在外界使用时甚至不用引入抽象子类。

简单工厂模式将操作对象的创建,和关于操作对象相关的业务逻辑分离开,降低操作对象的耦合度。由于工厂类只是为外界创建对象,所以并不需要实例化工厂类对象,只需要为外界提供类方法即可。外界需要什么类型的抽象子类,只需要传递对应的参数即可。

简单工厂模式主要包含三部分:

  • 工厂类:根据外界的需求,决定创建并返回哪个具体的抽象子类。
  • 抽象类:定义抽象子类所需的属性和方法,子类通过继承自抽象类获得这些方法。
  • 抽象子类:继承自抽象类,是具体操作的实现者。

业务场景

简单工厂模式主要适用于抽象子类的业务逻辑相同,但具体实现不同的情况。不同的操作子类执行同样的方法,最后的结果却是不同的,这也是多态的一种表现方式。

这里用一个简单的加减乘除的基础运算例子来说明一下,下面的UML类图和代码都会依据这个场景来实现。假设现在需要实现一个简单的加减乘除运算,这些运算具体操作都是类似的,都有两个被操作的值,只是运算符不同,这种情况就适合用简单工厂模式。

UML类图

根据上面提出的业务场景来画一张类图。

简单工厂模式

从上面图中我们可以看出,图中定义了一个运算抽象类,所有的运算操作类继承自这个运算抽象类。运算抽象类有两个参与运算的属性,通过调用getResult方法来获取这两个值最后运算的结果,调用方式都一样,只是最后的结果不同。抽象类并不参与运算,运算的结果通过运算操作类重载getResult方法去实现。

上图中还定义了一个简单工厂类,这个简单工厂类就是用于实现运算操作类实例化的逻辑,通过外界传进来的type参数,并将实例完成的运算操作类返回。

普通方式代码实现

首先定义抽象类,抽象类中将会包含参与运算的抽象子类的属性和行为(方法)。

//.h
@interface Operation : NSObject
@property (nonatomic, assign) CGFloat numberOne;
@property (nonatomic, assign) CGFloat numberTwo;
- (CGFloat)getResult;
@end
//.m
@implementation Operation
- (CGFloat)getResult {
return 0;
}
@end

定义抽象类之后,需要创建负责具体运算的抽象子类,也就是操作类,简单的定义了一下,代码不太多就全贴出来了。

//.h
@interface OperationAdd : Operation
@end //.m 
@implementation OperationAdd
- (CGFloat)getResult {
return self.numberOne + self.numberTwo;
}
@end
//.h
@interface OperationSub : Operation
@end  
//.m
@implementation OperationSub
- (CGFloat)getResult {
return self.numberOne - self.numberTwo;
}
@end 
//.h
@interface OperationMul : Operation
@end
//.m 
@implementation OperationMul
- (CGFloat)getResult {
return self.numberOne * self.numberTwo;
}
@end
//.h
@interface OperationDiv : Operation
@end
//.m
@implementation OperationDiv
- (CGFloat)getResult {
if (self.numberTwo == 0) {
NSLog(@"除数不能为零");
return 0;
} else {
return self.numberOne / self.numberTwo;
}
}
@end

下面先定义了四个静态变量,这四个静态变量声明了创建对象的类型,在后面反射部分代码中也会用到这四个静态变量。

static NSString kOperationAdd = @"OperationAdd";
static NSString 
kOperationSub = @"OperationSub";
static NSString kOperationMul = @"OperationMul";
static NSString 
kOperationDiv = @"OperationDiv";

现在具体参与运算的类都已经定义完成,就需要定义工厂类了。工厂类的职责就是根据外界需要,创建对应的抽象子类实例并返回给外界。

//.h
@interface OperationFactory : NSObject
+ (Operation )CreateOperationWithType:(NSString )type;
@end
//.m
@implementation OperationFactory
+ (Operation )CreateOperationWithType:(NSString )type {
if ([kOperationAdd isEqualToString:type]) {
return [OperationAdd new];
} else if ([kOperationSub isEqualToString:type]) {
return [OperationSub new];
} else if ([kOperationMul isEqualToString:type]) {
return [OperationMul new];
} else if ([kOperationDiv isEqualToString:type]) {
return [OperationDiv new];
}
return nil;
}
@end

上面我们就将工厂设计模式的定义都完成了,现在需要的就是外界直接拿来使用了。上面工厂类直接定义的类方法,因为外界获取某个具体的抽象子类时,并没有必要将工厂类实例化,工厂类只是完成一个功能。

-(void)viewDidLoad {
Operation *oper = [OperationFactory CreateOperationWithType:kOperationAdd];
oper.numberOne = 13;
oper.numberTwo = 24;
NSLog(@"result : %f", [oper getResult]);
}

到目前为止简单工厂模式的代码就写完了,可以看到外界想进行什么类型的运算,只需要将传入的运算类型参数改一下即可,工厂类就会实例化其他的抽象子类进行运算。但是这种工厂类的设计,有一个很大的问题,就在于每次增加或删除某个算法时,都需要对工厂类进行修改,这是不符合开放封闭原则的。对于这个问题,我们将会通过反射机制来进行处理。

工厂模式也是对面向对象编程三大特性之一的多态的一个很好的表述,下面将会简单的介绍一下多态的特性。

简单介绍一下多态

面向对象编程三大特性之一就有多态,多态是指在程序运行时,相同的消息可能会发给继承自同一个父类的不同类型的对象,虽然是同一个方法,但是运行时系统会根据当前对象所属的具体类型作出不同的响应。

面向对象三大特性中,继承和封装都是为了代码重用,继承可以继承自父类的特征和属性,封装可以将实现细节封装,外界调用实现某些功能。而多态则是为了接口重用。

多个子类继承同一个父类,就会具有和父类相同的行为和特征,子类可以对父类的方法进行重写,所以可能同一个方法每个子类的实现都不同。通过父类指针指向任意子类对象并调用相同方法,可能会得到不同的结果。

简单的说就是系统允许将当前类的指针,指向任何继承自当前类的子类,并且不会报错。由于子类继承自父类,所以和父类有相同的特征(方法)。当前类的指针向指向的子类对象发送消息,系统会根据具体的子类对父类方法的实现,作出不同的响应。 例如下面这行代码:

Operation *obj = [OperationFactory CreateOperationWithType:kOperationAdd];

在OC语言中大多数类都是继承自NSObject类的(也有继承自NSProxy等类),所以都具有NSObject的方法,如果子类对NSObject的方法进行了重写,NSObject指向子类对象并发送消息的结果就不一样了。

在上面这个例子中,OperationFactory工厂类将会返回Operation的子类实例,Operation的子类分别继承自同一父类,并且对其getResult方法进行了重写。Operation实例化的obj指针可能指向任何Operation的子类,并对其发送getResult消息。最终的结果会根据obj指针指向的子类有不同的结果,这就是多态。

我对多态的了解非常浅薄,有不对之处还请多多指出,这里只是顺带提了一下。

配合反射机制优化代码

在上面的代码中,我们会发现工厂类创建抽象子类的代码都是相同的,只是创建的具体对象不同,而且如果抽象子类很多的话,会有过多的条件语句。编程中这种重复代码我们都要将其简化,不然写出的代码就像垃圾代码一样,这也是新手和老手的区别之一。

在这里我们可以利用反射机制来简化代码,根据外面需要的操作子类的类型,反射出具体的类。在上面我们已经定义了一些NSString类型的静态变量,这些静态变量的值就是反射需要的字符串,外界只需要使用这些静态变量即可,不用自己手打字符串,也防止了错误的发生。修改之后外界不需要发生任何变化,只需要知道这些静态变量即可,只需要对工厂类进行修改。

只需将OperationFactory的创建方法改一下实现,其他地方不受影响。

  • (Operation )CreateOperationWithType:(NSString )type { return [NSClassFromString(type) new]; }

改完之后的代码非常符合面向对象编程的开放封闭原则,即当外界需求发生变化时,只对现有代码进行扩展,不对其原有代码进行修改的原则。

现在假设再增加一个其他运算功能,只需要再创建一个继承自抽象类的抽象子类,在抽象子类中重写getResult方法来实现运算,并且在上面定义的静态变量中加入一个对应的变量。其他地方都不会受到影响,这就是一个比较好的面向对象的设计。

iOS 简单工厂模式的更多相关文章

  1. iOS开发-简单工厂模式

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.概念很长,iOS开发中最常 ...

  2. iOS设计模式 - (3)简单工厂模式

    iOS设计模式 - (3)简单工厂模式           by Colin丶 转载请注明出处:              http://blog.csdn.net/hitwhylz/article/ ...

  3. IOS设计模式浅析之简单工厂模式(SimpleFactory)

    概述 首先说明一下,简单工厂模式不属于23种GOF设计模式之一.它也称作静态工厂方法模式,是工厂方法模式的特殊实现.这里对简单工厂模式进行介绍,是为本系列后面的工厂方法和抽象工厂模式做一个引子. 定义 ...

  4. iOS设计模式之简单工厂模式

    简单工厂模式 基本理解 到底要实例化谁,将来会不会增加实例化的对象,比如计算器增加开根运算,这是很容易变化的地方,应该考虑用一个单独的类来做这个创造实例的过程,这就是工厂. 通过使用工厂模式,我们可以 ...

  5. iOS设计模式:简单工厂模式

    1.简述 首先需要说明一下,简单工厂模式不属于23种GOF设计模式之一.它也称作静态工作方法模式,是工厂方法模式的特殊实现(也就是说工厂模式包含简单工厂模式).这里对简单工厂模式进行介绍,是为后面的工 ...

  6. iOS常用设计模式——工厂方法(简单工厂模式,工厂方法模式, 抽象工厂模式)

    1. 简单工厂模式 如何理解简单工厂,工厂方法, 抽象工厂三种设计模式? 简单工厂方法包含:父类拥有共同基础接口,具体子类实现子类特殊功能,工厂类根据参数区分创建不同子类实例.该场景对应的UML图如下 ...

  7. iOS经常使用设计模式——工厂方法(简单工厂模式,工厂方法模式, 抽象工厂模式)

    1. 简单工厂模式 怎样理解简单工厂,工厂方法. 抽象工厂三种设计模式? 简单工厂的生活场景.卖早点的小摊贩.他给你提供包子,馒头,地沟油烙的煎饼等,小贩是一个工厂.它生产包子,馒头,地沟油烙的煎饼. ...

  8. iOS 抽象工厂模式

    iOS 抽象工厂模式 什么是抽象工厂模式 简单了解一下 按照惯例,我们先了解一下什么是抽象工厂模式.抽象工厂模式和工厂方法模式很相似,但是抽象工厂模式将抽象发挥的更加极致,是三种工厂模式中最抽象的一种 ...

  9. PHP设计模式(一)简单工厂模式 (Simple Factory For PHP)

    最近天气变化无常,身为程序猿的寡人!~终究难耐天气的挑战,病倒了,果然,程序猿还需多保养自己的身体,有句话这么说:一生只有两件事能报复你:不够努力的辜负和过度消耗身体的后患.话不多说,开始吧. 一.什 ...

随机推荐

  1. 如何克隆kvm虚拟机

    关于如何使用kvm虚拟化技术创建虚拟机,这里有一系列博客讲的已经非常清楚了,这里不再赘述,不过其中有些小坑可能需要大家注意: 0. 写在创建虚拟机之前(即教程的系列三之前) 1. 确认防火墙是否关闭, ...

  2. NVIDIA CuDNN 安装说明

    CuDNN是专门针对Deep Learning框架设计的一套GPU计算加速方案,目前支持的DL库包括Caffe,ConvNet, Torch7等. CuDNN可以在官网免费获得,注册帐号后即可下载.官 ...

  3. 解决两台centos虚拟机Telnet服务无法联机的问题

    关闭防火墙 [root@localhost ~]# service iptables stopiptables: Flushing firewall rules:                    ...

  4. [Node.js] BDD和Mocha框架

    原文地址:http://www.moye.me/2014/11/22/bdd_mocha/ 引子 今天造了个轮子:写了个深拷贝任意JavaScript对象的模块(事实上npm上已经有类似的模块,纯造轮 ...

  5. nodejs morgan包

    新建app.js var express = require('express') var logger = require('morgan') var app = express() app.use ...

  6. [python]爬代理ip v2.0(未完待续)

    爬代理ip 所有的代码都放到了我的github上面, HTTP代理常识 HTTP代理按匿名度可分为透明代理.匿名代理和高度匿名代理. 特别感谢:勤奋的小孩 在评论中指出我文章中的错误. REMOTE_ ...

  7. 字符编码(ASCII,Unicode和UTF-8) 和 大小端

    本文包括2部分内容:“ASCII,Unicode和UTF-8” 和 “Big Endian和Little Endian”. 第1部分 ASCII,Unicode和UTF-8 介绍 1. ASCII码 ...

  8. Laravel 5 事件的使用

    事件类通常被保存在 app/Events 目录下,而它们的处理程序则被保存在 app/Handlers/Events 目录下. 事件的创建 下面我们用artisan来创建一个事件,比如叫CqhTest ...

  9. 在SQL Server里如何进行页级别的恢复

    在今天的文章里我想谈下每个DBA应该知道的一个重要话题:在SQL Server里如何进行页级别还原操作.假设在SQL Server里你有一个损坏的页,你要从最近的数据库备份只还原有问题的页,而不是还原 ...

  10. tips null和undefined的区别

    tips null和undefined的区别 1.undefined类型 undefined类型只有一个值,即特殊的undefined.在使用var声明变量但未对其加以初始化时,这个变量的值就是und ...