`

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

    博客分类:
  • ios
 
阅读更多
由 @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之间,增加如下的属性和方法原型:
@property (nonatomic, copy, readonly) NSString *title, *artist, *genre, *coverUrl, *year;  
- (id)initWithTitle:(NSString*)title artist:(NSString*)artist coverUrl:(NSString*)coverUrl year:(NSString*)year;
 

注意到新增代码中所有的属性都是只读的,因为在Album对象创建以后,不需要修改它们的值。
新增的方法是对象初始化器(object initializer),当你创建一个新的专辑(album)对象的时候,你需要传递专辑(album),艺术家,专辑封面URL,以及年份。
现在打开Album.m文件,在@implementation @end 之间 增加如下代码:
- (id)initWithTitle:(NSString*)title artist:(NSString*)artist coverUrl:(NSString*)coverUrl
  year:(NSString*)year {
    self = [super init];
    if (self)
    {
        _title = title;
        _artist = artist;
        _coverUrl = coverUrl;
        _year = year;
        _genre = @"Pop";
    }
    return self;
}
 
这里没什么复杂花哨的东西,仅仅是一个创建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之间 增加如下的方法原型:
- (id)initWithFrame:(CGRect)frame   albumCover:(NSString*)albumCover;
现在打开AlbumView.m,用如下代码替换@implementation 之后所有的代码:
@implementationAlbumView
{
    UIImageView *coverImage;
    UIActivityIndicatorView *indicator;
}
 
- (id)initWithFrame:(CGRect)frame albumCover:(NSString*)albumCover
{
    self = [super initWithFrame:frame];
    if (self)
    {
 
        self.backgroundColor = [UIColor blackColor];
        // the coverImage has a 5 pixels margin from its frame
        coverImage = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, frame.size.width-10,
 
        frame.size.height-10)];
        [self addSubview:coverImage];
 
        indicator = [[UIActivityIndicatorView alloc] init];
        indicator.center = self.center;
        indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
        [indicator startAnimating];
        [self addSubview:indicator];
    }
    return self;
}
 
@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,重复同样的过程来创建ViewController组。
现在拖动Album.hAlbum.m去模型组,拖动AlbumView.hAlbumView.m去视图组,最后拖动ViewController.hViewController.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,用如下代码替换它的内容:
@interfaceLibraryAPI : NSObject
 
+ (LibraryAPI*)sharedInstance;
 
@end
 
现在打开LibraryAPI.m,在@implementation 那一行后面插入下面的方法:
+ (LibraryAPI*)sharedInstance
{
    // 1
    static LibraryAPI *_sharedInstance = nil;
 
    // 2
    static dispatch_once_t oncePredicate;
 
    // 3
    dispatch_once(&oncePredicate, ^{
        _sharedInstance = [[LibraryAPI alloc] init];
    });
    return _sharedInstance;
}
 
在这个简短的方法中,有一些需要需要注意的点:
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之后,增加下面的代码:
- (NSArray*)getAlbums;
- (void)addAlbum:(Album*)album atIndex:(int)index;
- (void)deleteAlbumAtIndex:(int)index;
 

上面是你需要处理专辑数据的方法的原型。
打开PersistencyManager.m文件,在@implementation行之前,增加下面的代码:
@interfacePersistencyManager () {
    // an array of all albums
    NSMutableArray *albums;
}
 
上面增加了一个类扩张(class extension),这是另外一个增加私有方法和变量以至于外部类不会看到它们的方式。这里,你申明了一个数组NSMutableArry 来保存专辑数据。这个数组是可变的方便你增加和删除专辑。
现在在PersistencyManager.m文件中@implementation行之后增加如下代码:
- (id)init
{
    self = [super init];
    if (self) {
        // a dummy list of albums
        albums = [NSMutableArrayarrayWithArray:
                 @[[[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"],
                 [[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"],
                 [[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"],
                 [[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"],
                 [[Album alloc] initWithTitle:@"American Pie" artist:@"Madonna" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:@"2000"]]];
    }
    return self;
}
 
init中,你用五条样例专辑填充数组。如果你不喜欢上面的专辑,你可以自由用你喜欢的专辑替换它们。
现在在PersistencyManager.m文件中增加下面的三个方法:
- (NSArray*)getAlbums
{
    return albums;
}
 
- (void)addAlbum:(Album*)album atIndex:(int)index
{
    if (albums.count >= index)
        [albums insertObject:album atIndex:index];
    else
        [albums addObject:album];
}
 
- (void)deleteAlbumAtIndex:(int)index
{
    [albums removeObjectAtIndex:index];
}
 
这些方法让你可以增加和删除专辑。
构建你的工程确保每个资源都可以被正确的编译。
这时候,你可能想知道PersistencyManager类来自哪里?因为它不是一个单例类。下一部分,我们将探究LibraryAPI PersistencyManager之间的关系,那时候你将看到门面或者外观(Facade)模式。
接下来是本系列的其它文章:
 
 
 
 
 
 
 
 
 
  • 大小: 78 KB
  • 大小: 16.1 KB
  • 大小: 11.5 KB
  • 大小: 34.7 KB
  • 大小: 4.1 KB
分享到:
评论
3 楼 sjx19871225 2013-12-27  
非常好,辛苦了,转~
2 楼 ba1du 2013-12-17  
不错,学习了。
1 楼 紫荆伯爵 2013-09-17  
谢谢分享,学习了

相关推荐

    OBJECTIVE-C编程之道 IOS设计模式解析电子书+源代码

    OBJECTIVE-C编程之道 IOS设计模式解析电子书+源代码《Objective-C编程之道:iOS设计模式解析》是基于iOS的软件开发指南。书中应用GoF的经典设计模式,介绍了如何在代码中应用创建型模式、结构型模式和行为模式,...

    ios设计模式学习实例

    本应用实例中包含大部分的cocoa设计模式(已添加详细注释): 创建型:单例(单态)和抽象工厂 结构型:MVC、装饰器、适配器、外观(门面)、组合模式 行为型:观察者、备忘录、责任链、命令模式

    PHP常用的三种设计模式汇总

    比如单例设计模式、工厂设计模式、观察者模式、MVC框架结构设计模式等。 接下来我们一起来学习PHP中最常用的三种设计模式:单例设计模式、工厂设计模式和观察者设计模式。 单例设计模式 所谓单例模式,即在应用程序...

    IOS开发和设计模式简化

    抽象工厂模式适配器模式对象建模模式责任链模式命令模式...在iOS编程,利用设计模式可以大大提高你的开发效率,虽然在编写代码之初你需呀花费较大时间把各种业务逻辑封装起来。模型-视图-控制器(MVC)设计模式是被大家

    《iOS6开发指南》精彩书摘

    iOS开发中常用的4种模式,分别为单例模式、委托模式、观察者模式、MVC模式。按照问题提出、实现原理、应用案例的结构介绍了每种模式的适用情况、实现的原理并同时用具体案例来说明每个案例的实现方式。 第4章...

    DesignPatterns-iOS:使用适用于iOS环境的设计模式的应用示例

    DesignPatterns-iOS 使用设计模式的示例应用程序。 在此示例中,我使用了: MVC:模型视图控制器。 单例:在整个应用程序中仅创建一个实例。 外观:到复杂子系统的单一接口。 装饰器:在不修改对象代码的情况下...

    DemoCode:博客演示代码

    DemoCodeBlog Demo CodeiOS动画指南 - 2.Layer ...简化动画实现的EasyAnimation库设计模式(Swift) - 1.MVC和代理设计模式(Swift) - 2.单例模式、备忘录模式和策略模式设计模式(Swift) - 3.观察者模式、建造者模式

    基于iOS平台智能点餐系统的设计与实现 (2015年)

    随着人们生活水平的提高,人们外出就餐成为一种生活方式,社会...本文使用UIKit、animation、multithread等技术,采用MVC模式并辅用单例、代理、观察者等多种设计模式实现点餐系统,系统更稳定可靠并且拥有良好的可扩展性。

    swift3.0_NHFX_swift iOS

    作者NorthDogLi,源码swift3.0_NHFX_swift,此项目基于swift3.0 最近从工作之余抽出时间用swift把项目的商品首页写了一遍本项目涉及的技术点和框架使用包括: 框架使用.snapKit,... 5.mvc设计模式 6.纯代码布局

    resume:我的在线简历

    较强的代码阅读能力和语言学习能力理解并能熟练运用iOS应用开发中常见设计模式,如:MVC、委托、广播、单例等熟练使用CocoaTouch基础框架:Foundation 、UIKit、CoreGraphics、AVFoundation、Mapkit等掌握Core Data...

    ChooseItemDemo表格案例

    作者boatOfBigSea,源码ChooseItemDemo,可以任意选择、取消表格的cell,并且获得选中表格的数据。根据项目要求,要做一...其中用到MVC,单例设计模式,用一个工具类来专门处理相关的数据,希望能对小伙伴们有所帮助。

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    HybridApp 一种可以下载的Native App,其用户界面的全部或者部分元素在嵌入式浏览器组件(WebView之类的)里面运行 优雅降级 一开始就构建站点的完整功能,然后针对浏览器测试和修复。认为应该针对那些最高级、最...

    是男人就下100层游戏

    项目介绍这毕竟是个小游戏,本身其实没有什么技术难点,主要在于项目的设计,分为如下几个小模块1、玩家 (Player)玩家类我用了单例模式,继承自CCSprite,因为贯穿游戏一直会有玩家存在,在这个版本里也不会有第二个...

Global site tag (gtag.js) - Google Analytics