书接上文
上次使用了一些简单地重构方法,得到的最终代码如下:
.h 未做改动,文件省略
.m 文件
1 | #import "GUIResetPasswordController.h" |
模板方法
模板方法
设计模式是最常用来避免直接复制代码
的设计模式.
1. 隔离变化
所谓的隔离变化,概括起来说就是:
- 对比两份或者多份代码,找出不同点
- 抽取出方法 (要求子类必须重写)
- 将公共代码作为模板类.
- 子类实现差异方法
2.创建模板类
新建模板控制器 GUIVaiidationTemplateController
在编写模板类的时候,我的最佳实践步骤是:
- 版本控制保存当前状态
- 拷贝重构好的代码到模板类
- 原控制器继承模板类
- 将差异抽取,并在子类中分别实现
拷贝完毕的代码:
1 |
|
好的,现在开始对比两个子类. 两个子类的代码和上面的代码类似,就不上代码了.
以上面的代码为例,首先提取变化.
我粘贴完毕之后,发现在跳转控制器的地方,需要有这个控制器 GUIChangePasswordController
,而另一个注册的子类没有,所以这就是一个变化,应该将这个变化放到一个方法中.
在子类和模板类的方法有所不同, 模板类做的只是把方法提取出来,同时要求子类必须实现,自己不需要写具体的实现.
模板类中原代码:
1 | [SMS_SDK commitVerifyCode:self.verifyCodeTextfield.text result:^(enum SMS_ResponseState state) { |
隔离变化后代码:
1 | [SMS_SDK commitVerifyCode:self.verifyCodeTextfield.text result:^(enum SMS_ResponseState state) { |
重头戏来了 ! OC中没有任何关键字能要求子类必须重写父类的方法.那么很多的设计模式是不是对OC就不适用了呢 ? 其实还是有方法的.
在OC 2.0之后 , 大家都用 @throw了,更符合规范,但是1.0的 NSAssert 更加简洁,谁优谁劣,自己选择.
对应的要求子类必须实现的代码:
1 | -(id)pushTargetController{ |
可以看到,写了异常代码之后,返回值也不是必须的了,爽 !
到两个子类中实现跳转代码,为了方便,我称呼为 子类A 和 子类B
子类A1
2
3
4
5-(id)pushTargetController{
return [[GUIChangePasswordController alloc]init];
}
子类B
1 | -(id)pushTargetController{ |
最后一步
接下来的工作就比较简单暴力了. 把两个子类中完全相同的代码删掉.
将差异的部分提取到父类,抽取方法. 重复进行.
收尾
运行,测试.
子类A完整代码:
1 | #import "GUIResetPasswordController.h" |
子类B 代码:
1 | @implementation GUISignupPasswordController |
清爽吧 ?
结语
注意点
- 要养成随手编译的好习惯,尤其是在重构代码的过程中,随时发现问题.
- 善用git svn等版本控制工具,它们是你的后悔药.
不要再拷贝代码了
就如刚才的例子,如果有新需求,需要更改样式或者其他内容,那我需要分别修改两个类.这还只是两个类,如果是10个,20个呢 ? 程序员加班多得原因之一,就是为自己的不规范负责.
使用了模板方法,其实只要维护一份代码即可,以后增加新的类 ,只需要继承模板 ,维护起来非常方便.