设计模式--原型模式

前言:

由于之前写的时候匆忙,本篇博客有误,今天进行了修正。希望之前没几个人看,以免误人子弟。

另:希望iOS的各位一起来讨论iOS的技术。欢迎留言,不定时回复。

1.设计模式分类   

     所谓设计模式,是前人在开发过程中总结的经验。各自有各自的使用情况。分类条件不同 设计模式的分类也不尽相同。编程之道中大致分类如下

  • 创建型 包括 单例设计模式,简单工厂模式,工厂方法模式 抽象工厂模式 原型模式 生成器模式 
  • 接口适配型  包括 适配器 桥接 外观
  • 对象去耦型 包括 中介者模式 观察者模式
  • 抽象集合型 组合模式 迭代器模式 
  • 行为扩展型 访问者模式 装饰模式
  • 算法封装型 模版方法模式 策略模式 命令模式
  • 性能与对象访问型 享元模式 代理模式
  • 对象状态型  备忘录模式等。
  • 职责链模式 状态模式

2.原型设计模式

首先从简单的入手。看看原型模式吧。学习Javascript的时候有一个ProtoType 翻译过来就是原型,那么什么是原型呢?

举个生活中的例子,假设你要做生意 要发名片 那么 你就需要先设计一个名片然后打印N多份然后发送给客户。即copy。

在编程语言当中,经常有这样的情况,如我想操作某个对象,但是我又不想把他的内容改变了,这时候需要先保存这个对象。或者是我需要多个一样的对象。

2.1 代码设计

以Person类为例

#import 

@interface Person : NSObject
@property (nonatomic,copy) NSString * name;
@property (nonatomic,copy) NSString * sex;
@property (nonatomic,copy) NSString * experience;
//显示
-(void)display;

-(void)test1;
-(void)test2;
@end
@implementation Person
-(void)display
{
    NSLog(@"姓名:%@",_name);
    NSLog(@"性别:%@",_sex);
    NSLog(@"工作经验%@",_experience);
}
@end


 
  以上代码 在main函数中如果需要再创建一个,那么需要多次alloc init 
  

        
	Person * p = [[Person alloc]init];
        p.name = @"name1";
        p.sex = @"男";
        p.experience = @"一年";
        [p display];
       Person * p1 = [[Person alloc]init];
        p1.name = @"name1";
        p1.sex = @"男";
        p1.experience = @"一年";
        [p1 display];
 
  

 
  创建的过程是一个耗费cpu的过程 想象一下 足够大的数据量下都进行创建 系统性能会如何呢?而且如果对变量设置 基本上都是一样代码 造成代码冗余。 
  

于是原型设计模式就很有必要了。

2.2 对代码进行重构

@interface Person : NSObject
@property (nonatomic,copy) NSString * name;
@property (nonatomic,copy) NSString * sex;
@property (nonatomic,copy) NSString * experience;

//提供原型类的克隆接口
-(instancetype)clone;
//显示
-(void)display;

@end
@implementation Person
-(void)display
{
    NSLog(@"姓名:%@",_name);
    NSLog(@"性别:%@",_sex);
    NSLog(@"工作经验%@",_experience);
}
-(instancetype)clone
{
    return [self copy];               (1)标记
}
 
  

-(id)copyWithZone:(NSZone *)zone
{
    Prototype * p = [[[self class] alloc]init];
    p.name = self.name;
    return p;
}

@end
 
  
为什么要写下面这个方法呢?
答案: 以上代码中红色标记位置,是调用该类的copy方法,这个方法是从NSObject继承而来,而这个方法内部会调用copyWithZone方法,这个方法是NSCopying协议里面的,对于任何继承自NSObject的类而言,如果需要使用copy mutablecopy 则需要重载copyWithZone方法或者mutableCopyWithZone方法。
系统类之所以需要不需要重写这两个方法,是因为内部已经重载了。
 
   
  

2.3 什么是原型设计模式

原型设计模式:用原型实例指定创建对象的种类 并通过拷贝这个对象来得到新的对象

其类图如下:

设计模式--原型模式_第1张图片

从上面可以看到 当前一个原型的类中提供一个用于复制的接口 使用这个类创建出实例后,每个实例都有一个clone的接口 于是用这个实例就可以拷贝出新的对象。

2.4 iOS中的原型设计模式

iOS中已经实现了原型设计模式,提供了用于复制的接口。由于iOS不支持接口,将拷贝的接口做成了协议。NSCopying。

看系统内部NSCopying 的声明:

@protocol NSCopying

- (id)copyWithZone:(NSZone *)zone;

