1.
Result result=new Result();
result.setCode(201);
result.setStr(re);
result.setMessage("获取id成功");
建议:对常用的功能,可以:新增Constructor,把4行code用1行搞定。
Result result=new Result(code, str, msg);
2.
StringBuffer stringBuffer=new StringBuffer();
String[] formatStr=snFormatStr.split(splitChar);
for(String inStr:formatStr){
//判断以str开头,不分大小写
if(inStr.matches("^[Ss][Tt][Rr].*")){
stringBuffer.append(processStr(inStr));
建议:StringBuffer改成StringBuilder。
倒数第二行用commons-lang3的,既能避免硬编码,又避免了正则表达式。
StringUtils.startsWithIgnoreCase(CharSequence, CharSequence)
3.
private String processDate(String str) throws Exception{
String[] strings=str.split(innerChar);
String def="yyyyMMdd";
建议:凡是属于 “无状态的” “通用的”功能,可以放在Util.java里。
如果确实需要硬编码,放在Util.java里,让它们只永远出现一次。
4.
String re="";
……
if(ar.length>1&&add!=""){
建议:org.apache.commons.lang3.StringUtils.EMPTY
重用常量,不要自己新创建。
5.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Attempting to resolve a principal...");
建议:既然用了slf4j,里面就封装了判断log level的功能。
LOGGER.isDebugEnabled()是多余的。
6.
if (attributes == null) {
return null;
……
if (itemNo.length()!=3) {
throw new RuntimeException("ItemCode has exceed 3 bits !");
建议:
jdk,throw new IllegalArgumentException(...);
jdk,throw new IllegalStateException(...);
org.springframework.util.Assert.isTrue(boolean, String)
org.springframework.util.Assert.state(boolean, String)
7.
if (null != sos) {
try {
sos.close();
} catch (IOException e) {
LOGGER.error("handleRequest 关闭流出现异常 ! ",e);
}
}
建议:
org.apache.commons.io.IOUtils.closeQuietly(OutputStream)
org.apache.commons.io.IOUtils.closeQuietly(Writer)
8.
public void setApplicationContext(final ApplicationContext applicationContext) {
super.setApplicationContext(applicationContext);
this.applicationContext = applicationContext;
}
建议:既然父类已经有了ApplicationContext,子类的就是无用的,可以删除。
9.
public class ImageVaditeAuthenticationViaFormAction
if (this.credentialsBinder != null && this.credentialsBinder.supports(credentials.getClass())) {
this.credentialsBinder.bind(request, credentials);
}
建议:Web层里,传递给Service层的东东,不应该有Servlet API。
10.
UserCacheVO vo = new UserCacheVO();
vo.setLoginIP(request.getRemoteAddr());
vo.setLoginTime(DateUtil.DateTimeToString(new Date()));
vo.setResourceNo(resourceNo);
vo.setUserName(loginId);
vo.setUserSymbol(userSymbol);
建议:做成UserCacheVO vo = new UserCacheVO(w,x,y,z);
有人说:对于多个参数是相同类型,比如都是string,采用构造方法构造比较容易出现调用时
参数错位的错误,而且也不太容易发现
解决方式:这样定义Constructor,每5个参数放在一行,
任何IDE的排版(都是一行有80/120/160个字符),都会保持整齐队形了。
11.
建议:合并。每个package里,有几个几十个类是正常的。
com.gy.prvg.acl.constant里的多个常量类,合并为一个。Enum,也做在常量类里面。
(2014/ 09/ 22增补)目前公司里还有个类似的状况,工程师们喜欢狂建项目——
只有几个类的十几个类的,都能做个单独的项目出来。
建议:一个工程有了2000--4000个类,可以考虑拆分。几百个类的,先保持在一起。
好处:便于开发,便于查找,便于检错,便于调试,便于维护,便于测试。
12.
public static final List<AccountType> AllTypes() {
List<AccountType> types = new ArrayList<AccountType>();
for (AccountType accountType : AccountType.values()) {
types.add(accountType);
}
return types;
}
建议:List<TimeUnit> list = java.util.Arrays.asList(TimeUnit.values()); 一句话搞定。
13.
@Override
public String toString() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("loginId", loginId);
map.put("resourceNo", resourceNo);
map.put("deptNo", deptNo);
map.put("userName", userName);
map.put("userCode", userCode);
map.put("cardId", cardId);
map.put("phone", phone);
map.put("email", email);
map.put("status", status);
map.put("roleType", roleType);
map.put("corpName", getCorpName());
map.put("deptNo", getDeptName());
return JSON.toJSONString(map);
建议:
可以用JSON.toJSONString(this);一句搞定。或者加上@JsonIgnore能屏蔽些field。
搞json格式,全部项目应该用统一的jar。推荐:fastjson。
14.
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggingEvent;
public class AclLogFileAppender extends FileAppender
建议:org.apache.log4j.RollingFileAppender应该足够用了,不用自建class。
15.
public enum RoleType {
PlatAdmin("平台管理员"),
CorpAdmin("公司管理员"),
Normal("普通角色");
if ("财务视图".equals(view.getViewName())) {
view.setViewType("Finance");
}
if ("管理视图".equals(view.getViewName())) {
view.setViewType("Manage");
}
if ("参数视图".equals(view.getViewName())) {
view.setViewType("Param");
建议:用ASCII表里的英文字母或数字。
16.
public interface ILoginService {
void loadPrivilegeItemList(String resourceNo, String loginId, HttpServletRequest request);
HttpResult logon(HttpServletRequest request);
建议:
团长能够指挥士兵,士兵不能指挥团长。
上层能调用下层,下层不能调用上层。
Service层里,不应该有Web层api。
17.
if (data.get("uri").indexOf(action.getItemContent())>=0) {
建议:java.lang.String.contains(CharSequence)
18.
int len = roleCode.length() - 3;
int maxNo = Integer.valueOf(roleCode.substring(len));
String leafNo = String.valueOf(maxNo + 1);
leafNo = (leafNo.length() == 3) ? leafNo : (leafNo.length() == 2 ? "0" + leafNo : "00" + leafNo);
code = roleCode.substring(0, len) + leafNo;
stringBuilder.append(LoUtils.fill0InStr(calcDebitCount+"", 3, 0));
建议:org.apache.commons.lang3
StringUtils. leftPad(java.lang.String str, int size, char padChar)
StringUtils. leftPad(java.lang.String str, int size, java.lang.String padStr)
建议:
calcDebitCount+"",这种用法在不同的jdk和不同的编程语言之间都可能有毛病。例如:
assertEquals("1.23456789E7", 12345678.90 + "");
assertEquals("1.23456789E7", Double.toString(12345678.90));
assertEquals("1.23456789E7", String .valueOf(12345678.90));
这3个都变味了。所以,使用这些功能之前,要确保正确性。
19.
try {
……
} catch (SystemException se){
LOGGER.error(" Finding listCorporation is error !",se);
throw new SystemException(se.getErrorCode(), se.getMessage());
} catch (Exception e) {
LOGGER.error(" Finding listCorporation is error !",e);
throw new SystemException(ErrorCode.ERROR_9004,"查询公司出现异常!", e);
每个项目里的每层的每个类里,都有这些catch,有实际意义吗?
白白的增加了几千几万行code。
建议:绝大多数情况,不需要catch。public void someMethod() throws Exception是最简洁的。
只是在必要之处,例如:返回给页面之前,才做catch。
附:java的checked exception是个设计错误。
按照现代的程序理论:在任何地方,catch都是可有可无的,不应该强迫搞catch。
Java5以前的Runnable run(),就是强迫catch { },让开发者感到臃肿。
Java5+的Callable,V call() throws Exception; catch { } 就是可有可无的,很清爽。
20.
userRole.setRoleName(URLDecoder.decode(userRole.getRoleName(), "UTF-8"));
建议:
org.apache.commons.lang3.CharEncoding.UTF_8
凡是可能有编码毛病之处,用POST方式,
把org.springframework.web.filter.CharacterEncodingFilter当做过滤器,就实现了统管,
就不用在多处搞多个URLDecoder.decode()了。
21.
int index = StringUtil.isEmpty(pageLeafCode) ? 0 : pageLeafCode.indexOf("[");
if (index > 0) {
pageLeafCode = pageLeafCode.substring(index + 1,pageLeafCode.length() - 1);
建议:org.apache.commons.lang3.StringUtils.substringAfter(String, String)
22.
public String toString() {
return "Leaf [leafId=" + leafId + ", leafName=" + leafName
+ ", leafCode=" + leafCode + ", nodeCode=" + nodeCode
+ ", leafType=" + leafType + ", leafContent=" + leafContent
+ ", description=" + description + ", subSystemId="
+ subSystemId + "]";
建议:在基类里定义toString()一次就行了。
org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString(
this, ToStringStyle.SHORT_PREFIX_STYLE);
23.
Map<String,String> data = new HashMap<>();
data.put("uri", request.getRequestURI());
data.put("loginId", SSOConstant.getLoginId(request));
data.put("resourceNo", SSOConstant.getResourceNo(request));
建议:凡是常用的hardcode,都做成静态常量。
24.
CacheLoadUtil.getRelationMap().put(roleCode, map);
建议:缓存的东东,不应该在static map的里面,而应该在obj map里面。
25.
public static String objectToString(Object obj){
return obj.toString();
建议:删除这个函数。
26.
public static String replaceSpecialStr(Object value){
if(null != value && !"".equals(value)){
return value.toString().replaceAll("'", "’").trim();
建议:
org.apache.commons.lang3.StringUtils.isNotEmpty(CharSequence)
java.lang.String.replaceAll(String, String) 适合于:正则表达式。
java.lang.String.replace(CharSequence, CharSequence) 更适合此处。
27.
public static String nullConvert(String value){
return null==value?"":value;
建议:该报错的时候,就报错,用org.springframework.util.Assert.notNull(Object)
如果确实有用,用:org.apache.commons.lang3.StringUtils.defaultString(String)
28.
void modifyAuditStatus(Long[] ids, String operType, Map<String,String> data)
建议:Long[] ids改成:List<Long>,面向对象编程,少用array,多用List。
29.
Map<String, List<Role>> map = new HashMap<String,List<Role>>();
map.put("leftRoles", leftRoles);
map.put("rightRoles", rightRoles);
建议:既然只放两个,可以用:org.apache.commons.lang3.tuple.Pair.of(left, right)
30.
for (String loginId : addUsers) {
UserRole ur = new UserRole();
ur.setCreated(now);
ur.setCreatedBy(operator);
ur.setIsActive('0');
ur.setLoginId(loginId);
ur.setResourceNo(resourceNo);
ur.setRoleCode(roleCode);
ur.setStatus("1");
ur.setUpdated(now);
ur.setUpdatedBy(operator);
建议:
ur.setIsActive('0'); ur.setStatus("1"); 把类似的风格做成两样东东了,建议都用int风格。
建议:
用多参数的Constructor,把10行变成1行。
有人对此提出疑问:把10个参数放在Constructor里,太多了……
他说的,适合于啥情况呢?
OO设计,有几条重要原则:
(A)迪米特法则——“最少知识原则”。“ 不要和陌生人说话”。
(B)强内聚,弱耦合。即:关系越少越好。
UserRole,里面所有的属性都是“同类的傻傻的boolean/int/String/…”,是很简单的容器,
不属于OO设计范围,就算Constructor里有200+个参数,也是正确的。
31.
if (StringUtil.isNotEmpty(viewVO.getViewName())) {
viewVO.setViewName("%" + viewVO.getViewName() + "%");
where.append(" and viewName LIKE :viewName ");
}
if (StringUtil.isNotEmpty(viewVO.getViewType())) {
viewVO.setViewType("%" + viewVO.getViewType() + "%");
where.append(" and viewType LIKE :viewType ");
}
if (StringUtil.isNotEmpty(viewVO.getCreatedBy())) {
viewVO.setCreatedBy("%" + viewVO.getCreatedBy() + "%");
where.append(" and createdBy LIKE :createdBy ");
}
建议:
把"%"改成'%'
public static final char SQL_WILDCARD = '%';
把常用的拼接功能做成个静态函数:
public static void wildcardSqlWord(String str) {
return Util.SQL_WILDCARD + str + Util.SQL_WILDCARD;
}
32.
// 转换用户状态
switch (u.getStatus()) {
case "0":
u.setStatus("New");// 新建
break;
case "1":
u.setStatus("Normal");// 正常
break;
case "2":
u.setStatus("Forbidden");// 禁用
break;
default:
u.setStatus("Illegal");// 非法
break;
// 转换参数状态
switch (sys.getStatus()) {
case "0":
sys.setStatus("Normal");// 正常
break;
case "1":
sys.setStatus("Forbidden");// 禁止
break;
default:
sys.setStatus("Illegal");// 非法
break;
}
// 操作级别转换
switch (sys.getOperationGrade()) {
case "0":
sys.setOperationGrade("无");
break;
case "1":
sys.setOperationGrade("查询");
break;
case "2":
sys.setOperationGrade("修改");
break;
case "3":
sys.setOperationGrade("删除");
break;
case "4":
sys.setOperationGrade("全部");
break;
default:
sys.setOperationGrade("Illegal");
break;
建议:
在default后面,不要写break。
switch(x)里,尽量不用String,而用enum。
如果确实需要switch(数字),就在enum里加入成员常量。例如:
public enum CmdCategory implements MyEnum {
/**
* <code>dummy = 0;</code>
*/
dummy(0, 0),
/**
* 统一官网
*/
official(1, 10000),
/**
* 个人系统
*/
person(2, 20000),
/**
* 企业系统
*/
company(3, 30000),
33.
private CacheLoadUtil() {
super();
}
public class StringUtil {
private StringUtil(){
super();
};
建议:
public Util() { // 这里用了public,是为了覆盖率的完美。
throw new java.lang .NoSuchMethodError();
}
34.
String sql = " select l.* from T_PVG_LEAF l join T_PVG_ROLE_LEAF rl on l.leafCode
建议:应该回避”l”。
”l”长得很像数字1和i的大写字母,java的语言规范中都回避,long 3用”3L”表示。
35.
if (obj == null) {
result = "PO00000000";
}else {
result = LeafRelation.nextBriefCode(String.valueOf(obj));
}
建议:如果两个长度都中等,可以合并为一行,用java的三元运算符:
result = (null == obj) ? x : y
36.
public class CacheLoadUtil {
/**
* <p>以企业资源号为key 公司对象为value</p>
*/
private static final Map<String, Corporation> CORPS = new HashMap<String, Corporation>();
/**
* <p>以部门编号为key 部门对象为value</p>
*/
private static final Map<String, Department> DEPTS = new HashMap<String,Department>();
/**
* <p>以角色代码为key 角色对象为value</p>
*/
private static final Map<String, Role> ROLES = new HashMap<String,Role>();
建议:缓存,不要搞static Map,用实例化的对象,最好用框架EHCache、Memcache……
37. 推荐的Java测试组件
页面层:HtmlUnit。普通的页面和js(jQuery等),它都能搞定。
业务层:JUnit + Mockito/EasyMock
持久层:JUnit + HsqlDB/H2/Derby + Spring Context
测试结果报表:Cobertura / JaCoCo
构建工具:Maven / Ant。在命令行上(不依赖于IDE),用一条的指令搞定全部构建的东东。
38. 自动化测试的重要信息
(1) 想要在程序这条路上走几十年,搞自动化测试是最正确的路线。
(2) 参考 张林 的http://qc.gyist.com/Wiki.jsp?page=SoftwareTesting
(3) 参考第37节
(5) 假设某版本app有900个bug,有了“自动化测试”,能快速检查出来820个,
30个被测试组检查出来,10个在UAT环境出现,30个在生产环境出现,
还剩10个永远也不会发现。这是比较理想的情况。
假如没有“自动化测试”,820个就要分担给“测试组”、“UAT”和“生产环境”来暴露了。
这会极大的增加程序员返工的次数和沟通的次数。我们绝不想要这种状况。
这会极大的增加公司的经营成本。老板绝不想要这种状况!!!
原则:软件bug,越早发现,成本越低。
39. 有人问:为什么推荐JUnit4?为什么抛弃TestNG?
JUnit,简单易用,最好了。主流版本是:3.8.x和4.x。JUnit4,约束更少,功能更强大。
Testng,本身过度复杂,在各大IDE上的版本都不同,
本身也有内存泄露等毛病,新版本久不更新,应该抛弃。
40.
public static final Map<String, Corporation> getCorps() throws SystemException {
if (CORPS.isEmpty()) {
List<Corporation> cps = SpringBeanUtil.getBean(AclConstants.CORP_SERVICE, CorporationServiceImpl.class).queryCorporationAll();
…………
public class SpringBeanUtil implements ApplicationContextAware {
private static ApplicationContext ctx;
private SpringBeanUtil() {
super();
}
public static <T> T getBean(String id, Class<T> clazz) {
if (ctx == null) {
throw new NullPointerException("ApplicationContext is null");
}
return (T) ctx.getBean(id);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
ctx = applicationContext;
}
}
建议:删除Constructor,或者参考第33节
建议:扔掉getBean函数,用:
org.springframework.beans.factory.BeanFactory.getBean(String, Class<T>)
BeanFactory 是ApplicationContext的父接口。
建议:Spring搞的都是OO,我们用Spring也应该遵循OO。OO和静态的东东是相排斥的。
把Spring的ctx做成静态的引用,会有多种缺陷。例如:
(A)潜在的内存泄露。
(B)清理对象的时候,总是不能清理static ctx,这是灾难性的错误。
建议:getBean可能是作者的使用目的,是以static的方式访问的。
可它的初始化,竟然是以实例的方式搞的!!!
@Override
public void setApplicationContext
建议:经过上面4条建议,可以删除掉SpringBeanUtil这个可怜的类了。
41.
public String resetPassword(String loginId, String resourceNo,
Map<String,String> data) throws SystemException {
String message = "";
String userSymbol = CacheCommonUtil.getUserSymbol(loginId, resourceNo);
User user = CacheLoadUtil.getUsers().get(userSymbol);
SystemParam dPassword = systemDao.selectSystemParamByKey(SystemParamConstants.PARAM_PWD_GROUP,user.getResourceNo());
try {
if (dPassword==null||StringUtil.isEmpty(dPassword.getParamValue())) {//若无公司的默认密码,则默认为登录名
user.setPassword(CoderUtils.toHex(CoderUtils.encryptMD5(user.getLoginId())));
}else {
user.setPassword(CoderUtils.toHex(CoderUtils.encryptMD5(dPassword.getParamValue())));
}
user.setUpdated(new Date());
user.setUpdatedBy(data.get("loginId"));
userDao.update(user);
CacheLoadUtil.getUsers().put(userSymbol, user);
// 增加操作日志
SystemLogUtil.addSystemLog(data, FuncType.SYSTEM_SETTING,AclConstants.SYS_RESET_PASSWORD, "密码******", "密码******");
message = "success";
建议:第4行,凡是从缓存取数据的操作,应该建立个成员变量(member variable),
用Spring的标准set方式注入cacheManager对象,坚决抛弃静态功能。
建议:CoderUtils,词汇Coder明显意义错误。
可以改成CodecUtils,标准依据:org.apache.commons.codec.*
建议:加密用:org.apache.commons.codec.digest.DigestUtils .md5Hex(byte[] data)
建议:倒数第3行,密码,不应该以明文方式出现在log里。
42.
public class SystemServiceImpl implements ISystemService {
private static final Logger LOGGER = LoggerFactory.getLogger(SystemServiceImpl.class);
建议:实例范围的类,就用实例范围的Logger。
private Logger LOGGER = LoggerFactory.getLogger(getClass());
43. 自动化测试,包含了哪几块?
(A)自动化单元测试。专门测试某层级的某类。这是最细粒度的。
(B)自动化集成测试。
例如:某Web Service的TC,顺便也把它的下面几层(Service层、Dao层)都测试到了。
(C)冒烟测试
44. 自动化测试,有哪些原则?
这里,简称为TC(TestCase)。
(A)TC_Y不会依赖于TC_X和TC_Z。
(B)跑任何的TC,只用1条指令。注意:只是1条,不是2条或N条。
这1条,里面包含了启动相关的资源(DB,Web Server……)
(C)在developerA电脑里能跑的300个TC,移动到developerB电脑里的任何目录里,
也要跑对。像”C:/MyTempDir”之类的absolute path,绝不应该出现。
(D)不依赖于外部资源和网络。
例如:用www.ip138.com得到本机的外网ip。如果那家网站垮了或被封了,就会影响TC。
例如:利用了公司内网里某台DB和某些WebService。如果那些停了,就会影响TC。
例如:源码里出现了多次192.168.1.xxx就是错误的。
既然“不依赖于外部资源”了,那就更加不会依赖于“虚拟机”了。
有网络的时候能跑对;拔掉网线的接头,也能跑对。
(F)不应该弹出UI。
如果必须要弹出UI才能检验正确性,也行,还必须在弹出后3秒再自动的关闭它。
然后接着继续跑后面的TC。
45. TC需要的外部资源,如何模拟?
(A)Mockito / EasyMock
用于模拟:接口,抽象类,普通类(非final的)
待续。
(B)Embedded Tomcat
用于模拟Web Site和Web Service。
@BeforeClass
public static void setUpClass() throws Exception {
tomcat = new Tomcat();
tomcat.setBaseDir(tomcat4junit);
tomcat.addWebapp(new File("web").getCanonicalPath());
tomcat.setPort(PORT);
tomcat.start();
}
@AfterClass
public static void tearDownClass() throws Exception {
tomcat.stop();
tomcat.destroy();
}
(C)DbUnit + Derby / HsqlDB / H2 instead of MySql / Oracle / SqlServer
经典用法:在每次TC之前(@Before里),清空table,并且导入某xml文件到table里:
DatabaseOperation.CLEAN_INSERT.execute(dbConn2, dataSet2);
46.
private String translateContract(String contract,Declare declare) throws Exception {
Pattern pattern = Pattern.compile("[$]\\{[^}]*\\}");
Matcher matcher = pattern.matcher(contract);
StringBuffer sb = new StringBuffer();
While( … )
建议:
为了避免硬编码,为了减少复杂性,尽量不要自己搞正则表达式。
用org.springframework.beans.factory.config.PlaceholderConfigurerSupport的常量。
final String[] arr = StringUtils.substringsBetween("abc${123}efg${678}www",
DEFAULT_PLACEHOLDER_PREFIX, DEFAULT_PLACEHOLDER_SUFFIX);
assertEquals("123", arr[0]);
assertEquals("678", arr[1]);
至于替换的功能,参考:spring PropertyPlaceholderConfigurer
47.
contract.setSerialno(UUID.randomUUID().toString().replaceAll("-", ""));
建议:
public String replaceAll(String regex, String replacement)
性能差些,是针对正则表达式搞替换的,而不是针对普通String。
这里,应该用
public String replace(CharSequence target, CharSequence replacement)
48.
if(type.equals(Workflow.TASK_CLAIM)){
if(role.equals("S")){
appStatus = "1";
}else if(role.equals("M1")){
dataStatus = "2";
appStatus = "4";
}else if(role.equals("M2")){
dataStatus = "2";
appStatus = "7";
}else if(role.equals("P")){
appStatus = "10";
}else if(role.equals("PHK")){
appStatus = "13";
}else if(role.equals("PHTQR")){
appStatus = "15";
}else if(role.equals("PGZ")){
appStatus = "17";
}
}else if(type.equals(Workflow.TASK_COMPLETE)){
if(role.equals("S")){
if(passValue){
dataStatus = "2";
appStatus = "3";
}else{
dataStatus = "0";
appStatus = "0";
}
}else if(role.equals("M1")){
if(passValue){
dataStatus = "2";
appStatus = "6";
建议:
(A)用Java7的switch( str )功能。
(B)凡是常量,都应该以String[]/String[][]/enum的形式,做在Util里。
(C)每个常量的意义,用本地语言(中文)尽量详细解释。例如:
/**
* 很常用的表示状态的标志位。
* 只用一个byte/char表示状态,让解析更快。
*/
public static final String OK = String.valueOf(1);
49. 如何对程序打分(0--100)?
(A)勉强跑起来的,能用的,通过测试组的检查。0—30
中国的99+%的公司里的大部分程序员,做到了这条就停止了。
(B)自动化审核通过的(PMD,FindBugs,CheckStyle……),符合常见模式的。0—10
用自动化工具审核,然后修改。这条容易。
JavaEE里,通用的东东多,我们大多数都只做增删改查,需要的模式很少。这条也容易。
(C)主要的功能,被自动化测试覆盖了的。0--15
例如:某项目有WebService层,Service层,Dao层。
由于时间紧,仅仅只搞了WebService层的自动化测试,
虽然不足,但基本上覆盖了主要的功能。这也行的。
(D)95+%的代码行,被自动化测试覆盖了的。0—15
听说,中国阿里系的软件项目,能达到这个程度。
(E)工程总体代码行数,很少较少的。0--20
这个需要很多年的细致的知识积累,软件行业里,很少有人能达到。
例如:很多功能,在jdk/apache commons/spring里都有,我们简单的调用就行了。