IOS设计模式之一(MVC模式,单例模式)

本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns.

由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么翻译错误,请与我联系谢谢。

iOS
设计模式-你可能已经听说过这个词,但是你真正理解它意味着什么吗?虽然大多数的开发者可能都会认为设计模式是非常重要的,然而关于设计模式这一主题的文章却不多,并且有时候我们开发者在写代码的时候也不会太关注它。

在软件设计领域,设计模式是对通用问题的可复用的解决方案。设计模式是一系列帮你写出更可理解和复用代码的模板,设计模式帮你创建松耦合的代码以便你不需要费多大力就可以改变或者替换代码中的组件。

如果你刚接触设计模式,我们有好消息告诉你!首先,多亏了Cocoa的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践。

其次本指南将带你使用绝大多数(并不是所有)Cocoa中频繁使用的IOS 设计模式。

本指南被分为了许多部分,每个部分涉及一个设计模式。在每个部分中,你将会了解到如下内容:

?     
设计模式是什么?

?     
你为什么要用设计模式?

?     
如何使用设计模式,以及在使用的时候,哪里是合适的,哪里是需要注意的坑。

在本指南中,你将创建一个音乐库应用,这个应用将显示你的专辑以及它们相关联的信息。

在开发本应用的过程中,你将熟悉被大量使用的Cocoa
设计模式:

?     
创建型:单利(单态)和 抽象工厂

?     
结构型:模型-视图-控制器,装饰器,适配器,外观(门面)和组合模式

?     
行为型:观察者,备忘录,责任链和命令模式

不要被误导认为这是一篇关于设计模式理论的文章,在本音乐应用中,你将使用这些设计模式中的大多数,最终你的音乐应用将长的像下图所示的那样:

我们开始吧!

下载 starter
project
,导出zip文件的内容,然后用xcode打开BlueLibrary.xcodeproj.

工程里面没有太多的文件,仅仅包含缺省的ViewController以及空实现的HTTP
Client.

注意:当你创建一个新的Xcode工程的时候,你的代码其实已经涉及到了设计模式,你知道吗?模型-视图-控制器,委托,协议,单例-你不费吹灰之力就可以免费使用它们啦。

在你深入到第一个设计模式之前,你首先必须创建两个类,用这两个类去保存和显示音乐库专辑的信息。

在Xcode中,导航到"File\New\File..."(或者按Command+N快捷键),选择IOS>Cocoa Touch,然后Objective-C class,点击下一步。设置类名称为Album,父类选择NSObject,点击下一步,然后创建。

打开Album.h文件,在@interface和@end之间,增加如下的属性和方法原型:

Objective -c代码  

  1. @property (nonatomic, copy, readonly) NSString *title, *artist, *genre, *coverUrl, *year;

  2. - (id)initWithTitle:(NSString*)title artist:(NSString*)artist coverUrl:(NSString*)coverUrl year:(NSString*)year;

注意到新增代码中所有的属性都是只读的,因为在Album对象创建以后,不需要修改它们的值。

新增的方法是对象初始化器(object initializer),当你创建一个新的专辑(album)对象的时候,你需要传递专辑(album)名,艺术家,专辑封面URL,以及年份。

现在打开Album.m文件,在@implementation 和
@end 之间 增加如下代码:

Objective-c代码  

  1. - (id)initWithTitle:(NSString*)title artist:(NSString*)artist coverUrl:(NSString*)coverUrl

  2. year:(NSString*)year {

  3. self = [super init];

  4. if (self)

  5. {

  6. _title = title;

  7. _artist = artist;

  8. _coverUrl = coverUrl;

  9. _year = year;

  10. _genre = @"Pop";

  11. }

  12. return self;

  13. }

这里没什么复杂花哨的东西,仅仅是一个创建Album实例的初始化方法而已。

在Xcode中,再一次导航到"File\New\File..."选择Cocoa Touch,然后Objective-C
class,点击下一步。设置类名为AlbumView,但是这一次设置父类为UIView。点击下一步然后点击创建。

注意:如果你发现键盘快捷键更容易使用,Command+N将创建一个新文件,Command+Option+N将创建一个新组,Command+B将构建你的工程,Command + R 将运行它。