@end
如果需要用到拷贝 必须遵守这个协议 在类内实现copyWithZone方法。

但是 有没有发现,fundation中的好多对象都已经遵守了 而且还多了一个NSMutableCopying。这个暂时不讨论,只讨论NSCopying。

2.5 iOS中原型设计模式的使用情形:

需要创建的对象应独立于其类型和创建方式 

要实例化的类是在运行时决定的

不想要与产品层次相对应的工厂层次 (不太懂)

不同类的实例间的差异仅仅是状态的若干组合 因此复制相应数量的原型比手工实例化更方便。

该模式的最低限度是生成对象的真实副本 以用作同一环境下其他相关事物的基础

那么现在应该清楚iOS中的NSCopying 和NSMutableCopying是运用的原型设计模式。那么我们来讨论下。

首先,涉及到深拷贝和浅拷贝的内容。

浅拷贝:如果当前拷贝只是复制了对象的指针,而没有内容的复制 被称为浅拷贝。如下图。

设计模式--原型模式_第2张图片

深拷贝:如果当前拷贝不仅仅复制了对象的指针,而且在内存开辟了内存空间,以存储拷贝的内容,被称之为深拷贝。如下图。

设计模式--原型模式_第3张图片

2.6 cocoa Touch框架中的对象复制

coucoa touch框架为NSObject派生类提供了两个协议。NSCopying NSMutableCopying。

以iOS中字符串的复制为例 讨论深拷贝和浅拷贝。

-(void)test1

{
    NSString * str = @"abc";
    NSLog(@"字符串地址%p",str);
    NSString * str1 = [str copy];
    NSLog(@"不可变字符串copy后地址%p",str1);
    NSString * str2 = [str mutableCopy];
    NSLog(@"不可变字符串mutableCopy后地址%p",str2);
    NSMutableString * strM = [[NSMutableString alloc]initWithString:@"def"];
    NSMutableString * strM1 = [strM copy];
    NSLog(@"可变字符串copy后地址%p",strM1);
    NSString * strM2 = [strM mutableCopy];
    NSLog(@"可变字符串的mutablecopy地址%p",strM2);
}
console的信息:
2015-11-13 18:13:06.162 0-5 原型模式[2344:187326] 字符串地址0x1000020c0
2015-11-13 18:13:06.164 0-5 原型模式[2344:187326] 不可变字符串copy后地址0x1000020c0       
2015-11-13 18:13:06.165 0-5 原型模式[2344:187326] 不可变字符串mutableCopy后地址0x1004046e0
2015-11-13 18:13:06.165 0-5 原型模式[2344:187326] 可变字符串copy后地址0x66656435
2015-11-13 18:13:06.165 0-5 原型模式[2344:187326] 可变字符串的mutablecopy地址0x1004048d0

 
  由以上结果可以看出,NSString的copy得到的字符串地址相同,mutable copy后得到字符串地址不同,NSMutableString copy得到字符串地址不同,mutable copy得到字符串地址不同。 
  

结论:NSString的copy是浅拷贝,而NSMutableString的copy mutable copy NSString的mutableCopy都是深拷贝

是否是深拷贝的唯一判定标准是:是否通过拷贝得到了新的对象,而非仅仅拷贝了地址。

下面讨论拷贝得到的字符串的可变性。

-(void)test2
{
    //讨论字符串复制后的可变性
    NSString * str = @"abc";
    NSMutableString * str1 = [str copy];
    NSMutableString * str2 = [str mutableCopy];
    
    NSMutableString * strM = [[NSMutableString alloc]initWithString:@"abc"];
    NSMutableString * strM1 = [strM copy];
    NSMutableString * strM2 = [strM mutableCopy];
    [str1 appendString:@"a"];//1
    [str2 appendString:@"a"];//2
    [strM1 appendString:@"a"];//3
    [strM2 appendString:@"a"];//4
}

以上得到NSString NSMu table St ring的copy和mutableCopy字符串之后 分别用NSMutableString接收,然后操作该对象。

做标注的1,3处 会报错误如下

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0x63626135'
尝试将不可识别的消息发送给实例。

也就是说 1,3得到的字符串是不可变的。

结论:无论是NSString 还是NSMutable String 通过copy得到的字符串 都是不可变的。通过mutableCopy得到的字符串 都是可变的。

那么在oc中是仅仅是字符串是这样子的吗?

下面实例给出数组的测试用例

