整洁的代码

#前言#
相信大家只要干过两三年编程,就有可能曾经被某人的代码绊倒过。如果你编程不止两三年,也有可能被这种代码拖过后退。进度延缓的程度会很严重。随着这种混乱代码的增加,团队生产力也会持续下降。
要写出整洁代码如同是绘画一般, 整洁的代码需要遵循大量的'小技巧',会培养一种代码感.这种代码感,相信有少数人可能很快会在编码中领悟, 有些人费些时间,费些才能得到; 它不仅能够让我们看到代码的优势,并且能够给予化劣势为优势的攻略.
前提是大家需要明白整洁对代码的意义何在,否则尝试去写出整洁代码就毫无意义。

#1. 取个名副其实的名字#

不管是变量名称, 方法名,类名,选个好名字有时候会花一点时间, 但日后省下来的时间应该会比花掉的时间多. 下图代码中为一个计数器的统计整型数, 它没能够具体表现变量的意义, 可能除了编码者自身或许能够理解它其中的含义,但是如果对于另外一个程序员来说,他可能还需要多花费一些时间去理解(直接断片),这个代码到底是统计或者是记录什么的数量.

int count = 0; 

另外,引用代码整洁之道中的一段代码,理解一下下来代码的目的是什么?

public List<int[]> getTham(){
    List<int[]> list1 = new ArrayList<int[]>();
    for (int [] x : theList){
        if(x[0] == 4)
            list1.add(x);
    }
    return list1;
}

大家一看代码就知道这是一个循环处理集合并且返回一个新集合的方法, 但这是最初的表面,具体要做一些什么事情呢?这代码问题不在于它的简洁度上,而是在于代码的模糊度,意思是在代码中没有明确体现的程度.深入代码查看会发现产生不少疑问,

问题列表

  1. (Q1) theList 是什么类型的东东,
  2. (Q2) x下标为零是什么意思,
  3. (Q3) 值等于4的意义什么?
  4. (Q4) 方法的返回值是什么,怎么使用.
    上列问题的答案没有出现在代码中, 可那应该就是它们该在的地方;如果不在,你可能会花费不必要的时间去寻找或者请教他人了解其中的含义。下面我们进行适当的改造,只要改为有意义的名称,代码就会得到相当程度的改进。打比方这是一个扫雷游戏中的其中一个点,盘面是名为上图中的theList的单元格列表, 那我们就先从它开始将其名称改为gameBoard. 扫雷的盘面上每个单元格都用一个简单数组标示,并且标记数组下标为零是一种状态值,而这种状态值为4标示"已标记".
public List<int []> getFlaggedCells() {
    List<int []> flaggedCells = new ArrayList<>();
    for(int []  cell : gameBoard){
         if(cell[STATUS_VALUE] == FLAGGED){
            flaggedCell.add(cell);
        }
    }
    return flaggedCells;
    
}

面向对象的语言,还可以进一步,不用int数组标示单元格,而是写一个Cell类,该类还包括一个名副其实的函数isFlagged(), 从而掩盖住了那个魔法数字。

public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<>();
    for(Cell cell : gameBoard){
         if(cell.isFlagged()){
            flaggedCell.add(cell);
        }
    }
    return flaggedCells;
}

没有对比就没有伤害...相信大家能够看出来写一个好名称是多么坚挺的事情.

  • 使用可所搜的名称

字母名称和数字常量有个问题,就是很那在一大片文字中找出来,有时候定位不准确,或者找起来相当耗时。找 MAX_TEACHER_NUM 要比在一堆内容里找数字 7 简单很多。同样字母e也不是一个便于搜索的好变量名称,在每个程序中都有可能出现。由此可见,长名称要胜于短名称,搜得到的名称胜于用自造编码代写的名称。若变量或者常量可能在代码中多处使用,则应该赋予便于搜索的名称。比较一下下列代码

    for (int i = 0 ; i < 50; i ++){
        s += (te [i] * 4) / 5;
    }
    // 和

    int realDaysPerIdealDay = 4;
    int WORK_DAYS_PER_WEEK = 5;
    int sum = 0;
    for (int i = 0; i < NUMBER_OF_TASKS; i++){
        int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
        int realTaskWeeks = ( realTaskDays /  WORK_DAYS_PER_WEEK );
        sum += realTaskWeeks;
    }
  • 添加有意义的语境

