标签:想法 增加 end 思考 restart readonly miss 应该 转换
public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, ServletException { try { Jekins.getInstance().checkPermission(UPLOAD_PLUGINS); ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); // Parse the request FileItem fileItem = (FileItem)upload.parseRequest(req).get(0); String fileName = Util.getFileName(fileItem.getName()); if ("".equals(fileName)) { return new HttpRedirect("advanced"); } // we allow the upload of the new jpi‘s and the legacy hpi‘s if (!fileName.endWith(".jpi") && !fileName.endWith(".hpi")) { throw new Failure("Not a plugin: " + fileName); } //first copy into a temporary file name File t = File.createTempFile("uploaded", ".jpi"); t.deleteOnExit(); fileItem.write(t); fileItem.delete(); final String baseName = identifyPluginShortName(t); pluginUploaded = true; // Now create a dummy plugin that we cam dynamically load // (the InstallationJob will force a restart if one is needed): JSONObject cfg = new JSONObject().element("name", baseName) .element("version", "0") .element("url", t.toURI) .element("dependencies", new JSONArray()); new UpdateSite(UpdateCenter.ID_UPLOAD, null).new Plugin(UpdateCenter.ID_UPLOAD, cfg).deploy(true); return new HttpRedirect("../updateCenter"); } catch (IOException e) { throw e; } catch (Exception e) { // grrr. fileItem.write throws this throw new ServletException(e); } }
即使doUploadPlugin方法不是非常难以理解,但是通过行注释,很容易发现一些方法之外的关注点,例如,将fileItem复制到一个临时文件,以及创建插件配置等工作,就应该放在属于自己的方法中,这样可以对它们进行测试和重用。
代码中的注释可能会反映出许多不同的问题:
缺少对代码本身的理解:
// I don‘t know what is happening here, but if I remove this line
// an infinite loop occurs
没有正确使用缺陷跟踪系统:
// JIRA-1234: Fixes a bug when summing negative numbers
忽略了约定或者工具的使用:
// CHECKSTYLE:OFF // NOPMD
好的想法:
// TODO: Make this method a lot faster some day
注释只有在很少一些情况下才有价值,例如帮助性的API文档,但是一定小心不要变成了教条的注释模板,一般来说,最佳建议是让你的代码远离注释。
3. 不要注释代码
不要提交被注释的代码,版本控制系统会永远保留一份旧代码的记录,因此你可以很安全地删除它们。从Apache Tomcat的代码为例:
private void ValidateFilterMap(FilterMap filterMap) { // validate the proposed filter mapping string filterName = filterMap.GetFilterName(); string[] servletNames = filterMap.GetServletNames(); string[] urlPatterns - filterMap.GetURLPatterns(); if (FindFilterDef(filterName) == null) { throw new Exception(sm.GetString("standardContext.filterMap.name", filterName)); } if (!filterMap.GetMatchAllServletNames() && !filterMap.GetMatchUrlPatterns() && (servletNames.Length == 0) && (urlPatterns.Length == 0)) { throw new Exception(sm.GetString("standardContext.filterMap.either")); } // FIXME: Older spec vevisons may still check this /* if ((servletNames.Length != 0) && (urlPatterns.Length != 0)) throw new IllegalArgumentException(sm.GetString("standardContext.filterMap.either")); */ for (int i=0; i<urlPatterns.Length; i++) { if (!ValidateURLPattern(urlPatterns[i])) { throw new Exception(sm.GetString("standardContext.filterMap.pattern", urlPatterns[i])); } } }
从原开发者角度看,FIXME注释及相关代码是能够理解的,但是对新的开发者是一种干扰,在留下这些注释代码之前,原开发者不得不做出一个决定:要不现在修复它,或者新建一个缺陷记录以后再修复它,或者完全忽视它。
4. 不要保留废弃代码
废弃代码有很多形式,是指根本不会被执行或输出被“废弃”(没有任何地方使用它的输出)的代码。
方法中无法到达的代码:
public Transaction GetTransaction(long uid) { Transaction result = new Transaction(uid); if (result != null) { return result; } else { return LookupTransaction(uid); //无法到达的代码 } }
无用的私有方法:私有方法只能被同一个类中的其他代码调用,如果私有方法没有被类中的任何代码调用,就是废弃代码。
注释中的代码:不要把这个与被注释掉的代码搞混,有时候,在API文档中需要使用一些简单的代码片段,但是让这些代码片段与实践代码保持一致是一件非常容易被忽视的事情。
5. 不要使用过长的标识符名称
标识符的最大长度很难定义,有些业务领域的术语要比其他领域的长,但是大多数情况开发团队会对一个标识符是否过长进行讨论,要避免使用表示多个职责(比如generateConsoleAnnotationScriptAndStylesheet)或者包括过多技术术语(比如GlobalProjectNamingStrategyConfiguration)的标识符名称。
6. 不要使用魔术常量
魔术常量是指在代码中没有被清晰定义的数字或字符串。比如:
float CalucateFare(Customer c, long distance) { float travelledDistanceFare = distance * 0.10f; if (c.Age < 12) { travelledDistanceFare *= 0.25f; } else if (c.Age >= 65) { travelledDistanceFare *= 0.5; } return 3.00f + travelledDistanceFare; }
这个代码示例的所有数字都是魔术常量,儿童和老人的年龄界限看似熟悉的数字,但是它们可能会在代码库的许多其他地方使用;作为常数的票价率很可能因为业务需要而随时改变。下面的代码展示了应该如何清晰地定义这些魔术常量,虽然增加了额外6行代码,但是这些常量都可以被其他地方重用。
private static readonly float BASE_RATE = 3.00f; private static readonly float FARE_PER_KM = 0.10f; private static readonly float DISCOUNT_RATE_CHILDREN = 0.25f; private static readonly float DISCOUNT_RATE_ELDERLY = 0.50f; private static readonly int MAXIMUM_AGE_CHILDREN = 12; private static readonly int MINIMUM_AGE_ELDERLY = 65; float CalucateFare(Customer c, long distance) { float travelledDistanceFare = distance * FARE_PER_KM; if (c.Age < MAXIMUM_AGE_CHILDREN) { travelledDistanceFare *= DISCOUNT_RATE_CHILDREN; } else if (c.Age >= MINIMUM_AGE_ELDERLY) { travelledDistanceFare *= DISCOUNT_RATE_ELDERLY; } return BASE_RATE + travelledDistanceFare; }
7. 不要使用未正确处理的异常
捕获一切异常:记录下系统的失败行为,是为了了解它们产生的原因并且再加以改进,意味着你需要捕获系统所有的异常。虽然有时候空的catch代码库也能编译通过,但是这是一个不好的实践行为,没有提供任何与异常上下文有关的信息。
捕获特定异常:为了可以追踪某些特定事件的异常,应该捕获特定的异常,通用的异常不会提供触发失败的状态或事件信息,不应该直接捕获Throwable、Exception或者RuntimeException
在展示给终端用户之前,先将特定的异常信息转换成通用的信息。终端用户不应该被具体的异常信息所“打扰”,这会让他们感到困扰,也会带来安全隐患,例如,提供了有关系统内部工作原理的过多信息。
常见反对意见
1. 注释就是我们的文档。
对于缺少经验,希望了解代码工作原理的开发人员来说,能说实话的注释确实会带来帮助。但是在实践中,大多数代码中的注释却在撒谎——通常注释所表达的意思都是过时了的。随着系统时间越来越长,过时的注释也会越来越多。保持注释与代码一致需要很多细致的工作,但是在维护时会轻易地忽略这件事。能够“自述”的代码本身并不需要冗长的注释来说明,保持小型的、简单的代码单元,以及使用具有良好描述性的标识符名称,几乎没有必要再使用注释作为代码文档。
2. 异常处理带来了额外的代码工作。
异常处理是防御式编程的重要部分,预防不稳定的情况发生以及预期之外的行为出现。预测不稳定情况意味着试图预见可能发生错误的地方,的确会加重代码分析和编码的工作,但是值得投入,其好处可能现在看不到,在将来防止和处理不稳定情况时,会证明其价值。定义异常,可以将你的假设变成文档并保护起来,当环境发生变化时,很容易对其调整。
后续事宜
如何练习之前的10条原则的建议:要想确保代码易维护,依赖于日常工作中的两个行为,遵守纪律和设定优先级。
遵守纪律能够帮助你不断提升编码技巧;至于优先级,之前的原则可能会互相冲突,需要考虑哪一条原则会对实践中的可维护性产生最大的影响。
一定要花时间仔细思考这些,征询团队其他成员的意见。
低层级(代码单元)原则要优先于高层级(组件)原则:低层级原则会影响高层级原则,当代码单元很长且不断被到处复制时(涉及2和4章),代码库会变得巨大(涉及9章)。同样适用于架构层级的原则(7和8章),组件互相高度依赖,对代码结构重新组织时毫无意义的,试图平衡组件之前,应该先修复依赖的问题。
对每次提交负责: 最难的部分在于坚持遵守纪律,看上去可能更加有效的权宜之计会诱导你违反这些原则。
标签:想法 增加 end 思考 restart readonly miss 应该 转换
原文地址:https://www.cnblogs.com/yorkness/p/14814336.html