“我们虽然在构造软件,但软件也会重新塑造我们”。在写iOS项目架构-模块化的时候,我仍然觉得我所构建的统一行为方式还算不错,可以写出来与大家探讨探讨。昨日将应用发布的闲暇之余阅读了objc中国的这篇文章,令我明白尚有更优的解决方案。本文从实际的例子出发,发表一下我的拙见,用以和上文做对照,权当抛砖引玉。
###BaseViewController
####例子1
我们在构建一个App的时候,会有许多相同的行为。例如,任何需要网络请求的界面,总会需要一个indicator来告诉用户,“噢,我正在操作,请等我一下”。或者某个时候我们需要给用户一点小提示,来对他们的操作进行反馈等等。每一个ViewController都会有这样的行为。这是我们很直接地想到我们需要一个父类。
于是我们新建一个基类,BaseViewController
继承于UIViewController
@interface BaseViewController : UIViewController
/**
* a little pop window used to replace laertView
*
* @param tips tips will show to user
*/
- (void)showTips:(NSString *)tips;
/**
* When some operation need user waiting,use this method to show a HDU.
*
* @param title will show to user
*/
- (void)showIndicatorWithTitle:(NSString *)title;
- (void)hideIndicator;
@end
好了,现在我们所有的业务相关ViewController都继承自BaseViewController,并且在调用网络请求前轻松地写一句[self showIndicatorWithTitle:@"加载中..."]
,并且在网络请求完成后的回调里写下[self hideIndicator]
就可以轻松地完成指示器的显示隐藏了。感谢我们的继承!
####例子2
再举个例子。在我的项目里访问服务器的时候需要Token,Token在某些情况下有可能过期。Token过期的时候会返回一个Token过期的状态码,此时需要用户重新登录。这个情况我们可以总结成:
- 绝大多数ViewController都会发送网络请求
- 每一个网络请求都会返回Token过期
- 每个ViewController都需要处理Token过期的情况
因此我们可以将这一行为抽取出来,写在BaseViewController中。这样只需要在各个业务模块的的Model层收到Token过期消息的时候,用Notification转发给BaseViewController就可以了。其他诸如网络请求失败等情况都是如此。
每个项目有不同的统一行为,我们需要根据不同的行为抽取不同的基类。
###使用协议
但是有一种情况。例如LoginViewController
需要和BaseViewController
一样的显示/隐藏Indicator的接口,但却没有其他相同的行为。这时继承就显得不太合适了。
想象一下BaseViewController
因为Token过期Present了LoginViewController
,而LoginViewController
因为继承了BaseViewController
又不停地Present另一个LoginViewController
…这时候如果使用继承的话,就不得不写一些脏代码来避免这样的情况。
正如那篇文章所说,此时我们可以用协议@protocol
来替代继承。
@protocol PSIndicator <NSObject>
- (void)showTips:(NSString *)tips;
- (void)showIndicatorWithTitle:(NSString *)title;
- (void)hideIndicator;
@end
然后在LoginViewController
中:
@interface LoginViewController : UIViewController<PSIndicator>
@end
###最后
如果你在读这篇文章的时候想到,网络请求相关的内容为什么不直接封装在网络操作相关的模块,由它来负责指示器的显示和隐藏。这样还能省去手动调用的麻烦。关于这一点,在这篇文章里已经做了讨论。