1、背景说明
虽然不止一次说明代码规范的问题,但是在协同作战的时候,代码规范这问题总是被遗忘,希望大家能够引以为戒。协同作战的时候,如果不能遵守代码规范,很容易出现各种问题,而且不知道从何处入手解决,消耗时间成本。
实际上,对于代码规范这种问题,应该有人专门来审核代码的,但是公司小,项目也不大,参与的人员也不多的情况下,绝大多数公司都没有这种机制,尤其是在移动端编程上面 ,这就需要开发人员自我监督,避免出现不必要的麻烦。
本文就举例说明,在近期笔者所带领的团队协作中,出现的一些问题。
2、命名
2.1 控件名称
笔者在合并代码的过程中,看到如下的一段代码,大家注意看看id命名:
<TextView
android:id="@+id/zongnumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="6dp"/>
这种id的命名方式,是不被认可的,程序会报错,但是不符合我们约定俗成的规范,建议的规范是:模块名_控件简称_功能名
2.2 实体名称
看到一个实体类的命名为:
Part_3_JsonBean
看到这个,我也是醉了。首先,类名要使用驼峰标识,中间不要出现下划线;其次,除了to可以用2,for可以用4表示,一律不允许使用数字在类中表示;第三,part加上数字这种命名没有任何的实际标识意义,看到这个类也很茫然,尽量不要使用。
jinriremen_gridview.xml
这样的xml文件命名方式,也是不符合规范的,看半天,才意识到这说的是“今日热门”,凡事有直接英文翻译的,简单的单词,统统使用英文命名,today,hot这都很简单呀,单词之间全部用下划线分开,不然读起来很困难。
getExit();
这个是一个方法的命名,实际表示的意义,是退出程序。好吧,set和get方法,只能用在赋值和获取值这种方法上,其他方法,一律不要使用!!!其次,这种意思是说调用何种方法的方法名,或者使用go,或者使用on,不要使用get。
3 代码要简洁
代码要简介,意思是说,能够一句话写完,就不要分开成两句话,那么我们看看,如下两个例子:
3.1 不够简洁
这里提供一段代码,如下所示:
Intent intent = new Intent();
intent.putExtra("detail", detail.getText().toString());
intent.setClass(mActivity, NewestActivity.class);
startActivity(intent);
我们再看看,这段代码如何简化:
Intent intent = new Intent(getActivity(), NewestActivity.class);
intent.putExtra("detail", detail.getText().toString());
startActivity(intent);
从API文档中,很明显得知这个方法有构造方法,那么尽量优先使用构造方法,这样出错的可能性更小。这是规范性问题。
3.2 乱用简洁
有时候,程序员误以为,简洁就是所有代码都写在一行,所有的同类项都可以合并,这是不对的,我们举例如下:
public static class ViewHolder {
public ImageView remen_gallery_img;
public TextView title, zongnumber, canyu, shenyu, detail;
public ProgressBar progressBar;
}
这段代码中,我们的程序员将TextView的多个控件写在一行上,这是不科学的。每一个控件都表示的不同含义,严格来说,字段都需要添加doc注释。所以,除了表示同一个含义的字段,可以使用集合或者数组表示之外,其它的都需要单独声明。
4、字段注释
这里就不过多解释了,上面已经说了,严格来说需要使用doc注释。先上一个反例:
public class JsonBean {
private String already = null; // 以参与人数
private String thumb = null;
private String title = null;
private String detail = null;
private String total = null;// 总参与人数
private String remaining = null;// 剩余人数
private String issue = null;// 期数
public JsonBean() {
// TODO Auto-generated constructor stub
}
}
这种在后面添加注释,不能说错,但是不那么严格。这里提供的是一个实体对象,那么这个对象除了字段之外,只有get和set方法了,我们在外部调用的时候,需要将鼠标移动在这个字段上,就知道字段表示什么含义,这里,给出一个规范:
public class UserBean {
/** 参与次数 */
private int count;
/** 用户名 */
private String username;
/** 开奖时间 */
private String publish_time;
/** 用户ID */
private String user_id;
/** 幸运号码 */
private String luck_num;
/** ip地址 */
private String ip;
/** 头像 */
private String head_picture;
/** 抢单时间 */
private String create_time;
public UserBean() {
// TODO Auto-generated constructor stub
}
}
最后说明一点,字段前面的修饰符,一律使用private,这里是封装的特性体现。其次,一定要添加默认的构造方法,不能只写一个有参数构造方法,编译器如果识别到有参构造方法,不会再提供默认构造方法。有很多时候,我们传递参数的时候,是允许缺省的,所以一定要提供默认构造方法。
5、方法模块化
一个方法里面,尽可能只做一件事情,不要同时执行了很多不同的事情,尤其是不要再一个方法,嵌套多层的网络请求,案例如下:
/**
* 请求json数据
* **/
private void setJson(){
new Thread(new Runnable() {
@Override
public void run() {
try {
String json = HttpRequestUtils.doGet(PublicUtils.EG_URL + "/index");
JSONObject response = new JSONObject(json);
String msg = response.optString("msg");
JSONObject object = new JSONObject(msg);
String part_1 = object.optString("part_1");
String part_2 = object.optString("part_2");
String part_3 = object.optString("part_3");
JSONArray array2 = new JSONArray(part_1);
JSONArray array3 = new JSONArray(part_2);
JSONArray array4 = new JSONArray(part_3);
lists1 = new ArrayList<Part_1_JsonBean>();
lists2 = new ArrayList<Part_2_JsonBean>();
lists3 = new ArrayList<Part_3_JsonBean>();
/** 解析part_1数据 **/
for (int j = 0; j < array2.length(); j++) {
// 代码省略
}
Message message = new Message();
message.what = 200;
handler.sendMessage(message);
/** 解析part_2数据 **/
for (int j = 0; j < array3.length(); j++) {
// 代码省略
}
Message message2 = new Message();
message2.what = 201;
handler.sendMessage(message2);
/** 解析part_3数据 **/
for (int j = 0; j < array4.length(); j++) {
// 代码省略
}
Message message3 = new Message();
message3.what = 202;
handler.sendMessage(message3);
} catch (JSONException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
这段代码中,是说的设置json字符串,但是先要开启网络请求,网络请求也是非常耗时的,应该单独开线程。等得到返回的json字符串之后,再用一个线程处理设置数据,而这个方法就将两者合并在一起了,这样的编程习惯就不是那么好了。
修正案例如下:
/** handler消息处理机制 */
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ConstantValues.GET_NET_SUCCEED:
jsonString = (String) msg.obj;
if (jsonString != null && !jsonString.equals("")) {
new DelJsonThread().start();
} else {
Logger.ii(TAG, "There is no data");
}
break;
case ConstantValues.GET_NET_FAILED:
// 获取网络失败
break;
case ConstantValues.DEL_RESULT_SUCCEED:
listView.setAdapter(new HistoryListAdaptet(list,
HistoryActivity.this));
break;
case ConstantValues.DEL_RESULT_FAILED:
// 字符串处理失败
Logger.ii(TAG, "del result failed");
break;
default:
break;
}
}
};
if (!StringUtils.isEmpty(url)) {
HttpGetThread httpGetThread = new HttpGetThread(handler, url);
new Thread(httpGetThread).start();
}
网络请求代码,和处理返回json的线程类DelJsonThread()这里就不在提供了。
6、逻辑处理
在代码规范中,逻辑处理也是比较难遵守的,因为在测试的时候很难出现。这里的逻辑问题,主要是空指针这样的问题,主要有以下几个方面:
6.1 异步加载图片
这里使用的是开源组件,如果url为空的时候,会在后台提示异常。
ImageLoader.getInstance().displayImage(url,holder.remen_gallery_img, options);
if (!StringUtils.isEmpty(url)) {
ImageLoader.getInstance().displayImage(url,holder.remen_gallery_img, options);
}
6.2 从上一个界面获取数据
我们从一个Activity跳转到另一个Activity的时候,会使用如下方法获取数据,这里都需要做判断:
String url = getIntent().getStringExtra("history");
if (!StringUtils.isEmpty(url)) {
HttpGetThread httpGetThread = new HttpGetThread(handler, url);
new Thread(httpGetThread).start();
}else{
handler.sendEmptyMessage(ConstantValues.THE_URL_ERROR);
}
6.3 处理JSON字符串
当我们向服务器发出请求,服务器返回一段字符串,这个时候,我们需要先判断一下数据是否正确:
if (jsonString != null && !jsonString.equals("")) {
new DelJsonThread().start();
} else {
Logger.ii(TAG, "There is no data");
}
空指针异常还有许多情况,逻辑判断也有其它很多情况,比如,有了if,最好要添加else;在try-catch中,不要直接返回总异常,而要得到网络请求异常,数据错误异常,端口连接异常等等情况,不要偷懒,不然程序运行的错误要找很久才会发现。
7、总结
程序开发的规范,是一个很考验程序员功力的问题,希望大家一开始就养成良好的习惯。
以上,是笔者在公司做指导开发的时候,发现的一些问题,从中截取了一些代码。这里只是提供一些说明,并不提供完整可运行的程序,希望读者自己注意。