-(void)test3
{
    NSArray * arr = @[@"2",@"1"];
    
    NSMutableArray * arrM = [NSMutableArray arrayWithArray:arr];
    NSMutableArray * arr1 = [arr copy];
    NSMutableArray * arr2 = [arr mutableCopy];
    NSLog(@"NSArray地址%p",arr);
    NSLog(@"NSArray copy地址%p",arr1);
    NSLog(@"NSArray mutableCopy地址%p",arr2);
    
    NSMutableArray * arrM1 = [arrM copy];
    NSMutableArray * arrM2 = [arrM mutableCopy];
    
    NSLog(@"NSMutableArray copy地址%p",arrM1);
    NSLog(@"NSMutableArray mutableCopy地址%p",arrM1);
}
console:
2015-11-13 18:29:02.416 0-5 原型模式[2427:192975] NSArray地址0x10030aef0
 
  
2015-11-13 18:29:02.416 0-5 原型模式[2427:192975] NSArray copy地址0x10030aef0
2015-11-13 18:29:02.417 0-5 原型模式[2427:192975] NSArray mutableCopy地址0x10030c0e0
2015-11-13 18:29:02.417 0-5 原型模式[2427:192975] NSMutableArray copy地址0x10020cc80
2015-11-13 18:29:02.417 0-5 原型模式[2427:192975] NSMutableArray mutableCopy地址0x10020d1d0
Program ended with exit code: 0

 
  由以上结果可知iOS中不可变数组的copy为浅拷贝,不可变数组的mutablecopy和可变数组的copy和mutableCopy都是深拷贝。 
  

可变性讨论,给出数组复制后的可变性测试用例

-(void)test4
{
    NSArray * arr = @[@"2",@"1"];
    NSMutableArray * arrM = [NSMutableArray arrayWithArray:arr];
    NSMutableArray * arr1 = [arr copy];
    NSMutableArray * arr2 = [arr mutableCopy];
    NSMutableArray * arrM1 = [arrM copy];
    NSMutableArray * arrM2 = [arrM mutableCopy];
    
    [arr1 addObject:@"abc"];//1
    [arr2 addObject:@"abc"];//2
    [arrM1 addObject:@"abc"];//3
    [arrM2 addObject:@"abc"];//4
}
在以上标注的1,3的情况下 console信息如下:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x100205570'
3.结论:

1.NSMutableArray类对象的拷贝都是深拷贝,NSArray 的copy为浅拷贝,mutable copy是深拷贝,得到的新对象的可变性要看是copy还是mutable copy。

2.copy的情况下得到的对象不可变,mutablecopy的情况下得到的对象可变。

继续测试NSDictionary。

测试用例1:

-(void)testDictionary
{
    NSDictionary * d = [NSDictionary dictionary];
    NSDictionary * d2 = [d copy];
    NSMutableDictionary * d3 = [d mutableCopy];
    NSMutableDictionary * dictM = [NSMutableDictionary dictionary];
    NSMutableDictionary * dictM2 = [dictM copy];
    NSMutableDictionary * dictM3 = [dictM mutableCopy];
     NSLog(@"地址%p",d);
    NSLog(@"copy地址%p",d2);
    NSLog(@"mutablecopy地址%p",d3);
    NSLog(@"地址%p",dictM);
    NSLog(@"copy地址%p",dictM2);
    NSLog(@"mutablecopy地址%p",dictM3);
}
执行结果:
016-01-06 20:18:45.680 1fundation框架[1633:148875] 地址0x1002005b0
2016-01-06 20:18:45.680 1fundation框架[1633:148875] copy地址0x1002005b0
2016-01-06 20:18:45.680 1fundation框架[1633:148875] mutablecopy地址0x100103680
2016-01-06 20:18:45.681 1fundation框架[1633:148875] 地址0x100103a40
2016-01-06 20:18:45.681 1fundation框架[1633:148875] copy地址0x1002005b0
2016-01-06 20:18:45.681 1fundation框架[1633:148875] mutablecopy地址0x100103c60
Program ended with exit code: 0
可变性测试:

在以上代码的基础上添加:

    [d2 setObject:@"a" forKey:@"aa"];(1)
    [d3 setObject:@"a" forKey:@"aa"];
    [dictM2 setObject:@"a" forKey:@"aa"];(2)
    [dictM3 setObject:@"a" forKey:@"aa"];
1,2处会报错误,也就是说 NSDictionary NSMutableDictionary的copy不可变 mutable copy可变。


结论:

(1) fundation框架中 对象的copy不可变,mutable copy是可变的。

(2)不可变对象的copy是浅拷贝,mutable copy是深拷贝,可变对象的copy和mutable copy是深拷贝。

你可能感兴趣的:(算法&设计模式)