demoiOS之设计模式iOS

设计模式系列15--最终篇

2016-12-25  本文已影响587人  西木柚子
image

终于要写完这个系列了,GOF的设计模式总共有23种,我在前面的篇章只写了其中16个,剩下的7个放到这篇文章一起写了。因为这6个设计模式要么是iOS自身语言特性已经实现了,要么是没有什么太大的利用价值,所以放在一起简单讲解下。

今天要学习如下7种设计模式:

  1. 原型模式
  1. 迭代器模式
  2. 备忘录模式
  3. 访问者模式
  4. 观察者模式
  5. 模板方法模式
  6. 解释器模式

下面来一一讲解


1、原型模式

定义

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

看定义就知道原型模式就是iOS里面的对象克隆,这个iOS已经帮我们做好了,只要我们实现NSCopying或者NSMutableCopying协议就行了,具体实现看这篇文章:

深浅拷贝

UML结构图

image

2、迭代器模式

定义

提供一种方法顺序访问一个聚合对象中各个元素 , 而又不需暴露该对象的内部表示。

简单来说就是定义一个迭代器,可以使用同一种方式去遍历不同的的聚合对象,iOS里面的聚合对象就三种:NSArray、NSSet、NSDictonary。其他语言可能还有hash表,map表,链表等等iOS同样已经帮我们实现了迭代器,有三种方式去迭代集合对象

方法1、NSEnumertor抽象类

NSEnumertor本身是一个抽象类,依靠几个工厂方法来创建并返回具体的迭代器,比如下面的例子使用NSEnumertor来迭代NSArray

NSArray *array = @[@"啦啦啦", @"天空是无用且垂死的星辰", @"人在塔在",@"二营长,你他娘的意大利炮呢"];
NSEnumerator *enumertor = [array objectEnumerator];
id item ;
while (item = [enumertor nextObject]) {
    NSLog(@"%@", item);
}

当然还可以使用NSEnumertor来迭代NSSet和NSDictonary,就不在一一演示了,具体看这篇文章:

NSEnumertor迭代器的使用

方法2、基于块的枚举

 [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;
      }
  }];

方法3、快速枚举

这是苹果推荐的方法,比for循环效率高,具体使用如下:

NSArray *array = @[@"啦啦啦", @"天空是无用且垂死的星辰", @"人在塔在",@"二营长,你他娘的意大利炮呢"];

 for (NSString *str in array) {
         NSLog(@"%@", str);

     }

UML结构图

image

3、备忘录模式

定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 这样以后就可将该对象恢复到原先保存的状态。

简单来说就是在某个特定的时刻序列化对象,保存到内存或者硬盘上,在需要的时候再回复。保存的一般都是对象的属性值,比如我们我们打游戏的时候可以保存当前进度,然后可以读档,和这个是一样的道理。

如果是系统的类比如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];//反序列化

注意

如果被序列化的对象的属性是其他类的实例,那么其他类也必须支持序列化,一直递归下去。

UML结构图

image

4、访问者模式

定义

表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提 下定义作用于这些元素的新操作。

假设我们要实现的多个子类拥有相似的功能,我们一般会把这些子类抽象出来一个父类,这样外界只需要面向抽象父类编程即可。如下图所示:

image

而访问者模式是把这些方法分别实现为一个个的类,然后把这些子类放到一个集合里面,接着对这些子类循环执行这些方法。就是把操作和子类集合分离开来。这样以后扩展功能,只需要添加一个功能类即可,就不用修改子类,看着很美好是吧。

UML结构图

image

问题

但是我想说访问者模式是所有设计模式里面最没有用的一个模式,原因如下:

而访问者模式唯一的优点就是把操作和对象结构分离,可以在不改变对象结构的前提下给对象添加功能。而继承方式如果要添加功能,就必须给每个子类的添加功能和给父类添加接口,但是对比起来我更愿意使用继承方式,因为简单,也不需要增加那么多子类。虽然说使用继承模式实现违反了开闭原则,但是权衡来看,继承模式优点更多。

继承方式对比访问者模式

当然上面的只是我个人见解,大家自行判断使用哪种方式好。可以参考下面这个demo,分别用继承和访问者模式实现同样的功能,大家自己体会下。

访问者模式VS继承模式Demo


5、观察者模式

定义

定义对象间的一种一对多的依赖关系 ,当一个对象的状态发生改变时 , 所有依赖于它的对象 都得到通知并被自动更新。

这个就不用多说了吧,iOS开发中经常用到的NSNotification就是这个。如果你有兴趣自己实现下该模式,那么下面这个小demo你可以看看。

观察者模式Demo

UML结构图

image

6、模板方法模式

定义

定 义 一 个 操 作 中 的 算 法 的 骨 架 , 而 将 一 些 步 骤 延 迟 到 子 类 中 。 可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

这个在日常码代码的时候应该是有意识或者无意识的都会有用到,简单来说就是父类要实现一个很大的功能,需要很多步骤,可以在父类里面把这些步骤抽象出来,成为一个算法骨架。然后子类去具体实现这些步骤。不同的子类可以有不同的实现方式,但是算法结构不改变。直接看图吧

image

很简单对吧,平时开发中应该经常会用到,算是简单实用的一个设计模式

UML结构图

image

7、解释器模式

定义

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

我现在使用中文写作,每个句子都需要遵循一定的语法,比如句子大多数由主谓宾构成,然后你看到我写的文字,你会根据这个规律来解读这句话的意思。

那么对于计算机来说,要识别一个由特定语法构成的具体,也可以给它指定一套规则让他解读,比如正则表达式。关于解释器的使用就不演示了,这玩意太高大了,一般编译器才会用到。

有兴趣可以看下这篇文章,有演示如何使用解释器模式

解释器模式


总结

断断续续花了2个半月的时间终于把设计模式系列写完,对自己算是学习的总结,也希望对大家有所帮助,个人感觉学习设计模式还是十分有必要的,如果你使用面向对象语言编程的话(目前流行的语言大多数是面向对象),它可以帮助你打开程序设计世界一扇新的大门。只要多加练习思考,相信你会慢慢领悟到如何构建可复用、灵活、低耦合的程序的。

学习完设计模式只是开端,接下来需要通过大量阅读优秀的开源项目,并且勤加练习、思考,才能领悟透彻设计模式背后的思想。与君共勉,加油!

上一篇下一篇

猜你喜欢

热点阅读