PPPan's 平凡之路

非宁静无以致远

做一个互联网内容的贡献者。


iOS项目架构 - 统一行为

“我们虽然在构造软件,但软件也会重新塑造我们”。在写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

最后

如果你在读这篇文章的时候想到,网络请求相关的内容为什么不直接封装在网络操作相关的模块,由它来负责指示器的显示和隐藏。这样还能省去手动调用的麻烦。关于这一点,在这篇文章里已经做了讨论。