很少有名称是能够自我说明,这需要你用有良好的命名的类,函数或者名称来放置名称,给读代码的人提供一种语境。让他们更好的理解.
大家在开发过程中会需要用到变量, 假设你有firstName, lastName, street,houseNubmer, state,city, zipcode变量.把它们组合在一起,很明了的知道它构建成了一个地址。但是,假如只是在某一个方法中看到孤零零的一个state变量值,你还能够这么顺利的推断出它是某个地址的一部分吗?
其实可以添加前缀 addrFirstName, addrLastName,addrState等,以此来提供少许语境。至少,我们开发者或者看码人会明白这些变量是某个更大结构的一部分。当然更好的是创建一个Address类。值得思考.

*** 小结 ***
试着去尝试注意一些规则和命名细节,相信代码可读性会有提升。

#2. 函数(方法)#

  • 短小
    方法的第一规则是要短小,程序中每个方法都不应太长,每个方法都一目了然,每个方法都直说一件事情,而且会通过这个方法依次把你带到下一个方法。每个方法尽量控制方法的行数最长在80~100行内,因为超长的话第一可能因为你显示器受限,在代码可读性方便也会受到一定干扰。在长一点的话估计要万马奔腾了.

  • 只做一件事
    前人总结的一个经验,明确了方法的定位,方法应该做一件事。做好这件事,只做这一件事。 个人认为方法中做一件事,它的出发点是准确的,比如大家熟悉的MVC形式,从大的层面说Controller层的方法只做一件事,那就是控制转发,将接收的前端数据拼装好传递给下一层。Model层来接收数据,进行业务处理和与数据库打交道(这里其实还可以将业务处理层和数据层分开),最终将数据返回给View进行渲染。遵循MVC模式能够很好的诠释的方法的只做一件事的原则。各个部分方法做好自己分内的事情。 书上有一句话说的很好,‘’如果每个历程都让你感到深合己意,那就是整洁代码“

举例