现在打开AlbumView.h,在@interface 和 @end之间 增加如下的方法原型:

Objective-c代码  

  1. - (id)initWithFrame:(CGRect)frame   albumCover:(NSString*)albumCover;

现在打开AlbumView.m,用如下代码替换@implementation 之后所有的代码:

Objective-c代码  

  1. @implementationAlbumView

  2. {

  3. UIImageView *coverImage;

  4. UIActivityIndicatorView *indicator;

  5. }
  6. - (id)initWithFrame:(CGRect)frame albumCover:(NSString*)albumCover

  7. {

  8. self = [super initWithFrame:frame];

  9. if (self)

  10. {
  11. self.backgroundColor = [UIColor blackColor];

  12. // the coverImage has a 5 pixels margin from its frame

  13. coverImage = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, frame.size.width-10,
  14. frame.size.height-10)];

  15. [self addSubview:coverImage];
  16. indicator = [[UIActivityIndicatorView alloc] init];

  17. indicator.center = self.center;

  18. indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;

  19. [indicator startAnimating];

  20. [self addSubview:indicator];

  21. }

  22. return self;

  23. }
  24. @end

上面的代码里,你首先需要注意的是coverImage实例变量。它表示这个专辑的封面图。第二个变量是一个通过旋转来指示封面图正在下载的指示器。

在初始化器的实现中你设置背景颜色为黑色,创建了有5像素边框的图片视图,同时还创建了指示器。

注意:你可能想知道为什么私有变量在实现文件中定义,而不是在接口文件中?这是因为AlbumView以外的类不需要知道这些变量的存在,这些变量仅仅只在类内部函数使用。如果你在开发给其它开发者使用的框架,这个约定就显得十分重要了。

构建(Command +
B)你的工程确保每件事情都井井有条,都ok吗?然后准备迎接我们的第一个设计模式!

模型-视图-控制器(MVC)模式 - 设计模式之王

模型-视图-控制器(MVC) 是Cocoa的构建块之一,毫无疑问它是使用最频繁的设计模式。它根据通用的角色去划分类,这样就使得类的

职责可以根据角色清晰的划分开来。

涉及到的三个角色如下:

Model:

模型保存应用程序的数据,定义了怎么去操作它。例如在本应用中模型就是Album类。

View:

视图是模型的可视化表示以及用户交互的控件;基本上来说,所有的UIView对象以及它的子类都属于视图。在本应用中AlbumView代表了视图。

Controller:

控制器是一个协调所有工作的中介者(Mediator)。它访问模型中的数据并在视图中展示它们,同时它们还监听事件和根据需要操作数据。你可以猜猜哪个类是控制器吗?它正是:ViewController。

一个MVC模式的好的实现也就意味着每一个对象都会被划分到上面所说的组中。

我们可以很好的用下图来描述通过控制器实现的视图到模型的交互过程:

模型会把任何数据的变更通知控制器,然后控制器更新视图数据。视图对象通知控制器用户的操作,控制器要么根据需要来更新模型,要么检索任何被请求的数据。

你可能在想为什么不能仅仅使用控制器,在一个类中实现视图和模型,这样貌似更加容易?

所有的这些都归结于代码关注点分离以及复用。在理想的状态下,视图应该和模型完全的分离。如果视图不依赖某个实际的模型,那么视图就可以被复用来展示不同模型的数据。

举个例子来说,如果将来你打算加入电影或者书籍到你的资料库中,你仍然可以使用同样的AlbumView去显示电影和书籍数据。更进一步来说,如果你想创建一个新的与专辑有关联的工程,你可以很简单的复用Album类,因为它不依赖任何视图。这就是MVC的强大之处。

如何使用MVC模式

首先,你需要确保在你工程中的每个类是控制器,模型和视图中的一种,不要在一个类中组合两种角色的功能。到目前为止,你创建了一个Album类和AlbumView类,这样做挺好的。

其次,为了确保你能符合这种工作方法,你应该创建三个工程组(Project
Group)来保存你的代码,每个工程组只存放一种类型的代码。

导航到"文件\新建\组(File\New\Group)"(或者按下Command +
Option + N),命名组为Model,重复同样的过程来创建View和Controller组。

