终于要写完这个系列了,GOF的设计模式总共有23种,我在前面的篇章只写了其中16个,剩下的7个放到这篇文章一起写了。因为这6个设计模式要么是iOS自身语言特性已经实现了,要么是没有什么太大的利用价值,所以放在一起简单讲解下。
今天要学习如下7种设计模式:
下面来一一讲解
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
看定义就知道原型模式就是iOS里面的对象克隆,这个iOS已经帮我们做好了,只要我们实现NSCopying或者NSMutableCopying协议就行了,具体实现看这篇文章:
深浅拷贝
提供一种方法顺序访问一个聚合对象中各个元素 , 而又不需暴露该对象的内部表示。
简单来说就是定义一个迭代器,可以使用同一种方式去遍历不同的的聚合对象,iOS里面的聚合对象就三种:NSArray、NSSet、NSDictonary。其他语言可能还有hash表,map表,链表等等iOS同样已经帮我们实现了迭代器,有三种方式去迭代集合对象
NSEnumertor本身是一个抽象类,依靠几个工厂方法来创建并返回具体的迭代器,比如下面的例子使用NSEnumertor来迭代NSArray
NSArray *array = @[@"啦啦啦", @"天空是无用且垂死的星辰", @"人在塔在",@"二营长,你他娘的意大利炮呢"]; NSEnumerator *enumertor = [array objectEnumerator]; id item ; while (item = [enumertor nextObject]) { NSLog(@"%@", item); }
当然还可以使用NSEnumertor来迭代NSSet和NSDictonary,就不在一一演示了,具体看这篇文章:
NSEnumertor迭代器的使用
[array enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"%@", obj); }];
这个方法的有点是可以根据条件停止迭代,还可以使用block实现回调
NSArray *array = @[@"啦啦啦", @"天空是无用且垂死的星辰", @"人在塔在",@"二营长,你他娘的意大利炮呢"]; [array enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL * _Nonnull stop) { if ([obj isEqualToString:@"人在塔在"]){ NSLog(@"%@", obj); *stop = YES; } }];
这是苹果推荐的方法,比for循环效率高,具体使用如下:
NSArray *array = @[@"啦啦啦", @"天空是无用且垂死的星辰", @"人在塔在",@"二营长,你他娘的意大利炮呢"]; for (NSString *str in array) { NSLog(@"%@", str); }
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 这样以后就可将该对象恢复到原先保存的状态。
简单来说就是在某个特定的时刻序列化对象,保存到内存或者硬盘上,在需要的时候再回复。保存的一般都是对象的属性值,比如我们我们打游戏的时候可以保存当前进度,然后可以读档,和这个是一样的道理。
如果是系统的类比如NSArray、NSDictonary,那么iOS系统已经帮我们实现好了,具体使用见这篇文章
objective-C中的序列化(serialize)与反序列化(deserialize)
如果是我们自定义的类需要序列化,那么就在需要序列化的类里面实现NSCoding协议的两个方法就可以了
- (id)initWithCoder:(NSCoder *)coder - (void)encodeWithCoder:(NSCoder *)coder
如果觉得一个个写类的每个属性非常麻烦,那么就使用如下的宏,一句代码就可以搞定类的序列化了
#define SERIALIZE_CODER_DECODER() / / - (id)initWithCoder:(NSCoder *)coder / { / NSLog(@"%s",__func__); / Class cls = [self class]; / while (cls != [NSObject class]) { / /*判断是自身类还是父类*/ / BOOL bIsSelfClass = (cls == [self class]); / unsigned int iVarCount = 0; / unsigned int propVarCount = 0; / unsigned int sharedVarCount = 0; / Ivar *ivarList = bIsSelfClass ? class_copyIvarList([cls class], &iVarCount) : NULL;/*变量列表,含属性以及私有变量*/ / objc_property_t *propList = bIsSelfClass ? NULL : class_copyPropertyList(cls, &propVarCount);/*属性列表*/ / sharedVarCount = bIsSelfClass ? iVarCount : propVarCount; / / for (int i = 0; i < sharedVarCount; i++) { / const char *varName = bIsSelfClass ? ivar_getName(*(ivarList + i)) : property_getName(*(propList + i)); / NSString *key = [NSString stringWithUTF8String:varName]; / id varValue = [coder decodeObjectForKey:key]; / NSArray *filters = @[@"superclass", @"description", @"debugDescription", @"hash"]; / if (varValue && [filters containsObject:key] == NO) { / [self setValue:varValue forKey:key]; / } / } / free(ivarList); / free(propList); / cls = class_getSuperclass(cls); / } / return self; / } / / - (void)encodeWithCoder:(NSCoder *)coder / { / NSLog(@"%s",__func__); / Class cls = [self class]; / while (cls != [NSObject class]) { / /*判断是自身类还是父类*/ / BOOL bIsSelfClass = (cls == [self class]); / unsigned int iVarCount = 0; / unsigned int propVarCount = 0; / unsigned int sharedVarCount = 0; / Ivar *ivarList = bIsSelfClass ? class_copyIvarList([cls class], &iVarCount) : NULL;/*变量列表,含属性以及私有变量*/ / objc_property_t *propList = bIsSelfClass ? NULL : class_copyPropertyList(cls, &propVarCount);/*属性列表*/ / sharedVarCount = bIsSelfClass ? iVarCount : propVarCount; / / for (int i = 0; i < sharedVarCount; i++) { / const char *varName = bIsSelfClass ? ivar_getName(*(ivarList + i)) : property_getName(*(propList + i)); / NSString *key = [NSString stringWithUTF8String:varName]; / /*valueForKey只能获取本类所有变量以及所有层级父类的属性,不包含任何父类的私有变量(会崩溃)*/ / id varValue = [self valueForKey:key]; / NSArray *filters = @[@"superclass", @"description", @"debugDescription", @"hash"]; / if (varValue && [filters containsObject:key] == NO) { / [coder encodeObject:varValue forKey:key]; / } / } / free(ivarList); / free(propList); / cls = class_getSuperclass(cls); / } / }
在需要序列化的类里面只需要导入该.h文件,然后写一句 SERIALIZE_CODER_DECODER()
即可
如果需要对序列化后的对象保存到硬盘和从硬盘读取,下面演示的是保存到NSUserDefault,保存到其他地方操作类似
Myobject *object = [Myobject new]; object.property1 = 赋值1; object.property2 = 赋值2; object.property3= 赋值3; NSData *data1 = [NSKeyedArchiver archivedDataWithRootObject:object]; [[NSUserDefaults standardUserDefaults]setObject:data1 forKey:@"object"]; [[NSUserDefaults standardUserDefaults]synchronize];
读取
NSData *data = [[NSUserDefaults standardUserDefaults]objectForKey:@"object"]; MyObject *object = [NSKeyedUnarchiver unarchiveObjectWithData:data];//反序列化
如果被序列化的对象的属性是其他类的实例,那么其他类也必须支持序列化,一直递归下去。
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提 下定义作用于这些元素的新操作。
假设我们要实现的多个子类拥有相似的功能,我们一般会把这些子类抽象出来一个父类,这样外界只需要面向抽象父类编程即可。如下图所示:
而访问者模式是把这些方法分别实现为一个个的类,然后把这些子类放到一个集合里面,接着对这些子类循环执行这些方法。就是把操作和子类集合分离开来。这样以后扩展功能,只需要添加一个功能类即可,就不用修改子类,看着很美好是吧。
但是我想说访问者模式是所有设计模式里面最没有用的一个模式,原因如下:
而访问者模式唯一的优点就是把操作和对象结构分离,可以在不改变对象结构的前提下给对象添加功能。而继承方式如果要添加功能,就必须给每个子类的添加功能和给父类添加接口,但是对比起来我更愿意使用继承方式,因为简单,也不需要增加那么多子类。虽然说使用继承模式实现违反了开闭原则,但是权衡来看,继承模式优点更多。
当然上面的只是我个人见解,大家自行判断使用哪种方式好。可以参考下面这个demo,分别用继承和访问者模式实现同样的功能,大家自己体会下。
访问者模式VS继承模式Demo
定义对象间的一种一对多的依赖关系 ,当一个对象的状态发生改变时 , 所有依赖于它的对象 都得到通知并被自动更新。
这个就不用多说了吧,iOS开发中经常用到的 NSNotification
就是这个。如果你有兴趣自己实现下该模式,那么下面这个小demo你可以看看。
观察者模式Demo
定 义 一 个 操 作 中 的 算 法 的 骨 架 , 而 将 一 些 步 骤 延 迟 到 子 类 中 。 可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
这个在日常码代码的时候应该是有意识或者无意识的都会有用到,简单来说就是父类要实现一个很大的功能,需要很多步骤,可以在父类里面把这些步骤抽象出来,成为一个算法骨架。然后子类去具体实现这些步骤。不同的子类可以有不同的实现方式,但是算法结构不改变。直接看图吧
很简单对吧,平时开发中应该经常会用到,算是简单实用的一个设计模式
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
我现在使用中文写作,每个句子都需要遵循一定的语法,比如句子大多数由主谓宾构成,然后你看到我写的文字,你会根据这个规律来解读这句话的意思。
那么对于计算机来说,要识别一个由特定语法构成的具体,也可以给它指定一套规则让他解读,比如正则表达式。关于解释器的使用就不演示了,这玩意太高大了,一般编译器才会用到。
有兴趣可以看下这篇文章,有演示如何使用解释器模式
解释器模式
断断续续花了2个半月的时间终于把设计模式系列写完,对自己算是学习的总结,也希望对大家有所帮助,个人感觉学习设计模式还是十分有必要的,如果你使用面向对象语言编程的话(目前流行的语言大多数是面向对象),它可以帮助你打开程序设计世界一扇新的大门。只要多加练习思考,相信你会慢慢领悟到如何构建可复用、灵活、低耦合的程序的。
学习完设计模式只是开端,接下来需要通过大量阅读优秀的开源项目,并且勤加练习、思考,才能领悟透彻设计模式背后的思想。与君共勉,加油!