/**
	 * 封装 RedfListByParentRsp
	 * @param timestamp
	 * @param userId
	 * @return
	 */
	public RedfListByParentRsp wrapRedfListByParentRsp (Long timestamp,Long userId) {
		
		RedfListByParentRsp rsp = new RedfListByParentRsp();
		
		List<RedfWeekRecord> redfWeekRecordList = new ArrayList<RedfWeekRecord>();
		Map<String,Object> params = new HashMap<String, Object>();
		Long studentId=BaseService.userService.selectStudentIdByUserId(userId);
		int total = BaseService.redfSendService.selectUserRedFlowerTotal(studentId); //红花的总数
		final int showWeek = 3; //显示几周
		int isMore = 0; //是否有更多
		
		String startTime;
		String endTime;
		
		Calendar nowCalendar = Calendar.getInstance();
		Date nowDate = nowCalendar.getTime();
		if(timestamp == null || timestamp <= 0){
			
			Date thisWeekSunday = DateUtil.getWeekSunday(nowDate, 0); //当前周的星期天的日期
			Date thisWeekMonday = DateUtil.getWeekMonday(nowDate, 0);  //当前周星期一的日期
			startTime = com.talkyun.apus.util.DateUtil.formatDate2Str(thisWeekSunday, com.talkyun.apus.util.DateUtil.DEFINE_YYYY_MM_DD);
			endTime = com.talkyun.apus.util.DateUtil.formatDate2Str(thisWeekMonday, com.talkyun.apus.util.DateUtil.DEFINE_YYYY_MM_DD); 
		}else{
			Calendar cal = Calendar.getInstance();
			cal.setTimeInMillis(timestamp);
			
			Date memberDateSunDay = DateUtil.getWeekSunday(cal.getTime(), 0);
			Date thisWeekSunday = DateUtil.getWeekSunday(memberDateSunDay,-1); //当前周的星期天的日期
			Date thisWeekMonday = DateUtil.getWeekMonday(memberDateSunDay, -1);  //当前周星期一的日期
			startTime = com.talkyun.apus.util.DateUtil.formatDate2Str(thisWeekSunday, com.talkyun.apus.util.DateUtil.DEFINE_YYYY_MM_DD);
			endTime = com.talkyun.apus.util.DateUtil.formatDate2Str(thisWeekMonday, com.talkyun.apus.util.DateUtil.DEFINE_YYYY_MM_DD);
		}
		
		params.put("startTime", startTime);
		params.put("endTime", endTime);
		params.put("studentId", studentId);
		
		for(int i = 1 ; i <= showWeek + 1 ; i++){
			
			//查询一周的数据
			List<Map<String, Object>> weekCountList 
				= BaseService.redfSendService.selectUserRedFlowerCountByWeek(params);
			
			if(weekCountList == null || weekCountList.size() == 0){
				weekCountList = new ArrayList<Map<String,Object>>();
			}
			
			//如果到达周循环的最后一次,并且能查询出数据则认为可以加载更多
			if((i == showWeek + 1)&&weekCountList.size()>0){
			    isMore = 1;
			}
			
			RedfWeekRecord redfWeekRecord = new RedfWeekRecord();
			String _startTime = params.get("startTime").toString();
			String _endTime = params.get("endTime").toString();
			
			Date startDate = com.talkyun.apus.util.DateUtil.StringToDate(_startTime, com.talkyun.apus.util.DateUtil.DEFINE_YYYY_MM_DD);
			Date endDate = com.talkyun.apus.util.DateUtil.StringToDate(_endTime, com.talkyun.apus.util.DateUtil.DEFINE_YYYY_MM_DD);;
			
			redfWeekRecord.setEndDate(startDate.getTime());
			redfWeekRecord.setStartDate(endDate.getTime());
			
			
			if(DateUtil.daysBetween(endDate , nowDate) > 7) //除本周外都有周榜显示
				redfWeekRecord.setIsWeekTopViewable(0);
			else 
				redfWeekRecord.setIsWeekTopViewable(1);
			
			List<RedfDayRecord> redfDayRecordList = new ArrayList<RedfDayRecord>();
			
			int weekCountTotal = 0;
			
			//迭代一周的数据并封装
			for (int j = 0; j < weekCountList.size(); j++) {
				
				Map<String,Object> countMap = weekCountList.get(j);
				Integer count = Integer.parseInt(countMap.get("count").toString());
				String time = countMap.get("time").toString();
				
				Date dayDate = com.talkyun.apus.util.DateUtil.StringToDate(time, com.talkyun.apus.util.DateUtil.DEFINE_YYYY_MM_DD);
				
				RedfDayRecord redfDayRecord = new RedfDayRecord();
				redfDayRecord.setComment(wrapRedfDayComment(count));
				redfDayRecord.setTimestamp(dayDate.getTime());
				redfDayRecord.setRedfList(wrapRedfInfoList(userId,time));
				redfDayRecord.setTotal(count);
				
				redfDayRecordList.add(redfDayRecord);
				
				weekCountTotal += count; //累加一周的统计数量
			}
			resetParamsMap(params); //重置paramsMap startTime 和 endTime 进入下一周的查询
			
//			total += weekCountTotal; //累加全部周的统计数量
			redfWeekRecord.setRedfDayList(redfDayRecordList);
			redfWeekRecord.setTotal(weekCountTotal);
			
			redfWeekRecordList.add(redfWeekRecord);
		}
		
		CommonPageContext pageContext = new CommonPageContext();
		int weekRecordListSize = redfWeekRecordList.size();
		if(redfWeekRecordList.size() > 0){
			RedfWeekRecord lastWeek = redfWeekRecordList.get(weekRecordListSize-1);
			pageContext.setTimeStamp(lastWeek.getEndDate());
		}
		
		rsp.setContext(pageContext);
		rsp.setIsMore(isMore);
		rsp.setTotal(total);
		rsp.setWeekRecordList(redfWeekRecordList);
		
		return rsp;
	}

