【推荐】【老外写的iOS设计模式系列】第5部分 适配器模式
2013-09-18 14:25
489 查看
本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns.
由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么错误,请与我联系谢谢。
适配器(Adapter)模式
适配器可以让一些接口不兼容的类一起工作。它包装一个对象然后暴漏一个标准的交互接口。
如果你熟悉适配器设计模式,苹果通过一个稍微不同的方式来实现它-苹果使用了协议的方式来实现。你可能已经熟悉UITableViewDelegate, UIScrollViewDelegate, NSCoding 和 NSCopying协议。举个例子,使用NSCopying协议,任何类都可以提供一个标准的copy方法。
如何使用适配器模式
前面提到的水平滚动视图如下图所示:
为了开始实现它,在工程导航视图中右键点击View组,选择New File...使用iOS\Cocoa
Touch\Objective-C class 模板创建一个类。命名这个新类为HorizontalScroller,并且设置它是UIView的子类。
打开HorizontalScroller.h文件,在@end 行后面插入如下代码:
上面的代码定义了一个名为HorizontalScrollerDelegate的协议,它采用Objective-C 类继承父类的方式继承自NSObject协议。去遵循NSObject协议或者遵循一个本身实现了NSObject协议的类 是一条最佳实践,这使得你可以给HorizontalScroller的委托发送NSObject定义的消息。你不久会意识到为什么这样做是重要的。
在@protocol和@end之间,你定义了委托必须实现以及可选的方法。所以增加下面的方法:
这里你既有必需的方法也有可选方法。必需的方法要求委托必须实现它,因为它提供一些必需的数据。在这里,必需的是视图的数量,指定索引位置的视图,以及用户点击视图后的行为,可选的方法是初始化视图;如果它没有实现,那么HorizontalScroller将缺省用第一个索引的视图。
下一步,你需要在HorizontalScroller类中引用新建的委托。但是委托的定义是在类的定义之后的,所以在类中它是不可见的,怎么办呢?
解决方案就是前置声明委托协议以便编译器(和Xcode)知道协议的存在。如何做?你只需要在@interface行前面增加下面的代码即可:
继续在HorizontalScroller.h文件中,在@interface 和@end之间增加如下的语句:
这里你声明属性为weak.这样做是为了防止循环引用。如果一个类强引用它的委托,它的委托也强引用那个类,那么你的app将会出现内存泄露,因为任何一个类都不能释放调分配给另一个类的内存。
id意味着delegate属性可以用任何遵从HorizontalScrollerDelegate的类赋值,这样可以保障一定的类型安全。
reload方法在UITableView的reloadData方法之后被调用,它重新加载所有的数据去构建水平滚动视图。
用如下的代码取代HorizontalScroller.m的内容:
让我们来对上面每个注释块的内容进行一一分析:
1. 定义了一系列的常量以方便在设计的时候修改视图的布局。水平滚动视图中的每个子视图都将是100*100,10点的边框的矩形.
2. HorizontalScroller遵循UIScrollViewDelegate协议。因为HorizontalScroller使用UIScrollerView去滚动专辑封面,所以它需要用户停止滚动类似的事件
3.创建了UIScrollerView的实例。
下一步你需要实现初始化器。增加下面的代码:
滚动视图完全充满了HorizontalScroller。UITapGestureRecognizer检测滚动视图的触摸事件,它将检测专辑封面是否被点击了。如果专辑封面被点击了,它会通知HorizontalScroller的委托。
现在,增加下面的代码:
Gesture对象被当做参数传递,让你通过locationInView:导出点击的位置。
接下来,你调用了numberOfViewsForHorizontalScroller:委托方法,HorizontalScroller实例除了知道它可以安全的发送这个消息给委托之外,它不知道其它关于委托的信息,因为委托必须遵循HorizontalScrollerDelegate协议。
对于滚动视图中的每个子视图,通过CGRectContainsPoint方法发现被点击的视图。当你已经找到了被点击的视图,给委托发送horizontalScroller:clickedViewAtIndex:消息。在退出循环之前,将被点击的视图放置到滚动视图的中间。
现在增加下面的代码去重新加载滚动视图:
我们来一步步的分析代码中有注释的地方:
1. 如果没有委托,那么不需要做任何事情,仅仅返回即可。
2. 移除之前添加到滚动视图的子视图
3. 所有的视图的位置从给定的偏移量开始。当前的偏移量是100,它可以通过改变文件头部的#DEFINE来很容易的调整。
4. HorizontalScroller每次从委托请求视图对象,并且根据预先设置的边框来水平的放置这些视图。
5. 一旦所有视图都设置好了以后,设置UIScrollerView的内容偏移(contentOffset)以便用户可以滚动的查看所有的专辑封面。
6. HorizontalScroller检测是否委托实现了initialViewIndexForHorizontalScroller:方法,这个检测是需要的,因为这个方法是可选的。如果委托没有实现这个方法,0就是缺省值。最后设置滚动视图为协议规定的初始化视图的中间。
当数据已经发生改变的时候,你要执行reload方法。当增加HorizontalScroller到另外一个视图的时候,你也需要调用reload方法。增加下面的代码来实现后面一种场景:
didMoveToSuperview方法会在视图被增加到另外一个视图作为子视图的时候调用,这正式重新加载滚动视图的最佳时机。
最后我们需要确保所有你正在浏览的专辑数据总是在滚动视图的中间。为了这样做,当用户的手指拖动滚动视图的时候,你将需要做一些计算。
再一次在HorizontalScroller.m中增加如下方法:
为了计算当前视图到中间的距离,上面的代码考虑了滚动视图当前的偏移量,视图的尺寸以及边框。最后一行代码是重要的,一当子视图被置中,你将需要将这种变化通知委托。
为了检测用户在滚动视图中的滚动,你必需增加如下的UIScrollerViewDelegate方法:
scrollViewDidEndDragging:willDecelerate:方法在用户完成拖动的时候通知委托。如果视图还没有完全的停止,那么decelerate参数为true.当滚动完全停止的时候,系统将会调用scrollViewDidEndDecelerating.在两种情况下,我们都需要调用我们新增的方法去置中当前的视图,因为当前的视图在用户拖动以后可能已经发生了变化。
你的HorizontalScroller现在已经可以使用了。浏览你刚刚写的代码,没有涉及到任何与Album或AlbumView类的信息。这个相对的棒,因为这意味着这个新的滚动视图是完全的独立和可复用的。
构建的工程确保每个资源可以正确编译。
现在HorizontalScroller完整了,是时候去在app使用它了。打开ViewController.m 增加下面的导入语句:
增加HorizontalScrollerDelegate协议为ViewController遵循的协议:
在类的扩展中增加下面的实例变量:
现在你可以实现委托方法;你可能会感到惊讶,因为只需要几行代码就可以实现大量的功能啦。
在ViewController.m中增加下面的代码:
它设置保存当前专辑数据的变量,然后调用showDataForAlbumAtIndex:方法显示专辑数据。
注意:在#pragma mark 指令后面写方法代码是一种通用的实践。c 编译器会忽略调这些行,但是如果你通过Xcode的弹出框的时候,你将看到这些指令会帮你把代码组织成有独立和粗体标题的组。这可以帮你使得你的代码更方便在Xcode中导航。
接下来,增加下面的代码:
正如你意识到的,这个是返回滚动视图所有子视图数量的协议方法。因为滚动视图要显示所有专辑的封面,这个数量就是专辑记录的数量。
现在,增加下面的代码:
这里你创建了一个新的AlbumView,并且将它传递给HorizontalScroller。
够了,仅仅三个简短的方法就可以显示一个漂亮的水平滚动视图。
是的,你任然需要创建滚动视图,并且把它增加到你的主视图中,但是在这样做之前,你增加下面的方法先:
这个方法通过LibraryAPI加载专辑数据,然后根据当前视图的索引设置当前显示的视图。如果当前的视图索引小于0,意味着当前没有选定任何视图,此时可以选择第一个专辑来显示,否则下面一个专辑将会显示。
现在在viewDidLoad的[self showDataForAlbumAtIndex:0]之前增加下面的代码来初始化滚动视图:
上面的代码简单的创建了一个HorizontalScroller类的实例,设置它的背景色,委托,增加它到主视图,然后加载所有子视图去显示专辑数据。
注意:如果一个协议变得特别冗长,包含太多的方法。你应该考虑将它氛围更家细粒度的协议。UITableViewDelegate 和 UITableViewDataSource是一个好的例子。因为它们都是UITableView的协议。试着设计你的协议以便每个协议都关注特定的功能。
构建并运行你的on过程,查看一下你帅气十足的水平滚动视图吧:
对了,等等。水平滚动视图没问题,但是为什么没有显示封面呢?
是的,那就对了-你还没有实现下载封面的代码。为了实现这个功能,你需要去新增一个下载图片的方法。因为所有对服务的访问都通过LibraryAPI,那我们就可以在LibraryAPI中实现新的方法。然而我们首先需要虑一些事情:
1. AlbumView不应该直接和LibraryAPI交互。你不想混淆显示逻辑和网络交互逻辑。
2. 同样的原因,LibraryAPI也不应该知道AlbumView。
3. 一旦封面已经下载,LibraryAPI需要通知AlbumView,因为AlbumView显示专辑封面。
听上去是不是挺糊涂的?不要灰心。你将学习如何使用观察者模式来实现它。
原文出处:http://xmuzyq.iteye.com/blog/1942381
由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么错误,请与我联系谢谢。
适配器(Adapter)模式
适配器可以让一些接口不兼容的类一起工作。它包装一个对象然后暴漏一个标准的交互接口。
如果你熟悉适配器设计模式,苹果通过一个稍微不同的方式来实现它-苹果使用了协议的方式来实现。你可能已经熟悉UITableViewDelegate, UIScrollViewDelegate, NSCoding 和 NSCopying协议。举个例子,使用NSCopying协议,任何类都可以提供一个标准的copy方法。
如何使用适配器模式
前面提到的水平滚动视图如下图所示:
为了开始实现它,在工程导航视图中右键点击View组,选择New File...使用iOS\Cocoa
Touch\Objective-C class 模板创建一个类。命名这个新类为HorizontalScroller,并且设置它是UIView的子类。
打开HorizontalScroller.h文件,在@end 行后面插入如下代码:
@protocolHorizontalScrollerDelegate <NSObject> // methods declaration goes in here @end
上面的代码定义了一个名为HorizontalScrollerDelegate的协议,它采用Objective-C 类继承父类的方式继承自NSObject协议。去遵循NSObject协议或者遵循一个本身实现了NSObject协议的类 是一条最佳实践,这使得你可以给HorizontalScroller的委托发送NSObject定义的消息。你不久会意识到为什么这样做是重要的。
在@protocol和@end之间,你定义了委托必须实现以及可选的方法。所以增加下面的方法:
@required // ask the delegate how many views he wants to present inside the horizontal scroller - (NSInteger)numberOfViewsForHorizontalScroller:(HorizontalScroller*)scroller; // ask the delegate to return the view that should appear at <index> - (UIView*)horizontalScroller:(HorizontalScroller*)scroller viewAtIndex:(int)index; // inform the delegate what the view at <index> has been clicked - (void)horizontalScroller:(HorizontalScroller*)scroller clickedViewAtIndex:(int)index; @optional // ask the delegate for the index of the initial view to display. this method is optional // and defaults to 0 if it's not implemented by the delegate - (NSInteger)initialViewIndexForHorizontalScroller:(HorizontalScroller*)scroller;
这里你既有必需的方法也有可选方法。必需的方法要求委托必须实现它,因为它提供一些必需的数据。在这里,必需的是视图的数量,指定索引位置的视图,以及用户点击视图后的行为,可选的方法是初始化视图;如果它没有实现,那么HorizontalScroller将缺省用第一个索引的视图。
下一步,你需要在HorizontalScroller类中引用新建的委托。但是委托的定义是在类的定义之后的,所以在类中它是不可见的,怎么办呢?
解决方案就是前置声明委托协议以便编译器(和Xcode)知道协议的存在。如何做?你只需要在@interface行前面增加下面的代码即可:
@protocol HorizontalScrollerDelegate;
继续在HorizontalScroller.h文件中,在@interface 和@end之间增加如下的语句:
@property (weak) id<HorizontalScrollerDelegate> delegate; - (void)reload;
这里你声明属性为weak.这样做是为了防止循环引用。如果一个类强引用它的委托,它的委托也强引用那个类,那么你的app将会出现内存泄露,因为任何一个类都不能释放调分配给另一个类的内存。
id意味着delegate属性可以用任何遵从HorizontalScrollerDelegate的类赋值,这样可以保障一定的类型安全。
reload方法在UITableView的reloadData方法之后被调用,它重新加载所有的数据去构建水平滚动视图。
用如下的代码取代HorizontalScroller.m的内容:
#import "HorizontalScroller.h" // 1 #define VIEW_PADDING 10 #define VIEW_DIMENSIONS 100 #define VIEWS_OFFSET 100 // 2 @interfaceHorizontalScroller () <UIScrollViewDelegate> @end // 3 @implementationHorizontalScroller { UIScrollView *scroller; } @end
让我们来对上面每个注释块的内容进行一一分析:
1. 定义了一系列的常量以方便在设计的时候修改视图的布局。水平滚动视图中的每个子视图都将是100*100,10点的边框的矩形.
2. HorizontalScroller遵循UIScrollViewDelegate协议。因为HorizontalScroller使用UIScrollerView去滚动专辑封面,所以它需要用户停止滚动类似的事件
3.创建了UIScrollerView的实例。
下一步你需要实现初始化器。增加下面的代码:
- (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)]; scroller.delegate = self; [self addSubview:scroller]; UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollerTapped:)]; [scroller addGestureRecognizer:tapRecognizer]; } return self; }
滚动视图完全充满了HorizontalScroller。UITapGestureRecognizer检测滚动视图的触摸事件,它将检测专辑封面是否被点击了。如果专辑封面被点击了,它会通知HorizontalScroller的委托。
现在,增加下面的代码:
- (void)scrollerTapped:(UITapGestureRecognizer*)gesture { CGPoint location = [gesture locationInView:gesture.view]; // we can't use an enumerator here, because we don't want to enumerate over ALL of the UIScrollView subviews. // we want to enumerate only the subviews that we added for (int index=0; index<[self.delegate numberOfViewsForHorizontalScroller:self]; index++) { UIView *view = scroller.subviews[index]; if (CGRectContainsPoint(view.frame, location)) { [self.delegate horizontalScroller:self clickedViewAtIndex:index]; [scroller setContentOffset:CGPointMake(view.frame.origin.x - self.frame.size.width/2 + view.frame.size.width/2, 0) animated:YES]; break; } } }
Gesture对象被当做参数传递,让你通过locationInView:导出点击的位置。
接下来,你调用了numberOfViewsForHorizontalScroller:委托方法,HorizontalScroller实例除了知道它可以安全的发送这个消息给委托之外,它不知道其它关于委托的信息,因为委托必须遵循HorizontalScrollerDelegate协议。
对于滚动视图中的每个子视图,通过CGRectContainsPoint方法发现被点击的视图。当你已经找到了被点击的视图,给委托发送horizontalScroller:clickedViewAtIndex:消息。在退出循环之前,将被点击的视图放置到滚动视图的中间。
现在增加下面的代码去重新加载滚动视图:
- (void)reload { // 1 - nothing to load if there's no delegate if (self.delegate == nil) return; // 2 - remove all subviews [scroller.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [obj removeFromSuperview]; }]; // 3 - xValue is the starting point of the views inside the scroller CGFloat xValue = VIEWS_OFFSET; for (int i=0; i<[self.delegate numberOfViewsForHorizontalScroller:self]; i++) { // 4 - add a view at the right position xValue += VIEW_PADDING; UIView *view = [self.delegate horizontalScroller:self viewAtIndex:i]; view.frame = CGRectMake(xValue, VIEW_PADDING, VIEW_DIMENSIONS, VIEW_DIMENSIONS); [scroller addSubview:view]; xValue += VIEW_DIMENSIONS+VIEW_PADDING; } // 5 [scroller setContentSize:CGSizeMake(xValue+VIEWS_OFFSET, self.frame.size.height)]; // 6 - if an initial view is defined, center the scroller on it if ([self.delegate respondsToSelector:@selector(initialViewIndexForHorizontalScroller:)]) { int initialView = [self.delegate initialViewIndexForHorizontalScroller:self]; [scroller setContentOffset:CGPointMake(initialView*(VIEW_DIMENSIONS+(2*VIEW_PADDING)), 0) animated:YES]; } }
我们来一步步的分析代码中有注释的地方:
1. 如果没有委托,那么不需要做任何事情,仅仅返回即可。
2. 移除之前添加到滚动视图的子视图
3. 所有的视图的位置从给定的偏移量开始。当前的偏移量是100,它可以通过改变文件头部的#DEFINE来很容易的调整。
4. HorizontalScroller每次从委托请求视图对象,并且根据预先设置的边框来水平的放置这些视图。
5. 一旦所有视图都设置好了以后,设置UIScrollerView的内容偏移(contentOffset)以便用户可以滚动的查看所有的专辑封面。
6. HorizontalScroller检测是否委托实现了initialViewIndexForHorizontalScroller:方法,这个检测是需要的,因为这个方法是可选的。如果委托没有实现这个方法,0就是缺省值。最后设置滚动视图为协议规定的初始化视图的中间。
当数据已经发生改变的时候,你要执行reload方法。当增加HorizontalScroller到另外一个视图的时候,你也需要调用reload方法。增加下面的代码来实现后面一种场景:
- (void)didMoveToSuperview { [self reload]; }
didMoveToSuperview方法会在视图被增加到另外一个视图作为子视图的时候调用,这正式重新加载滚动视图的最佳时机。
最后我们需要确保所有你正在浏览的专辑数据总是在滚动视图的中间。为了这样做,当用户的手指拖动滚动视图的时候,你将需要做一些计算。
再一次在HorizontalScroller.m中增加如下方法:
- (void)centerCurrentView { int xFinal = scroller.contentOffset.x + (VIEWS_OFFSET/2) + VIEW_PADDING; int viewIndex = xFinal / (VIEW_DIMENSIONS+(2*VIEW_PADDING)); xFinal = viewIndex * (VIEW_DIMENSIONS+(2*VIEW_PADDING)); [scroller setContentOffset:CGPointMake(xFinal,0) animated:YES]; [self.delegate horizontalScroller:self clickedViewAtIndex:viewIndex]; }
为了计算当前视图到中间的距离,上面的代码考虑了滚动视图当前的偏移量,视图的尺寸以及边框。最后一行代码是重要的,一当子视图被置中,你将需要将这种变化通知委托。
为了检测用户在滚动视图中的滚动,你必需增加如下的UIScrollerViewDelegate方法:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (!decelerate) { [self centerCurrentView]; } } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { [self centerCurrentView]; }
scrollViewDidEndDragging:willDecelerate:方法在用户完成拖动的时候通知委托。如果视图还没有完全的停止,那么decelerate参数为true.当滚动完全停止的时候,系统将会调用scrollViewDidEndDecelerating.在两种情况下,我们都需要调用我们新增的方法去置中当前的视图,因为当前的视图在用户拖动以后可能已经发生了变化。
你的HorizontalScroller现在已经可以使用了。浏览你刚刚写的代码,没有涉及到任何与Album或AlbumView类的信息。这个相对的棒,因为这意味着这个新的滚动视图是完全的独立和可复用的。
构建的工程确保每个资源可以正确编译。
现在HorizontalScroller完整了,是时候去在app使用它了。打开ViewController.m 增加下面的导入语句:
#import "HorizontalScroller.h" #import "AlbumView.h"
增加HorizontalScrollerDelegate协议为ViewController遵循的协议:
@interfaceViewController ()<UITableViewDataSource, UITableViewDelegate, HorizontalScrollerDelegate>
在类的扩展中增加下面的实例变量:
HorizontalScroller *scroller;
现在你可以实现委托方法;你可能会感到惊讶,因为只需要几行代码就可以实现大量的功能啦。
在ViewController.m中增加下面的代码:
#pragma mark - HorizontalScrollerDelegate methods - (void)horizontalScroller:(HorizontalScroller *)scroller clickedViewAtIndex:(int)index { currentAlbumIndex = index; [self showDataForAlbumAtIndex:index]; }
它设置保存当前专辑数据的变量,然后调用showDataForAlbumAtIndex:方法显示专辑数据。
注意:在#pragma mark 指令后面写方法代码是一种通用的实践。c 编译器会忽略调这些行,但是如果你通过Xcode的弹出框的时候,你将看到这些指令会帮你把代码组织成有独立和粗体标题的组。这可以帮你使得你的代码更方便在Xcode中导航。
接下来,增加下面的代码:
- (NSInteger)numberOfViewsForHorizontalScroller:(HorizontalScroller*)scroller { return allAlbums.count; }
正如你意识到的,这个是返回滚动视图所有子视图数量的协议方法。因为滚动视图要显示所有专辑的封面,这个数量就是专辑记录的数量。
现在,增加下面的代码:
- (UIView*)horizontalScroller:(HorizontalScroller*)scroller viewAtIndex:(int)index { Album *album = allAlbums[index]; return [[AlbumView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) albumCover:album.coverUrl]; }
这里你创建了一个新的AlbumView,并且将它传递给HorizontalScroller。
够了,仅仅三个简短的方法就可以显示一个漂亮的水平滚动视图。
是的,你任然需要创建滚动视图,并且把它增加到你的主视图中,但是在这样做之前,你增加下面的方法先:
- (void)reloadScroller { allAlbums = [[LibraryAPI sharedInstance] getAlbums]; if (currentAlbumIndex < 0) currentAlbumIndex = 0; else if (currentAlbumIndex >= allAlbums.count) currentAlbumIndex = allAlbums.count-1; [scroller reload]; [self showDataForAlbumAtIndex:currentAlbumIndex]; }
这个方法通过LibraryAPI加载专辑数据,然后根据当前视图的索引设置当前显示的视图。如果当前的视图索引小于0,意味着当前没有选定任何视图,此时可以选择第一个专辑来显示,否则下面一个专辑将会显示。
现在在viewDidLoad的[self showDataForAlbumAtIndex:0]之前增加下面的代码来初始化滚动视图:
scroller = [[HorizontalScroller alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 120)]; scroller.backgroundColor = [UIColor colorWithRed:0.24f green:0.35f blue:0.49f alpha:1]; scroller.delegate = self; [self.view addSubview:scroller]; [self reloadScroller];
上面的代码简单的创建了一个HorizontalScroller类的实例,设置它的背景色,委托,增加它到主视图,然后加载所有子视图去显示专辑数据。
注意:如果一个协议变得特别冗长,包含太多的方法。你应该考虑将它氛围更家细粒度的协议。UITableViewDelegate 和 UITableViewDataSource是一个好的例子。因为它们都是UITableView的协议。试着设计你的协议以便每个协议都关注特定的功能。
构建并运行你的on过程,查看一下你帅气十足的水平滚动视图吧:
对了,等等。水平滚动视图没问题,但是为什么没有显示封面呢?
是的,那就对了-你还没有实现下载封面的代码。为了实现这个功能,你需要去新增一个下载图片的方法。因为所有对服务的访问都通过LibraryAPI,那我们就可以在LibraryAPI中实现新的方法。然而我们首先需要虑一些事情:
1. AlbumView不应该直接和LibraryAPI交互。你不想混淆显示逻辑和网络交互逻辑。
2. 同样的原因,LibraryAPI也不应该知道AlbumView。
3. 一旦封面已经下载,LibraryAPI需要通知AlbumView,因为AlbumView显示专辑封面。
听上去是不是挺糊涂的?不要灰心。你将学习如何使用观察者模式来实现它。
原文出处:http://xmuzyq.iteye.com/blog/1942381
相关文章推荐
- 【推荐】【老外写的iOS设计模式系列】第3部分 门面模式
- 【推荐】【老外写的iOS设计模式系列】第4部分 装饰器模式
- 【推荐】【老外写的iOS设计模式系列】第7部分 备忘录模式
- 【推荐】【老外写的iOS设计模式系列】第8部分 命令模式
- 【推荐】【老外写的iOS设计模式系列】第1部分
- 【推荐】【老外写的iOS设计模式系列】第2部分 MVC模式&单例模式
- 【推荐】【老外写的iOS设计模式系列】第6部分 观察者模式
- iOS 设计模式系列:Adapter – 适配器模式
- iOS设计模式之三(适配器模式,观察者模式)
- IOS设计模式浅析之适配器模式(Adapter)
- iOS设计模式(代码分析系列2:简单工厂模式)
- 翻转设计模式系列——第三部分--设计模式(1)--概览
- 23种设计模式 第二部分 结构模式(1) 适配器模式
- 设计模式系列 - 简单工厂 推荐
- C#设计模式系列:适配器模式(Adapter)
- Android重拾设计模式系列——适配器模式
- 深入理解JavaScript系列(39):设计模式之适配器模式
- PHP设计模式之适配器模式 推荐
- 设计模式干货系列:(七)适配器模式【学习难度:★★☆☆☆,使用频率:★★★★☆】
- 深入理解JavaScript系列(39):设计模式之适配器模式详解