首先看看以下三张图,在没有代码的情况下能猜猜各是什么开发语言。
---------------------------------------------------------------
---------------------------------------------------------------
---------------------------------------------------------------
第一张图是CSS,可以看到清晰的选择器和属性键值对。
第二张图是HTML,可以看到清晰的head和body定义。
第三张图是Java,可以看到头部imports,类定义,成员变量,构造函数以及其他方法。
图片来自:https://schneide.wordpress.com/2015/04/26/the-typography-of-source-code/
各个语言都有自己的规范,开发所用的各种IDE也都内置了代码格式化工具,比如Eclipse对Java和JavaScript代码的标准格式化定义:
也可以通过 Checkstyle 检查Java代码的规范性。
这里罗列几个最有争议的。
(1)缩进
使用Tab 还是空格?2个空格还是4个空格?具体关于这个网上的争论太多了,但是需要特别注意的是像Python和CoffeeScript等是通过缩进来控制代码逻辑。
https://softwareengineering.stackexchange.com/questions/57/tabs-versus-spaces-what-is-the-proper-indentation-character-for-everything-in-e
https://medium.com/@hoffa/400-000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
(2)换行
大括号是否应该换行?
if (you.hasAnswer()) { you.postAnswer(); } else { you.doSomething(); }
if (you.hasAnswer()) { you.postAnswer(); } else { you.doSomething(); }
if (you.hasAnswer()) you.postAnswer(); else you.doSomething();
https://softwareengineering.stackexchange.com/questions/2715/should-curly-braces-appear-on-their-own-line
大括号是否应该省略?
for (int i = 0; i < size; i++) { a += b; }
for (int i = 0; i < size; i++) a += b;
for (int i = 0; i < size; i++) a += b;
https://stackoverflow.com/questions/8020228/is-it-ok-if-i-omit-curly-braces-in-java
(3)对齐
赋值等号对齐
int a_variable = 1; int another_variable = 2; int yet_another_variable = 3;
注释对齐
// whole line comment int linePointer; // inline comment BufferredReader br = new BufferredReader(); // inline comment File f = new File(); // inline comment
参数对齐
int some_call_result = some_object.a_method( with, quite_long, list_of, parameters );
链式方法对齐
protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/resources/**", "/signup", "/about").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") .anyRequest().authenticated() .and() .formLogin(); }
(4)空行
合理的利用空行来区分代码块。
/** * Initialize this servlet. Most of the processing has been factored * into support methods so that you can override particular functionality * at a fairly granular level. * * @throws ServletException if we cannot configure ourselves correctly */ public void init() throws ServletException { final String configPrefix = "config/"; final int configPrefixLength = configPrefix.length() - 1; // Wraps the entire initialization in a try/catch to better handle // unexpected exceptions and errors to provide better feedback // to the developer try { initInternal(); initOther(); initServlet(); initChain(); getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this); initModuleConfigFactory(); // Initialize modules as needed ModuleConfig moduleConfig = initModuleConfig("", config); initModuleMessageResources(moduleConfig); initModulePlugIns(moduleConfig); initModuleFormBeans(moduleConfig); initModuleForwards(moduleConfig); initModuleExceptionConfigs(moduleConfig); initModuleActions(moduleConfig); moduleConfig.freeze(); Enumeration names = getServletConfig().getInitParameterNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); if (!name.startsWith(configPrefix)) { continue; } String prefix = name.substring(configPrefixLength); moduleConfig = initModuleConfig(prefix, getServletConfig().getInitParameter(name)); initModuleMessageResources(moduleConfig); initModulePlugIns(moduleConfig); initModuleFormBeans(moduleConfig); initModuleForwards(moduleConfig); initModuleExceptionConfigs(moduleConfig); initModuleActions(moduleConfig); moduleConfig.freeze(); } this.initModulePrefixes(this.getServletContext()); this.destroyConfigDigester(); } catch (UnavailableException ex) { throw ex; } catch (Throwable t) { // The follow error message is not retrieved from internal message // resources as they may not have been able to have been // initialized log.error("Unable to initialize Struts ActionServlet due to an " + "unexpected exception or error thrown, so marking the " + "servlet as unavailable. Most likely, this is due to an " + "incorrect or missing library dependency.", t); throw new UnavailableException(t.getMessage()); } }
(5)命名
违反命名规范的例子有很多,这里列2个:
非驼峰命名
package com.test.EC_shop; public class db_manager { private String connection_url; public void Init() { // ... } }
连续编号
public class UC8010 extends CMN0101 { private String FLD0001; public int MTD0001() { // ... } } publi class UC8020 extends CMN0101 { private String FLD0001; public int MTD0001() { // ... } }
再列举一些代码书写上的其他Style。
(1)多层嵌套 还是 中途退出?
public String getInsuranceName(Employee employee) { if (employee != null) { Car car = employee.getCar(); if (car != null) { Insurance insurance = car.getInsurance(); if (insurance != null) { return insurance.getName(); } } } return "UNKNOWN"; }
public String getInsuranceName(Employee employee) { if (employee == null) { return "UNKNOWN"; } Car car = employee.getCar(); if (car == null) { return "UNKNOWN"; } Insurance insurance = car.getInsurance(); if (insurance == null) { return "UNKNOWN"; } return insurance.getName(); }
(2)常量放在左侧还是右侧?
if (currentValue == 5) { // do work } if (5 == currentValue) { // do work }
if (obj == null) { } if (null == obj) { } if (obj = null) { // 赋值运算、NPE异常 } if (null = obj) { // 编译错误 }
https://stackoverflow.com/questions/2369226/object-null-or-null-object
(3)定义接口类型还是实现类型?
List<Integer> sampleList = new ArrayList<Integer>();
ArrayList<Integer> sampleList = new ArrayList<Integer>();
(4)标准for循环还是增强for循环?
for (int i = 0; i < peopleList.size(); i++) { People p = peopleList.get(i); // .. }
for (People p : peopleList) { // ... }
(5)变量是否需要先定义为null?
Object localVariableObj2 = null; localVariableObj2 = new Object();
Object localVariableObj2 = new Object();
*** 成员变量有默认值不需要赋值null,本地变量没有默认值需要赋值null。
(6)通过class还是interface定义全局常量?
public final class Constants { public static final int PI = 3.14; }
public interface Constants { int PI = 3.14; }
还有这么定义的:
public interface Constants { public static final String TOAST = "toast"; }
这些都是常见的,还有很多。