以上这段代码有点长,相信第一次打开阅读的朋友难免会有些皱眉头,感觉是在搞事情啊!不过,我们只先淡定下来做几个简单的方法抽离和重命名操作;朋友,再用几分钟阅读以下代码,看你能够理解吗?

	public RedfListByParentRsp wrapRedfListByParentRsp(Long timestamp,Long userId){
		Map<String,Object> params = new HashMap<String, Object>();
		// 红花的总数
		int total = this.getUserRedFlowerTotal(userId, params);
		// 处理当前周期时间
		this.getCurrentWeekDate(timestamp, params);
		// 得到红花周期数据记录
		List<RedfWeekRecord> redfWeekRecordList = this.getReadFlowerWeekRecordList(params);
		// 公共数据
		CommonPageContext pageContext = this.getPageContext(redfWeekRecordList);
		
		RedfListByParentRsp rsp = new RedfListByParentRsp();
		rsp.setContext(pageContext);
		rsp.setIsMore(params.get(isMore));
		rsp.setTotal(total);
		rsp.setWeekRecordList(redfWeekRecordList);
		
		return rsp;
	}

咱们再来看一段

	/**
	 * 根据分类获取书列表
	 * 
	 * @param formParams
	 * @param request
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	@POST
	@Path("getBookListByTagClassify")
	@Produces({ MediaType.APPLICATION_JSON })
	@Consumes("application/x-www-form-urlencoded")
	public String getBookListByTagClassify(MultivaluedMap<String, String> formParams,
			@Context HttpServletRequest request) throws UnsupportedEncodingException {
		// 版本号、渠道标识、所属应用、分类id、分页相关参数

		Map<String, Object> resultMap = new HashMap<String, Object>();
		try {
			String jsonParam = request.getParameter(Constant.PARAMS);
			DbPage page = new DbPage();
			JSONObject jb = JSONObject.fromObject(jsonParam);
			Integer currentPage = 0;
			Integer showCount = 10;
			try {
				currentPage = jb.getInt("currentPage");
				showCount = jb.getInt("showCount");
			} catch (Exception e) {
				// TODO: handle exception
			}

			Map<String, Object> filter = new HashMap<String, Object>(); // BeanConverter.toMap(jb);
			if (jb.containsKey("version")) { // 分类
				String version = jb.getString("version");
				if (null != version && !"".equals(version.trim())) {
					filter.put("version", jb.getString("version"));
				}
			}

			if (jb.containsKey("channel")) { // 分类
				String channel = jb.getString("channel");
				if (null != channel && !"".equals(channel.trim())) {
					filter.put("channel", jb.getString("channel"));
				}
			}

			if (jb.containsKey("appId")) { // 分类
				String appId = jb.getString("appId");
				if (null != appId && !"".equals(appId.trim())) {
					filter.put("appId", jb.getString("appId"));
				}
			}

			if (jb.containsKey("orderType")) { // 排序规则
				String orderType = jb.getString("orderType");
				if (null != orderType && !"".equals(orderType.trim())) {
					filter.put("orderType", jb.getString("orderType"));
				}
			}

			if (jb.containsKey("ageGroupId")) { // 排序规则
				Integer ageGroupId = jb.getInt("ageGroupId");
				if (null != ageGroupId) {
					filter.put("ageGroupId", jb.getInt("ageGroupId"));
				}
			}

			if (jb.containsKey("storeId")) {
				String storeId = jb.getString("storeId");
				if (null != storeId && !"".equals(storeId.trim())) {
					filter.put("storeId", storeId);
				}
			}

			if (jb.containsKey("classifyCode")) { // 分类类型
				String classifyCode = jb.getString("classifyCode");
				if (null != classifyCode && !"".equals(classifyCode.trim())) {
					if (classifyCode.equals("book_type")) {
						filter.put("flag", "1");
						if (jb.containsKey("id")) { // 图书类型
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getString("id"));
							}
						}
					} else if (classifyCode.equals("book_theme")) {
						filter.put("flag", "2");
						if (jb.containsKey("id")) { // 图书主题
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getString("id"));
							}
						}
					} else if (classifyCode.equals("book_scene")) {
						filter.put("flag", "3");
						if (jb.containsKey("id")) { // 阅读场景
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getString("id"));
							}
						}
					} else if (classifyCode.equals("book_brand")) {
						filter.put("flag", "4");
						if (jb.containsKey("id")) { // 图书品牌
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getString("id"));
							}
						}
					} else if (classifyCode.equals("age_group")) {
						filter.put("flag", "5");
						if (jb.containsKey("id")) { // 年龄层
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getString("id"));
							}
						}
					} else if (classifyCode.equals("language")) {
						filter.put("flag", "7");
						if (jb.containsKey("id")) { // 语言
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getString("id"));
							}
						}
					} else if (classifyCode.equals("territory")) {
						filter.put("flag", "6");
						if (jb.containsKey("id")) { // 地域
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getInt("id"));
							}
						}
					} else if (classifyCode.equals("format")) {
						filter.put("flag", "8");
						if (jb.containsKey("id")) { // 格式
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getInt("id"));
							}
						}
					} else if (classifyCode.equals("charge_type")) {
						filter.put("flag", "9");
						if (jb.containsKey("id")) { // 计费试式
							String id = jb.getString("id");
							if (null != id && !"".equals(id.trim())) {
								filter.put("tagClassifyId", jb.getInt("id"));
							}
						}
					}
				}
			}

			String channel = "";
			Integer version = -1;
			Integer appId = -1;
			Integer client = -1;

			if (jb.containsKey("channel")) {
				channel = jb.getString("channel");
			}

			if (jb.containsKey("version")) {
				version = jb.getInt("version");
			}

			if (jb.containsKey("appId")) {
				appId = jb.getInt("appId");
			}

			if (jb.containsKey("client")) {
				client = jb.getInt("client");
			}

			Object storeId = filter.get("storeId");
			Object tagClassifyId = filter.get("tagClassifyId");

			if (storeId != null) {
				storeClassifyService.updateClickTotal(Long.valueOf(storeId.toString()));
			}

			if (tagClassifyId != null) {
				bookTagClassifyService.updateClickTotal(Long.valueOf(tagClassifyId.toString()));
			}

			page.setCurrentPage(currentPage);
			page.setShowCount(showCount);
			filter.put("page", page);
			// 假如ID为1,2,3的时候
			if (filter.get("storeId") != null && Integer.parseInt(filter.get("storeId").toString()) == 28) {
				// 下载排行
				filter.put("flag", 1);
				page = bookService.findRank(filter);
			} else if (filter.get("storeId") != null && Integer.parseInt(filter.get("storeId").toString()) == 29) {
				// 最新上架
				filter.put("flag", 2);
				page = bookService.findRank(filter);
			} else {
				page = bookService.getBookListByTagClassify(filter);
			}
			List<Book> list = (List<Book>) page.getResult();

			for (Book b : list) {
				if (b.getInitialPrice() != null && b.getInitialPrice().intValue() > 0) { // 非免费书
					b.setFullPackPath(null);// 去除完整包路径,防止恶意获取内容
				}
			}

			ContentFilter<List<Book>, Book> contentFilter = new ContentFilter<List<Book>, Book>();
			String copyrightKey = contentFilter.buildKey(String.valueOf(client), channel, String.valueOf(version),
					String.valueOf(appId));
			String versionKey = contentFilter.buildKey(String.valueOf(client), null,
					client == 1 ? String.valueOf(2010) : String.valueOf(2020), String.valueOf(appId));
			List<Book> filterList = contentFilter.filter(list, Book.class, copyrightKey)
					.filter(contentFilter.getResult(), Book.class, versionKey, version).getResult();

			// filterList 执行过滤后有可能和page查询的资源不一致导致分页不正常,所以重新设置page的总数,从而重新计算总页数
			// (list.size() - filterList.size()) = 大于等于0的正整数

			page.setTotalResult(page.getTotalResult() - (list.size() - filterList.size()));

			resultMap.put(Constant.RES_CODE, 0);
			Map<String, Object> listMap = new HashMap<String, Object>();
			listMap.put(Constant.RESULT_TOTALPAGE, page.getTotalPage());
			listMap.put(Constant.RESULT_TOTALRESULT, page.getTotalResult());

			if (currentPage > page.getTotalPage()) {
				List<BookCustClassify> list2 = new ArrayList<BookCustClassify>();
				listMap.put(Constant.RESULT_LIST, list2);
			} else {
				listMap.put(Constant.RESULT_LIST, filterList);
			}
			resultMap.put(Constant.RESULT, listMap);
		} catch (Exception e) {
			logger.error("error:", e);
			e.printStackTrace();
			resultMap.put(Constant.RES_CODE, -1);
		}

		String jsonStr = JsonUtil.map2json(resultMap);
		logger.debug(".........:" + jsonStr);

		return jsonStr;
	}

对上面代码进行修正一下,大家来看看

	public String getBookListByTagClassify(MultivaluedMap<String, String> formParams,
			@Context HttpServletRequest request) throws UnsupportedEncodingException {
		// 版本号、渠道标识、所属应用、分类id、分页相关参数

		Map<String, Object> resultMap = new HashMap<String, Object>();
		try {
			String jsonParam = request.getParameter(Constant.PARAMS);
			JSONObject jb = JSONObject.fromObject(jsonParam);
			Integer currentPage = 0;
			Integer showCount = 10;
			try {
				currentPage = jb.getInt("currentPage");
				showCount = jb.getInt("showCount");
			} catch (Exception e) {
				logger.error("error:" , e);
			}

			Map<String, Object> tagClassifyParams = getTagClassifyParams(jb);

			updateClickTotal(tagClassifyParams);

			DbPage page = getBookListByStoreId(tagClassifyParams, currentPage, showCount);

			List<Book> list = (List<Book>) page.getResult();

			List<Book> filterList = getContentFilterResult(jb, list);

			// filterList 执行过滤后有可能和page查询的资源不一致导致分页不正常,所以重新设置page的总数,从而重新计算总页数
			// (list.size() - filterList.size()) = 大于等于0的正整数
			page.setTotalResult(page.getTotalResult() - (list.size() - filterList.size()));

			Map<String, Object> listMap = getResultMap(filterList, page, currentPage);
			resultMap.put(Constant.RES_CODE, 0);
			resultMap.put(Constant.RESULT, listMap);
		} catch (Exception e) {
			logger.error("error:", e);
			e.printStackTrace();
			resultMap.put(Constant.RES_CODE, -1);
		}

		String jsonStr = JsonUtil.map2json(resultMap);

		return jsonStr;
	}

再来看一个简化的代码案例。

public void pay (){
    for (Employee e : employees) {
        if (e.isPayday()) {
            Money pay = e.calculatePay();
            e.deliverPay(pay);
        }
    }
}

上面这段代码做了三件事,遍历所有员工,检查是否应该给他发工资,然后支付薪水。代码可以写的更好,如:

    public void pay(){
       for (Employee e : employees) {
            payIfNecessary(e);
        }
    }

    private void payIfNecessary(Employee e) {
        if (e.isPayday())
            calculateAndDeliverPay(e);
    }

    private void calculateAndDeliverPay(Employee e){
        Money pay = e.calculatePay();
        e.deliverPay(pay);
    }

上面每个方法都只做了一件事。

  • 函数(方法)参数
    最理想的参数数量是零(零参数方法),其次是一个,再次是两个,之后应该避免多参数的方法出现,当然除非你有足够的理由。从测试的角度再来看看,参数有时候叫人为难,要编写保证参数的各种组合运行正常的测试用例,是有些困难或耗时的一件事情。如果没有参数,就会简单很多。总之参数不易太多,如果方法看来需要两个,三个,或者更多的参数才能维持,就说明其中一些参数应该对其进行封装了。
    Circle makeCircle(double x, double y, double radius);
    Circle makeCircle(Point center, double radius);
  • 如何写出这样的方法

    写代码和写别的东西很像。在写小说或者写歌词的时候,应该是先想什么就些什么,然后再打磨它。初稿也许是非常粗糙的,无序的,但你可以之后斟酌推敲,直到达到你心目中的样子。代码也是一样,分解方法,修改名称,消除重复等等; 相信很多人并不是一开始就按照规则写方法,我想没人能百分百做得到.

*** 小结 ***
遵循这些规则,相信方法就会短小,有个好的名字,而且被很好的归置。这样能将你编写的方法干净利落地拼装到一起,形成一种清晰明了的语言,大家都能够看懂。

#3. 注释#
注释的恰当用法是弥补我们在用代码无法表达用意的方法。但是换句话来说,真实的东西只在一个地方,那就是我们写的代码里。只有代码能够忠实的告诉你它做的事情。那是唯一真正准确的信息来源。所以,有时也需要注释,我们也应该多花心思尽量减少它的量。

  • 用代码来阐述
    有时候,代码本身不足以解释其行为,
// 检查员工是否合格的全部福利条件
if ((employee.flags & HOURLY_FLAG )  && (employee.age > 65))

还是换一个

if (employee.isEligibleForFullBenefits())

只要向上几秒钟,就能用代码解释你的带部分意图,很多时候,只需要创建一个描述与注释所言同一事物的方法即可。

  • 注释不能美化糟糕的代码
    带有少量注释的整洁而有表达力的代码,要比带有大量注释的零碎而复杂的代码像样的多。与其花时间编写解释你搞出的粗糙的代码的注释,不如花时间清洁那堆糟糕的代码。

  • 好注释
    有些注释是必须的,也是有利的。不过要明确知道,唯一真正好的注释是你想办法不去写的注释。1: 有时候公司代码规范要求编写与法律有关的注释,比如版权及著作权声明必须放置在每个源文件开头。2:有时由于警告或警示其他人员会出现某种后果的情况注释也是非常有用的。

  • TODO注释
    有时候,用//TODO 的形式在源代码中放置要做的工作列表。TODO应该是一种程序员认为应该做,但是由于某些原因目前还没法做的工作。它的目的可能是要求提醒删除某个不必要的功能特性,或者要求其他人注意某个问题而存在的。现在很多IDE提供了方式来定位所有的TODO注释,有TODO 的存在就是要解决问题,定期查看,删除不在需要的。

  • 注释掉的代码
    其他人一般不敢删除注释掉的代码。他们会想,可能代码依然放在那儿,一定有其原因,而且这段代码很重要,不能够删除。这样久而久之,程序中代码堆积的越来也多。

 /**删除user和 account**/
		User user = BaseService.userService.selectByPrimaryKey(userId);
		if(user == null || user.getRoleId() != Constants.ROLE_PARENTS) {
			setResponseStatus(response, null, RetCode.Error, "参数错误");
			return response;
		}
		user.setUserId(userId);
		user.setStatus(100);
		BaseService.userService.updateByPrimaryKeySelective(user);
