第1章 你好,设计模式

概念

设计模式的概念是建筑师 Christopher Alexander 提出的,用于解决建筑行业经常发生的问题,后来这一概念被应用到其他领域,尤其是软件开发过程。说白了就是一套反复使用、多人知晓、经过分类的代码设计经验的总结。恰当的使用设计模式,可以使代码更加健壮、易复用和易扩展。此外还有一个好处,两个都会设计模式的人交流起来一定是高效的。打个比方,设计模式好比汉语中成语,用最简洁的语言传递信息。

在面向对象的世界里,有一些设计原则影响着设计模式,比如:“优先使用对象组合而不是类继承”和“针对接口编程而不是针对实现编程”。

例如,通过给程序的 变动部分 定义接口而对其封装和隔离,这些部分的变动就独立于程序的其他部分,因为他们不依赖任何细节。以后就可以变更或扩展这些可变的部分而不影响程序的其他部分。程序将因此能够灵活而可靠的进行变更,因为我们消除了部分与部分之间的依赖关系并减少了耦合。

设计模式起源-MVC

模型-视图-控制器(MVC)是 Cocoa Touch 中很多机制和技术的基础。在 MVC 设计模式中,对象根据功能分为三组,分别扮演模型、视图和控制器的角色。此外,MVC 也定义了三者之间的通信方式

模型对象用于解释“是什么”,维护应用程序的数据,并定义操作数据的特定逻辑。模型是为了解释特定问题的数据类型,通常是可以复用的。比如通讯录中的列表页和详情页都用到了 Person 类。

视图对象用于向用户展示信息,可以响应用户操作,并懂得如何将自己展现在屏幕上。视图可以和一个模型的部分或是多个模型合作,同时视图也可以是复用的,比如系统中常用的 UITableView 等。

虽然视图和模型关系密切,但是在 MVC 的应用程序中,两者之间没有耦合。注释1 除非性能原因需要视图对数据进行缓存,否则不应该在视图中持有和存储它所展示的数据。

控制器作为模型和视图的中间人或协调人,它建立起沟通渠道,使视图得以知晓模型的变更而给与响应。

注释1
我们在使用 UITableView 的过程中经常对自定义Cell 进行数据绑定操作,代码如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    CustomModel *model = _dataSource[indexPath.row];
    CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
    [cell configWithModel:model];
}
#import "CustomCell.h"
#import "CustomModel.h" 
// 多数情况下这么做只是为了控制器的代码看上去更加简洁,但是这么做已经违反了 MVC 的设计原则。
// 根据 MVC 的设计原则,视图和模型是独立的,系统中的类也确实是这样的,比如 UITableView。

@implementation CustomCell

- (void)configWithModel:(CustomModel *)model {
}

@end

MVC 之间的通信

控制器是如何协调模型和视图工作的呢,也就是三者之间是如何通信的呢?控制器可以不受限制的访问模型(即持有模型对象,将其作为自己的属性),如图所示从控制器指向模型一个单向箭头。类似的从控制器到视图的通信也是不受限制的,如图所示。而模型和视图是完全独立的,互不持有注释1 控制器可以直接访问视图和模型,视图和模型相互独立。

斯坦福大学iOS公开课.png

那从视图向控制器是如何通信的呢?因为通常来说视图是可以复用的,所以不能对控制器知道太多,既然视图不知道哪个控制器持有了它,所以只能以一种“盲”的方式和控制器通信。其中一种方式叫做target/action,控制器将自己作为视图的目标 target,同时提供一个动作 action,就像一支箭一样,告诉视图说“如果有人点击或是滑动了你,你就发送这个动作给我”。有时视图中发生的情况更复杂一些,控制器想要更具体的知道视图发生了什么,比如:will/should/did,以 UIScrollView 为例,我想在视图正在滚动和滚动结束时做一些事,这时 scrollView 自己没有办法去做,所以它委托控制器来做这些事,这是另外一种“盲”的通信方式。再以 UITableView 为例,tableView 中是没有持有数据的,而是通过 dataSource 对其赋值,这也是一种特殊的代理。

那从模型到控制器是如何通信的呢?模型和视图一样,不会知道控制器太多。但是当它发生变化是,控制器要知道。也是有两种方式,一种叫做通知,另一种叫做键值观察,简称 KVO

通常一个项目中会有很多组 MVC,不同组之间也会有交互发生。这时就可以把一组 MVC 看做是另外一组的视图,从而建立起整个项目。

设计原则

针对接口编程,而不是针对实现编程

Objective-C作为面向对象语言,自然可以通过继承的方式让我们能够从现成的类继承所需的大部分功能,从而快速定义新类。Objective-C 不支持多继承,但是有一种类似的东西叫做协议(protocol)。协议也是对象之间的一种合约,但本身不能实例化为对象。

