原文地址:http://www.raywenderlich.com/86477/introducing-ios-design-patterns-in-swift-part-1
Update 04/22/2015: Updated for Xcode 6.3 and Swift 1.2.
Update note: This tutorial was updated for iOS 8 and Swift by Vincent Ngo. Original Post by Tutorial team member Eli Ganem.
************原文只有swift版本,我顺便添加OC版本,因为不了解swift,所以写的OC代码可能会有错************
设计模式是在软件设计中对常见问题的复用解决方案,是帮助你写出便于理解与复用的代码的模板,也帮助你写出低耦合代码以便较为轻松地变更代码内容。
在这篇分为两个部分的教程中,你会创建一个Music Library app(音乐库应用),这个应用会展示音乐专辑和相关信息。
在开发这个app的过程中,你会熟悉最常用的Cocoa设计模式:
- Creational(创建型): Singleton(单例模式)
- Structural(结构型): MVC, Decorator(装饰模式), Adapter(适配器模式), Facade(外观模式).
- Behavioral(行为型): Observer(观察者模式),Memento(备忘录模式)
不要误以为这篇文章是走理论的;你在这个app中会用到大部分上面的设计模式,你的app最后会是这样:
下载开始项目,解压,在Xcode中打开BlueLibrarySwift.xcodeproj
此项目有三个重点:
1. 有两个IBOutlet连接到ViewController,一个是table view,一个是toolbar
2. storyboard有三个部分,已经为AutoLayout创建了约束,顶部部分是展示专辑封面的地方;封面下的部分是table view,展示专辑相关信息;最后一个部分是toolbar,有两个button,从图上可以看到:一个undo,一个删除。
3. 一个HTTP Client类(HTTPClient),类的实现由你稍后来编写。
Note:你知道当你创建一个新的Xcode项目时,你的代码就已经带有设计模式了吗?MVC,Delegate,Protocol,Singleton - 你直接得到了
在深入第一种设计模式前,需要先创建两个类分别存储,展示数据
- 新建:File\New\File…(或者按快捷键 Command+N).选择 iOS > Cocoa Touch Class ,点击Next. 类名写为Album,
NSObject的子类
. 最后:选择语言为Swift,点击Next,Create. - 打开Album.swift,添加下列属性。
1 // swift
2
3 var title : String!
4 var artist : String!
5 var genre : String!
6 var coverUrl : String!
7 var year : String!
8
9 // Objective-C
10
11 @property (nonatomic,copy) NSString *title;
12 @property (nonatomic,copy) NSString *artist;
13 @property (nonatomic,copy) NSString *genre;
14 @property (nonatomic,copy) NSString *coverUrl;
15 @property (nonatomic,copy) NSString *year;
创建了5个属性,Album类将会实时关注这5个属性。
- 接下来添加Album对象初始化方法
// Swift
init(title: String, artist: String, genre: String, coverUrl: String, year: String) {
super.init()
self.title = title
self.artist = artist
self.genre = genre
self.coverUrl = coverUrl
self.year = year
}
// Objective-C
// Album.h
+(Album *)initWithTitle:(NSString *) title artist:(NSString *)artist genre: (NSString *)genre coverUrl:(NSString *)coverUrl year:(NSString *)year;
// Album.m
+(Album *)initWithTitle:(NSString *) title artist:(NSString *)artist genre: (NSString *)genre coverUrl:(NSString *)coverUrl year:(NSString *)year{
if(self = [super init]){
self.title = title
self.artist = artist
self.genre = genre
self.coverUrl = coverUrl
self.year = year
}
}
- 重写description方法
// Swift
override var description: String {
return "title: \(title)" +
"artist: \(artist)" +
"genre: \(genre)" +
"coverUrl: \(coverUrl)" +
"year: \(year)"
}
// Objective-C
(NSString *)description{
return [NSString stringWithFormat:@"title:%@,artist:%@,genre:%@,coverUrl:%@,year:%@",self.title,self.artist,self.genre,self.coverUrl,self.year];
}
description方法以字符串形式返回Album对象的属性。
- 建立第二个类,把类名设定为AlbumView,继承自UIView
1 // Swift
2
3 private var coverImage: UIImageView!
4 private var indicator: UIActivityIndicatorView!
5
6 // Objective-C
7 // 写到.m文件里
8
9 @implementatin AlbumView
10
11 @property (nonatomic,weak) UIImageView *coverImage;
12 @property (nonatomic,weak) UIActivityIndicatorView *indicator;
13
14 @end
coverImage是专辑封面图片,第二个属性是实时跟踪封面加载情况的活动指示器
这两个属性设定为私有类型是因为在AlbumView类之外的地方不需要知道这两个属性的存在,它们只是用在内部实现功能里。当你要创建一个供大家使用的库或者框架时使用这个非常重要的规则就能保持私有变量信息不公开。
- 同样,为AlbumView类添加初始化方法
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
init(frame: CGRect, albumCover: String) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
backgroundColor = UIColor.blackColor()
coverImage = UIImageView(frame: CGRect(x: 5, y: 5, width: frame.size.width - 10, height: frame.size.height - 10))
addSubview(coverImage)
indicator = UIActivityIndicatorView()
indicator.center = center
indicator.activityIndicatorViewStyle = .WhiteLarge
indicator.startAnimating()
addSubview(indicator)
}
// 然而已经不知道OC应该怎么写。
NSCoder需要被初始化因为UIView遵从NSCoding
commonInit是一个辅助方法,它被用到了两个init方法当中,你之后会需要这些方法,设定AlbumView的默认条件。背景黑色,为imageView设置5个像素的边缘,以及将coverImage和indicator属性添加到父视图。
- 最后:添加下列方法
// Swift
func highlightAlbum(#didHighlightView: Bool) {
if didHighlightView == true {
backgroundColor = UIColor.whiteColor()
} else {
backgroundColor = UIColor.blackColor()
}
}
//OC
- (void) highlightAlbum:(BOOL)didHighlightView{
if([self didHighlightView]){
self.view.coverImage.backgroundColor = [UIColor whiteColor];
}else{
self.view.coverImage.backgroundColor = [UIColor blackColor];
}
}
高亮时专辑封面为白色,反之为黑色。
编译工程,确保一切都没有问题,然后准备开始第一个设计模式的学习。
MVC – The King of Design Patterns 设计模式王者
MVC是Cocoa的基石,毫无疑问它是被运用最多的设计模式。它按照对象的常规角色对对象进行分类,且鼓励开发者这样为代码创建目录以保证项目层次清晰明确。
这三种角色分别是:
- Model(模型):Model对象存储应用数据,定义管理这些数据的方法,就像在这个项目中你的Album类。
- View(视图):View对象负责把Model和与用户的交互借口视觉呈现出来,基本上就是所有的UIView衍生出的对象,就像这个项目中你的AlbumViw类。
- Controller(控制器):Controller对象是协调以上工作的媒介,它拿到Model的数据然后在View中将之呈现出来,还有监听交互情况并按照它管理数据,现在你知道你的项目中的Controller是谁了吗?没错,就是ViewController。
要想实现好这个设计模式,就要把每个对象都分配到Model,View,Controller组中。
下图可以描述MVC的关系:
当Model对象的数据发生改变时,它会通知Controller对象,然后Controller对象更新对应的View对象上展示的数据。当用户在View对象进行了交互操作时,View对象会通知Controller对象,然后Controller对象会更新对应的Model对象中的数据。
第一次更新:2016-03-02 11:58:18
当Model对象的数据发生改变时,它会通知Controller对象,相应的,Controller对象更新对应的View对象上展示的数据。当用户在View对象进行了交互操作时,View对象会通知Controller对象,然后Controller对象会更新对应的Model对象中的数据。
你可能会有这样的疑惑,为什么不丢弃Controller这个媒介,然后把View和Model的实现都写在一个类里,这样就不用通知来通知去,让View和Model直接通信?
这里提出两个概念,你们就明白这样做的目的了:低耦合性和高复用性。理想情况下,在一个应用中每个界面中的数据大多数是来自于多个Model对象,如果把View对象和Model对象绑定死了,那就没法处理这种情况了。
举个例子:如果你以后想在这个音乐仓库应用里添加电影仓库或图书仓库,你仍然可以使用AlbumView
这个View对象来展示你的电影或者图书Model对象。假如你的电影仓库需要展示电影主题曲的一些信息,那么你就可以复用Album
对象,因为它不依赖于任何View对象,这就是MVC的强大之处。
How to Use the MVC Pattern - 如何运用MVC模式
第一:你需要保证项目中的每个类要么是Model要么是Controller要么是View,不要将两个角色的功能放到一个类中。目前你已经将AlbumView类和Album类建好。
第二:遵从上面所讲的要点,将三个角色分别放在三个组中:点击File\New\Group,将组命名为Model,同样的,新建另外两个组,分别命名View,Controller。
第三:将三个类分别拖到对应的组中。
完成后你的项目结构应是这样的:
当然你可以多建一些类或者组,但是完成当前这个项目,这三个组就够了。
现在项目结构已经清楚了,接下来的工作就是从某个地方获取到专辑的相关数据。你可以创建一个名为API的类,用于负责整个应用的数据管理工作,这就需要用到接下来要讲的Singleton设计模式。
The Singleton Pattern - 单例模式
单例模式使一个类在整个应用生命周期内只存在一个实例,并且有一个全局的方法来访问这个实例。在单例模式下,当第一次访问某个类的实例时,该类通常使用延迟加载的方式创建该类的单例。
注意: Apple常用到这个方法.比如:
NSUserDefaults.standardUserDefaults()
,UIApplication.sharedApplication()
,UIScreen.mainScreen()
,NSFileManager.defaultManager()
都返回一个单例对象。
last updated 2016-03-03 10:53:49