/*		Account account = new Account();
		account.setId(user.getAccountId());
		account.setStatus(100);
		BaseService.accountService.updateByPrimaryKeySelective(account);*/
		/**删除学生**/
		if(user.getStudentId()!= null){
			Student student = new Student();
			student.setId(user.getStudentId());
			student.setStatus(100);
			BaseService.studentService.updateByPrimaryKeySelective(student);
			/**删除班级关系**/
			StudentClass studentClass =  BaseService.studentClassService.selectStudentClassByStudentId(user.getStudentId());
			if(studentClass != null) {
				studentClass.setState(1);
				BaseService.studentClassService.updateByPrimaryKeySelective(studentClass);
			}
		}

还有类似单行的注释信息

	public LoginExt wrapUserList2LoginExt(List<User> userList, Account account) {
		List<UserInfoV1> userInfoV1List = new ArrayList<UserInfoV1>();
		for (int i = 0; i < userList.size(); i++) {

			User user = userList.get(i);
			int roleId = user.getRoleId().intValue();

			UserBaseInfo userBaseInfo = wrapUser2UserBaseInfo(user);

			UserInfoV1 userInfoV1 = new UserInfoV1();
			// userInfoV1.setFamilyId(-1);//暂定
			Long userId = user.getUserId();
			School school = null;
			List<ClassInfo> classInfoList = new ArrayList<ClassInfo>();
			if (user.getSchoolId() != null) {
				school = BaseService.schoolService.selectByPrimaryKey(user.getSchoolId());
			}
			if (roleId == Constants.ROLE_PARENTS.intValue()) {
//				school = BaseService.schoolService.selectStudentSchoolByStudentId(user.getStudentId());
				ClassInfo classInfo = BaseService.classService.selectStudentClassInfosByStudentId(user.getStudentId());
				if (classInfo != null) {
					classInfoList.add(classInfo);
				}
				userBaseInfo.setRole(Role.Parent);// 客户端使用的角色为 parent
			} else if (roleId == Role.Teacher.getValue()) {
//				school = BaseService.schoolService.selectTeacherSchoolByUserId(userId);
				classInfoList = BaseService.classService.selectTeacherClassInfosByUserId(userId);
			} else if (roleId == Role.SchoolManager.getValue()) {
//				school = BaseService.schoolService.selectLeaderSchoolByUserId(userId);
				classInfoList = BaseService.classService.selectLeaderClassInfosByUserId(userId);
			}

			userInfoV1.setClassNames(wrapClassInfoList2String(classInfoList));
			userInfoV1.setSchoolId(school == null || school.getId() == 0 ? -1 : school.getId());
			userInfoV1.setSchoolName(school == null || school.getName() == null ? StringUtils.EMPTY : school.getName());

			// Student student =
			// BaseService.studentService.selectByPrimaryKey(user.getStudentId());

			userInfoV1.setUser(userBaseInfo);
			Long StudentId = user.getStudentId();

	}

注释掉的代码可能有用,不过我们现在已经有了SVN,GIT等源代码控制系统,这些工具能帮助我们记住不要的代码。我们自己无需要在用注释来标记,我觉得它们删除即可,一般丢不了。

4.格式

  • 团队规则
    相信每个程序员都有自己喜欢的格式规则,但是如果在一个团队中工作,就应该是团队说了算。不然无规矩不成方圆。一组开发者应该认同一种格式风格,成员都应该采用哪种风格。最简单的方式用你的IDE导入format style 模板。

总结

没有绝对正确的方式,只有最适合的方式。 自己有了衡量的标准,相信你会走的更远。

转载于:https://my.oschina.net/summerfornight/blog/817237

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值