协议并不定义任何实现,而只声明方法,以确定复合协议的类的行为。因此协议是只定义了抽象行为的“接口”。实现协议的类定义这些方法的实现,以执行真正的操作。另外一种定义通过抽象基类定义一些抽象方法,生成一些子类可以共享的默认行为。

使用协议有两个好处:

  • 只要对象符合客户端所要求的接口,客户端就不必在意所使用对象的确切类型;
  • 客户端只知道定义接口的协议或者抽象类,因此客户端对对象的类一无所知。

优先使用对象组合而不是类继承

类继承可以让我们实现复用,快速定义新类。继承也被称为白箱复用(white-box reuse),因为父类内部的实现细节对子类是可见的。对象组合可以代替继承。由于对象的内部细节对其他对象是不可见,所以这种复用方式也被成为黑箱复用(black-box reuse)。继承和组合各有优缺点。

继承

  • 优点:

    • 继承简单直接,关系在编译是静态定义;
    • 被复用的实现易于修改。
  • 缺点:

    • 因为继承是在编译时定义,所以无法在运行时变更从父类继承来的实现;
    • 子类的部分描述常常定义在父类中;
    • 子类直接面对父类实现的细节,因此破坏了封装
    • 父类实现的任何变更都会强制子类也进行变更,因为它们的实现联系在了一起;
    • 新的问题场景下继承的实现不再适用,必须重写父类或是继承来的实现。

组合

  • 优点:

    • 不会破坏封装,因为只通过接口来访问对象;
    • 大大减少实现的依存关系,因为对象的实现是通过接口来定义的;
    • 可以在运行时将任意对象替换为其他同类型的对象;
    • 有助于保持类的封装以专注于单一任务;
    • 类及其层次接口能够保持简洁,不至于过度膨胀而无法管理。
  • 缺点:

    • 设计中设计较多对象;
    • 系统的行为将依赖与不同对象间的关系,而不是定义于单个类中;

tips:尽管有以上缺点,对象组合仍有诸多好处。可以在部分使用继承来克服这些缺点。优先使用对象组合而不是类继承,并不是说完全不适用类继承。需要根据具体情况对如何用类和对象做出清晰的判断。如果系统设计合理,继承与组合可以互相配合。设计类时通常倾向与考虑对象组合。然后找出冗余行为,进行细化。如果找到冗余行为,也许意味着此处应该使用继承。

UML-类图

类图用来说明类的结构和类于类之间关系的示意图。Objective-C 中的协议和分类与其他面向对象语言略有不同。

1)依赖
依赖关系是类与类之间的联接。一个类依赖于另一个类的定义。如,一个人(Person)可以买车(Car)和房子(House),Person类依赖于Car和House的定义,因为Person引入了Car和House。与关联不同的是,Person类中没有Car和House的属性,Car和House的实例是以参量的方式传入到buy()方法中的。一般而言,依赖关系在Java语言中体现为局部变量,方法形参,或者对静态方法的调用。

image

2)关联
关联是类与类之间的联接,使一个类知道另一个类的属性和方法。关联可以是双向,也可以是单向的。一般使用成员变量来实现。

image

3)聚合
聚合是一种强的关联关系。是整体和个体之间的关系。例如,汽车类与引擎类,轮胎类之间的关系就是整体与个体之间的关系。与关联关系一样,聚合关系也是通过实例变量实现的。但是关联关系涉及的两个类在同一层次,而聚合关系中两个类处在不平等的层次上,一个代表整体,一个代表部分。

image

4)组合
组合也是关联关系的一种,一种比聚合关系强的关系。组合关系中的部分类不能独立于整体类存在。整体类和部分类有相同的生命周期。如Person类和Leg类。

image

5)继承/泛化
泛化和继承其实是一个逆过程 泛化就是有子类抽象出一个父类 而继承就是由父类具体化一个子类 例如足球比联赛跟什么西甲 意甲 英超之间就是泛化/继承的关系

image

6)组合和聚合的区别
组合和聚合都属于关联,所以它们之间难免有相似之处,区别举例来说明:
程老师的《大话》里举大那个大雁的例子很贴切 在此我就借用一下 大雁喜欢热闹害怕孤独 所以它们一直过着群居的生活 这样就有了雁群 每一只大雁都有自己的雁群 每个雁群都有好多大雁 大雁与雁群的这种关系就可以称之为聚合 另外每只大雁都有两只翅膀 大雁与雁翅的关系就叫做组合 有此可见 聚合的关系明显没有组合紧密 大雁不会因为它们的群主将雁群解散而无法生存 而雁翅就无法脱离大雁而单独生存——组合关系的类具有相同的生命周期
聚合关系图:

image

聚合关系图:

image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容