最近新入职一家公司,负责维护一个有5年历史的Android APP,在维护中我发现有些技巧可以帮我们更高效的实现功能的迁移,避免重复造轮子,在本文介绍的反射是其中一种方法可以以最小代价不重复代码。
前言
在平时的开发中,经常会有相同的功能在不同的类中需要实现,这时候就有可能出现重复代码,最简单的方法就是把已经实现的代码片段直接拷贝到新类中对应的位置,当然这是最low的手段,我反正是属于那种比较懒的程序员(你也是,对吧),我平时的做法是把这个功能实现代码抽取出来封装成公共方法。
Next,那么问题来了,抽取方法并不是万能的,因为有时这些代码内部完全可能会访问到当前类的成员变量或其他成员方法,当然你可能会说,把涉及的相关变量作为形参传递或直接传递一个当前类的对象不就可以了嘛。其实这样做,在很多情况下是完全可以行得通的,但是传递进来当前类的对象是无法访问到它的private私有方法的,而且有时候传递进来的变量本身就很复杂,因此必须得另辟蹊径,解决问题之道在乎变通。
我的代码维护原则
- 尽量不去改动已有代码
- 尽量做到代码只增不减
- 尽量使用已有代码实现
【注意】在我的维护过程中,前期我坚持不去删减任何已有代码(即使,人家写的很烂,也不轻易乱动),因为我要保证之前的代码可以正常运行,这是首要的条件;其次,如果人家写的好,就更应该去好好阅读源代码,揣摩人家的编程思想,求同存异,学会吸收和借鉴别人经验,才能在技术的道路上渐行渐远~唔,貌似,扯多了,OK,看下面:
我的代码维护实例
1.新需求
是这样的,leader告诉我,要在App中新增一个页面,而这个页面的功能在各个分散的其他页面均已实现,说到底是把重要的功能聚集在一起,这样用户才能用得轻松。leader把页面给我后,我看了一下,就问了下图标资源从哪里获取,leader一听就说那不是icon,而是字体Fontawesome绘制出来的效果,如果不清楚的请看我的博客一种可以代替图标的字符集Fontawesome.
呐,看下面的2张图,是不是功能相同,入口不同罢了:
2.实现方案
我通过一连串的代码跟踪,最后在MainActiviy中找到了点击事件的代码,不过话说回来,我前面的兄弟写的代码质量挺高的,嘻嘻。
继续追踪,跳到如下2个代码的具体实现方法处,发现这2个方法均是定义在MainActiviy中的private私有方法,可想而知,如果我在新增的页面(统一叫做:MineFragment,下同)即使能很轻松拿到MainActiviy的对象,但是仍然无法调用下面2个方法,当然拷贝出来放在MineFragment里自然是可行的,但这属于代码重复,应该要尽量避免的,或者直接修改2个方法的访问权限为public,但这既会改动别人写好的代码又容易导致安全问题,因此我想到了利用反射来调用MainActiviy的这2个方法,复用代码。
private void synData(final boolean showLoading) {
//...
}
...
private void showMyInfo() {
//...
}
3.向外提供公共的方法(反射实现)
在MainActivity中新增public的方法invokeMethodByName(…),内部使用反射来实现调用MainActivity中所有的方法,代码如下:
/**
* 开放此方法,目的是让在其他的类中访问到MainActivity中重要方法,复用
*/
public void invokeMethodByName(String methodName, Class<?>[] clazz, Object... args) {
try {
Method method;
if (clazz == null) {
method = MainActivity.class.getDeclaredMethod(methodName);
method.invoke(this);
} else {
method = MainActivity.class.getDeclaredMethod(methodName, clazz);
method.invoke(this, args);
}
} catch (Exception e) {
e.printStackTrace();
}
}
4.外部调用
现在, 在MineFragment中只需调用invokeMethodByName()方法就可以完成相同的功能,代码如下:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.fa_pencil:
mainActivity.invokeMethodByName("showMyInfo", null, (Object[]) null);
break;
case R.id.fa_cloud_download:
mainActivity.invokeMethodByName("synData", (new Class[]{boolean.class}), (new Object[]{true}));
break;
case R.id.fa_bell:
mainActivity.invokeMethodByName("getOnlyCallAccount", null, (Object[]) null);
break;
case R.id.fa_share_alt:
mainActivity.invokeMethodByName("showShare", null, (Object[]) null);
break;
case R.id.edit_icon:
editAvatar();
break;
case R.id.fa_align_left:
getOrderInfo();
break;
}
}
是不是很方便喔~