现在拖动Album.h和Album.m去模型组,拖动AlbumView.h和AlbumView.m去视图组,最后拖动ViewController.h和ViewController.m到控制器组。

此时工程结构应该看起来和下图类似:

没有了之前所有文件都散落在各处,现在你的工程已经开起来好多了。显然你也可以有其它的组和类,但是本应用的核心包含在这三个类别中(Model,View,Controller)。

现在所有的组件都已经安排好了,你需要从某处获取专辑数据。你将创建一个贯穿于代码的管理数据的API-这也就代表将有机会去讨论下一个设计模式 -
单例(单态)模式。

单例(单态)模式

单例设计模式确保对于一个给定的类只有一个实例存在,这个实例有一个全局唯一的访问点。它通常采用懒加载的方式在第一次用到实例的时候再去创建它。

注意:苹果大量使用了此模式。例如:[NSUserDefaults
standardUserDefaults], [UIApplication sharedApplication], [UIScreen mainScreen],
[NSFileManager defaultManager],所有的这些方法都返回一个单例对象。

你很可能会想为什么这么关心是否一个类有多个实例?毕竟代码和内存都是廉价的,对吗?

有一些情况下,只有一个实例显得非常合理。举例来说,你不需要有多个Logger的实例,除非你想去写多个日志文件。或者一个全局的配置处理类:实现线程安全的方式访问共享实例是容易的,比如一个配置文件,有好多个类同时修改这个文件。

如何使用单例模式

首先来看看下面的图:

上面的图描述了一个有单一属性(它就是单一实例)和sharedInstance,init两个方法的类。

客户端第一次发送sharedInstance消息的时候,instance属性尚未被初始化,所以此时你需要创建一个新的实例,然后返回它的引用。

当你下一次调用sharedInstance的时候,instance不需要任何初始化可以立即返回。这个逻辑保证总是只有一个实例。

你接下来将用这个模式来创建一个管理所有专辑数据的类。

你将注意到工程中有一个API的组,在这个组里你可以放入给你应用提供服务的所有类。在此组中,用IOS\Cocoa
Touch\Objective-C class 模板创建一个新类,命名它为LibraryAPI,设置父类为NSObject.

打开LibraryAPI.h,用如下代码替换它的内容:

Objective-c代码  

  1. @interfaceLibraryAPI : NSObject
  2. + (LibraryAPI*)sharedInstance;
  3. @end

现在打开LibraryAPI.m,在@implementation 那一行后面插入下面的方法:

Objective-c代码  

  1. + (LibraryAPI*)sharedInstance

  2. {

  3. // 1

  4. static LibraryAPI *_sharedInstance = nil;
  5. // 2

  6. static dispatch_once_t oncePredicate;
  7. // 3

  8. dispatch_once(&oncePredicate, ^{

  9. _sharedInstance = [[LibraryAPI alloc] init];

  10. });

  11. return _sharedInstance;

  12. }

在这个简短的方法中,有一些需要需要注意的点:

1.声明一个静态变量去保存类的实例,确保它在类中的全局可用性。

2.声明一个静态变量dispatch_once_t ,它确保初始化器代码只执行一次

3.使用Grand
Central Dispatch(GCD)执行初始化LibraryAPI变量的block.这 
正是单例模式的关键:一旦类已经被初始化,初始化器永远不会再被调用。

下一次你调用sharedInstance的时候,dispatch_once块中的代码将不会执行(因为它已经被执行了一次),你将得到原先已经初始化好的实例。

注意: 为了学习更多关于GCD方面的信息以及如何使用,请查看本站指南Multithreading and Grand
Central Dispatch
 和 How to
Use Blocks

你现在有一个单例的对象作为管理专辑数据的入口。咋们更进一步来创建一个处理资料库数据持久化的类。

在API组中,使用iOS\Cocoa
Touch\Objective-C class 模板 创建一个新类,命名它为PersistencyManager,设置父类为NSObject.

打开PersistencyManager.h 在文件头部增加下面的导入语句:

#import
"Album.h"

接下来,在PersistenceManager.h文件的@interface之后,增加下面的代码:

