原型模式的定义
原型模式(Prototype)即应用于“复制”操作的模式,此模式最初定义在《设计模式》(Addison-Wesley,1994),里面是这样定义的“使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象”。简单来理解就是根据这个原型创建新的对象,这种创建是指深复制,得到一份新的内存资源,而不是一个新的指针引用。使用原型实例指定创建对象的种类,并通过复制这个原型创建新的对象.
当一个类的组成比较复杂,例如包含多个组件或多个自定义类属性的时候,直接复制当前对象比从头开始创建对象要简单得多,则使用原型模式最为合适。又或者对象间的区别不大,只是几个属性不同的时候,也可以使用原型模式,前提是要继承同一个父类。
适用环境
需要创建的对象应独立于其类型与创建方式。
要实例化的类是在运行时决定的。
不想要与产品层次相对应的工厂层次。
不同类的实例间的差异仅是状态的若干组合。(复制相应数量的原型比手工实例化更加方便)
类不容易创建。(复制已有的组合对象并对副本进行修改会更容易)
主要涉及的知识:复制©
关于深复制与浅复制(深拷贝&浅拷贝),可以参考我博客里的一篇文章iOS中的深拷贝和浅拷贝的学习记录 这里就不过多介绍了.
我们来举一个例子,首先我们先创建一些假数据,让这些假数据看起来,很多,显得创建一个这样的item会比较难,通过这些假数据,加载到我们要操作的ViewControllview上面
创建假数据Person类
点h文件
// PersonItem.h
// 原型模式CSDN
//
// Created by 王颜龙 on 13-12-30.
// Copyright (c) 2013年 longyan. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface PersonItem : NSObject<NSCopying>
@property (nonatomic, strong)UIImage *headerImg;//头像
@property (nonatomic, assign)int age;//年纪
@property (nonatomic, assign)CGFloat height;//身高
@property (nonatomic, assign)CGFloat weight;//体重
@property (nonatomic, assign)int numID;//学号
@property (nonatomic, assign)int classNum;//学年
@property (nonatomic, assign)int score1;//分数1
@property (nonatomic, assign)int score2;//分数2
@property (nonatomic, assign)int score3;//分数3
@property (nonatomic, assign)int score4;//分数4
@property (nonatomic, assign)int score0;//分数0
@property (nonatomic, assign)BOOL isChoose;//判断是否被选中
@end
加载假数据
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
NSMutableArray *arr = [[NSMutableArray alloc]initWithCapacity:0];
//制作假数据
for (int i = 0; i < 10; i++) {
PersonItem *item = [[PersonItem alloc]init];
item.headerImg = [UIImage imageNamed:[NSString stringWithFormat:@"%d",i+1]];
item.age = 15+i;
item.height = 165+i;
item.weight = 120+i;
item.numID = 100+i;
item.classNum = 10+i;
item.score0 = 100+i;
item.score1 = 90+i;
item.score2 = 80+i;
item.score3 = 70+i;
item.score4 = 60+i;
item.isChoose = NO;
[arr addObject:item];
}
EditViewController *vc = [[EditViewController alloc]initWithItem:arr];
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:vc];
self.window.rootViewController = nav;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
接下来,我们往项目里拖拽一个GMGridView,和一些图片数据,方便我们的展示
进入展示页面,是这样的
我们现在假设,如果我们想创建一个新的数据,但是这个数据又和之前的类似,但又有一些要修改的地方,例如我们修改头像,那么我们应该要怎么做呢? 我们可以像一开始那样重新创建一个preson类的item
PersonItem *item = [[PersonItem alloc]init];
item.headerImg = [UIImage imageNamed:[NSString stringWithFormat:@"%d",i+1]];
item.age = 15+i;
item.height = 165+i;
item.weight = 120+i;
item.numID = 100+i;
item.classNum = 10+i;
item.score0 = 100+i;
item.score1 = 90+i;
item.score2 = 80+i;
item.score3 = 70+i;
item.score4 = 60+i;
item.isChoose = NO;
但是如果要是创建100个,1000个的话,实际上这样做是不太合理的,这个时候就要用到我们的原型模式了.
再次说一下原型模式的定义:当一个类的组成比较复杂,例如包含多个组件或多个自定义类属性的时候,直接复制当前对象比从头开始创建对象要简单得多,则使用原型模式最为合适。
在这里GMGridView的初始化以及一些修改 我就不写了,很简单,重点写一下3个按钮得方法
#pragma bottomView method
//错误复制的方法
- (void)errorItem{
if (self.chooseArr.count == 0) {
return;
}
PersonItem *item = [self.chooseArr objectAtIndex:0];
item.headerImg = [UIImage imageNamed:@"15"];
[self.selectedItems addObject:item];
[self.gridView reloadData];
}
//正确复制的方法
- (void)copyItem{
if (self.chooseArr.count == 0) {
return;
}
PersonItem *itemNew = [[self.chooseArr objectAtIndex:0] copy];
itemNew.headerImg = [UIImage imageNamed:@"15"];
[self.selectedItems addObject:itemNew];
[self.gridView reloadData];
}
//删除按钮
- (void)delItem{
if (self.chooseArr.count == 0) {
return;
}
if (self.selectedItems.count <=1) {
return;
}
[self.selectedItems removeObject:[self.chooseArr objectAtIndex:0]];
[self.chooseArr removeAllObjects];
[self.gridView reloadData];
}
如果你点击了错误复制的方法,你会发现
你会发现什么,你复制了新的item,但是旧的item的头像也跟着改变了,这是为什么呢?我们看一下打印台
他们两个的内存地址是一样的,也就是说指针指向的是同一块内存地址,所以你修改了A,B自然也会变,也就是所谓浅拷贝.
这时如果你删除你会发现,他们都消失了
想避免这种方法的话,我们就要对person类实现深拷贝,那么就要遵循NSCopying协议,代码如下
#import "PersonItem.h"
@implementation PersonItem
//遵守NSCopying协议,深复制出一个新的item
- (id)copyWithZone:(NSZone *)zone{
PersonItem *item = [[[self class]allocWithZone:zone]init];
item.headerImg = [_headerImg copy];
item.age = _age;
item.height = _height;
item.weight = _weight;
item.numID = _numID;
item.classNum = _classNum;
item.score0 = _score0;
item.score1 = _score1;
item.score2 = _score2;
item.score3 = _score3;
item.score4 = _score4;
return item;
}
@end
这样如果你选择正确的复制的话,结果就会是这样的
原因自然是他们分别是2个不同的内存地址,所以修改互不干扰
这就是我理解的原型模式,如有错误,希望大家指正.