简述设计模式
工厂模式: 在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让 一个类的实例化延迟到子类中进行。
使用条件: 为了将实例化具体类的代码从应用中抽离, 或封装起来, 使它不会干扰医用的其他部分。
例子: Head_First中是披萨��为例子给出代码, 书中是以JAVA为语言基础给出的例子, 这里作者主要做的是iOS开发, 所以翻译成OC进行举例。
相关知识点和思路
涉及到的知识点
主要包括: 基类, 封装的使用(对应部分会在代码中详细标注)。
工厂模式可以划分为: 简单工厂和抽象工厂两类。
简单工厂
思路分析
在设计模式的学习和使用里, 我们追求让我们的代码更具有弹性, 然而我们使用固有类的实例方法去alloc对象, 这导致代码更脆弱, 和缺乏弹性。
/* 创建一个红鸭子对象 */
Duck *duck = [[RedDuck alloc] init];
我们要使代码更具有弹性, 可以更好额进行控制可扩展, 但是我们还是给他具体类的实例, 这与我们的设计是相悖的。
当需求变成我们需要一群相关的具体类时, 我们的代码通常会变成这样:
/* 这里会有一大堆的类, 但是要等到运行时才知道实例哪一种 */
- (Duck *)initwith:(NSString *)str {
Duck *duck;
if ([str isEqualToString:@"red"]) {
duck = [[RedDuck alloc] init];
} else if ([str isEqualToString:@"green"]) {
duck = [[GreenDuck alloc] init];
} else {
duck = [[DefultDuck alloc] init];
}
return duck;
}
其实就上面这一段代码我们已经算是初步解决了这个问题, 但是这样写又有了新的问题。
一旦我们的类中有需要修改或扩展的地方, 我们必须要从新找到这个类进行检查和修改、 通常这样的修改代码, 很可能造成更多的麻烦。
当遇到这样的问题我们就需要从OO(面向对象)的设计原则去寻找线索了, 我们的首要原则就是处理改变, 并帮我们找出会变化的方面, 把它从不变中剥离出来。
实现步骤
识别出变化的部分
这里我就直接拿书中的Pizza例子来翻译了
假设你有一个披萨店, 你会这样写
/* Pizza订单方法 */
- (Pizza *)order {
Pizza *pizza = [[Pizza alloc] init];
[pizza bake]; /* 烘烤 */
[pizza cut]; /* 切块 */
[pizza box]; /* 打包 */
return pizza;
}
但是你需要更多类型的Pizza
下面的(CheesePizza, GreekPizza)都是Pizza的子类, 代码中使用OC多态性
- (Pizza *)initWith:(NSString *)type {
Pizza *pizza;
if ([type isEqualToString:@"cheese"]) {
pizza = [[CheesePizza alloc] init];
} else if ([type isEqualToString:@"greek"]) {
pizza = [[GreekPizza alloc] init];
} else {
pizza = [[DefultPizza alloc] init];
} /* 这里需要所有的Pizza子类类型 */
[pizza bake]; /* 烘烤 */
[pizza cut]; /* 切块 */
[pizza box]; /* 打包 */
return pizza;
}
新的需求
但是随着需求的变更, 我们需要更多类型的Pizza, Veggie(素食Pizza)。 并且Greek Pizza卖的不好, 所以需要在菜单中去掉他。
- (Pizza *)initWith:(NSString *)type {
Pizza *pizza;
if ([type isEqualToString:@"cheese"]) {
pizza = [[CheesePizza alloc] init];
}
/* GreekPizza 要去掉 */
else if ([type isEqualToString:@"greek"]) {
pizza = [[GreekPizza alloc] init];}
/* 新添加蔬菜Pizza */
else if ([type isEqualToString:@"Veggie"]) {
pizza = [[VeggiePizza alloc] init];
} else {
pizza = [[DefultPizza alloc] init];
} /* 这里需要所有的Pizza子类类型 */
[pizza bake]; /* 烘烤 */
[pizza cut]; /* 切块 */
[pizza box]; /* 打包 */
return pizza;
}
从代码中我们找到Pizza的变与不变
变 :Pizza的类型
不变:烘烤, 切块和打包的流程
既然我们找到了变与不变, 该是我们使用封装的时候了, 我们需要将变化的部分剥离出来进行封装。
而封装的新对象, 我们可以称之为工厂。
创建一个简单的Pizza生产工厂
工厂类PizzaFactory
+ (Pizza *)creatPizzaWith:(NSString *)type {
Pizza *pizza;
/* 这部分是上面订单中的Pizza分类方法 */
if ([type isEqualToString:@"cheese"]) {
pizza = [[CheesePizza alloc] init];
} else if ([type isEqualToString:@"Veggie"]) {
pizza = [[VeggiePizza alloc] init];
} else {
pizza = [[DefultPizza alloc] init];
}
return pizza;
}
虽然只是简单的剥离和封装但但实际上解决了许多问题
我们的简单Pizza工厂可以被很多对象使用, 不仅仅只是上面的披萨店, 必败客, 还有宅急送等很多地方。Factory可以有许多的客户
我们这样封装成一个类, 当需要变更时,我们只改变这个类即可。
并且我们把具体的实例化过程从客户的代码中移除了。
当我再次修改客户的代码只需要在代码中调用我们的工厂去生产对应类型的Pizza
+ (Pizza *)newWith:(NSString *)type {
Pizza *pizza;
pizza = [PizzaFactory creatPizzaWith:type]; /* 工厂调用 */
[pizza bake]; /* 烘烤 */
[pizza cut]; /* 切块 */
[pizza box]; /* 打包 */
return pizza;
}
当我们在需要蔬菜Pizza时:
Pizza *pizza = [Pizza newWith:@"Veggie"]; /* 我们得到的蔬菜披萨 */
工厂模式: 所有工厂模式都用来封装对象的创建。通过让子类决定该创建的对象是什么, 来打到将对象创建的过程封装的目的。
完整代码
工厂类
#import <Foundation/Foundation.h>
#import "Pizza.h"
@interface PizzaFactory : NSObject
+ (Pizza *)creatPizzaWith:(NSString *)type;
@end
#import "PizzaFactory.h"
#import "MeatPizza.h"
#import "VegetablesPizza.h"
@implementation PizzaFactory
+ (Pizza *)creatPizzaWith:(NSString *)type {
Pizza *pizza;
if ([type isEqualToString:@"greek"]) {
pizza = [[MeatPizza alloc] init];
} else {
pizza = [[VegetablesPizza alloc] init];
}
return pizza;
}
@end
Pizza类
#import <UIKit/UIKit.h>
@interface Pizza : UIView
+ (Pizza *)newWith:(NSString *)type;
@end
+ (Pizza *)newWith:(NSString *)type {
Pizza *pizza;
pizza = [PizzaFactory creatPizzaWith:type]; /* 工厂调用 */
[pizza bake]; /* 烘烤 */
[pizza cut]; /* 切块 */
[pizza box]; /* 打包 */
return pizza;
}
- (void)bake {
NSLog(@"babk");
}
- (void)cut {
NSLog(@"cut");
}
- (void)box {
NSLog(@"box");
}