Objective-c代码  

  1. - (NSArray*)getAlbums;

  2. - (void)addAlbum:(Album*)album atIndex:(int)index;

  3. - (void)deleteAlbumAtIndex:(int)index;

上面是你需要处理专辑数据的方法的原型。

打开PersistencyManager.m文件,在@implementation行之前,增加下面的代码:

Objective-c代码  

  1. @interfacePersistencyManager () {

  2. // an array of all albums

  3. NSMutableArray *albums;

  4. }

上面增加了一个类扩张(class extension),这是另外一个增加私有方法和变量以至于外部类不会看到它们的方式。这里,你申明了一个数组NSMutableArry 来保存专辑数据。这个数组是可变的方便你增加和删除专辑。

现在在PersistencyManager.m文件中@implementation行之后增加如下代码:

Objective-c代码  

  1. - (id)init

  2. {

  3. self = [super init];

  4. if (self) {

  5. // a dummy list of albums

  6. albums = [NSMutableArrayarrayWithArray:

  7. @[[[Album alloc] initWithTitle:@"Best of Bowie" artist:@"David Bowie" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png" year:@"1992"],

  8. [[Album alloc] initWithTitle:@"It‘s My Life" artist:@"No Doubt" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png" year:@"2003"],

  9. [[Album alloc] initWithTitle:@"Nothing Like The Sun" artist:@"Sting" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png" year:@"1999"],

  10. [[Album alloc] initWithTitle:@"Staring at the Sun" artist:@"U2" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png" year:@"2000"],

  11. [[Album alloc] initWithTitle:@"American Pie" artist:@"Madonna" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:@"2000"]]];

  12. }

  13. return self;

  14. }

在init中,你用五条样例专辑填充数组。如果你不喜欢上面的专辑,你可以自由用你喜欢的专辑替换它们。

现在在PersistencyManager.m文件中增加下面的三个方法:

Objective-c代码  

  1. - (NSArray*)getAlbums

  2. {

  3. return albums;

  4. }
  5. - (void)addAlbum:(Album*)album atIndex:(int)index

  6. {

  7. if (albums.count >= index)

  8. [albums insertObject:album atIndex:index];

  9. else

  10. [albums addObject:album];

  11. }
  12. - (void)deleteAlbumAtIndex:(int)index

  13. {

  14. [albums removeObjectAtIndex:index];

  15. }

这些方法让你可以增加和删除专辑。

构建你的工程确保每个资源都可以被正确的编译。

这时候,你可能想知道PersistencyManager类来自哪里?因为它不是一个单例类。下一部分,我们将探究LibraryAPI 和PersistencyManager之间的关系,那时候你将看到门面或者外观(Facade)模式。

接下来是本系列的其它文章:

IOS设计模式之二(门面模式,装饰器模式)

IOS设计模式之三(适配器模式,观察者模式)

IOS设计模式之四(备忘录模式,命令模式)

IOS设计模式之一(MVC模式,单例模式),布布扣,bubuko.com

时间: 2024-10-01 04:57:11

IOS设计模式之一(MVC模式,单例模式)的相关文章

Android 设计模式之MVC模式

说到Android设计模式的MVC模式,估计很多人都是比较熟悉了,这里深入了解一下MVC到底是怎么回事,以ListView为例子讲解. 一.深入理解MVC概念 MVC即Model-View-Controller.M:逻辑模型,V:视图模型,C:控制器. MVC模式下,系统框架的类库被划分为3种:模型(Model).视图(View).控制器(Controller).模型对象负责建立数据结构和相应的行为操作处理.视图对象负责在屏幕上渲染出相应的图形信息展示给用户看.控制器对象负责截获用户的按键和屏幕

iOS:使用MVC模式帮ViewController瘦身

如何给UIViewController瘦身 随着程序逻辑复杂度的提高,你是否也发现了App中一些ViewController的代码行数急剧增多,达到了2,3千行,甚至更多.这时如果想再添加一点功能或者修改现有逻辑变得让人无比头疼.如果你遇到了这类问题,那是时候停下来了,思考一下如何更好地组织代码,给VC瘦身.本文将会阐述如何结合MVC的思想帮你的VC瘦身同时提高复用和可扩展性. 一.开发中常见的现象和缺点 iOS中最常见的一种设计模式就是MVC,但在实际开发过程中,我们因为这样.那样的原因让单纯

【设计模式】MVC模式

MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式.这种模式用于应用程序的分层开发. Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO.它也可以带有逻辑,在数据变化时更新控制器. View(视图) - 视图代表模型包含的数据的可视化. Controller(控制器) - 控制器作用于模型和视图上.它控制数据流向模型对象,并在数据变化时更新视图.它使视图与模型分离开. 实现 我们将创建一个作为模型的 Student 对象.Student

设计模式-12 MVC模式

一 MVC设计模式 MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式,它是一个存在于服务器 表达层的模型,它将应用分开,改变应用之间的高度耦合 MVC设计模式将应用程序分离为3个主要的方面:Model,View和Controller 1 Model Model代表了描述业务路逻辑,业务模型.数据操作.数据模型的一系列类的集合.这层也定义了数据修改和操作的业务规则. 2 View View代表了UI组件,像CSS,JQuery,html等.他只负责展示从co

ios 深刻理解MVC模式—代理方法

在oc中MVC模式得到广泛应用,所谓MVC,即模型Model,视图View,控制器Controller 控制器通过模型数据控制视图,而代理方法则是控制器控制视图的所使用的方法. 使用代理所需要满足的条件: 1.代理协议:里面声明了代理方法 2.视图View定义满足代理协议的代理属性,例如 @property(nonatomic,weak)id<UITableViewDelegate>delegate; 3.给View提供一个或几个方法,方法内View的代理view.delegate 调用它的代

iOS设计模式之MVC和Delegate

MVC和Ddelegate可以说是iOS开发中最重要的两种设计模式了,MVC(Model-View-Controller)几乎是最常见的设计模式了,即使是别的平台的开发,不仅仅是应用,甚至是复杂的系统也能看到MVC的影子.对于搞移动开发的人来说MVC必须是要明白的设计模式.在iOS系统上Delegate同样是非常常见的设计模式,Delegate一般用于传递数据(可以是Model)和处理事件回调(你要认真的去研究一下UITableView的delegate或许你就明白了,而且delegate及其方

iOS设计模式——享元模式

公共交通(如公共汽车)已有一百多年的历史了.大量去往相同方向的乘客可以分担保有和经营车辆(如公共汽车)的费用.公共汽车有多个站台,乘客沿着路线在接近他们目的地的地方上下车.到达目的地的费用仅与行程有关.跟保有车辆相比,乘坐公共汽车要便宜得多.这就是利用公共资源的好处. 在面向对象软件设计中,我们利用公共对象不仅能节省资源还能提高性能.比方说,某个人物需要一个类的一百万个实例,但我们可以把这个类的一个实例让大家共享,而把某些独特的信息放在外部,节省的资源可能相当可观(一个实例与一百万个实例的差别)

iOS通用的MVC模式项目框架MobileProject

最近项目比较不赶的情况下,决定把一些通用.常用的内容集成在一个项目框架中,意在新项目中可以快速搭建:其实经过几个项目后,总是有一些重复的创建工作,可以使用本项目的内容直接进行开发:采用的是MVC的分层模式,本文将会重点介绍关于层级的划分及一些已经集成的第三方功能介绍:当然本项目的源代码已经上传到gitHub(地址:https://github.com/wujunyang/MobileProject)上面,当然要是对你有帮助记得给个星,假如大家有时间也可以一起完善,或者有什么问题也可以及时留言:

iOS设计模式-原型模式

| 导语 定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 通过深复制(克隆.拷贝)一个指定类型的对象来创建更多同类型的对象.这个指定的对象可被称为“原型”对象,也就是通过复制原型对象来得到更多同类型的对象.即原型设计模式.iOS中使用copy协议完成这个过程. 原型模式主要用于对象的深复制,它的核心是就是类图中的原型类Prototype. 原型模式的核心思想是把复制的过程封闭到对象的内部完成.第一个好处是修改复制的过程而不影响外部的使用者. 原型模式的优点及使用场景 简化对

iOS设计模式 - (4)策略模式

理论部分,参考博文:http://blog.csdn.net/hguisu/article/details/7558249 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法:当然也可以将这些查找算法封装在一个统一的方法中,