by Marco Massenzio

由Marco Massenzio

使您的Java代码闻起来很新鲜 (Make your Java code smell nice and fresh)

A few years ago I joined a startup working on a cloud enterprise service that was originally built by an offshore team.

几年前,我加入了一家从事云企业服务的初创公司,该服务最初是由离岸团队构建的。

The chosen technologies (REST, Java, MongoDB) were actually valid technical choices for the problem at hand. Too bad they then proceeded to get it spectacularly wrong with a bloated (and unmanageable) data schema and an even worse Java implementation.

所选技术(REST,Java,MongoDB)实际上是解决当前问题的有效技术选择。 不幸的是,他们随后使用with肿的(且难以管理的)数据模式和更糟糕的Java实现,将其改错了。

The most amusing part was they genuinely believed that Mongo’s “schema-less” claim meant that there was no need for carefully designing the data model, thinking through the indexes, and considering how data would be accessed.

最有趣的部分是他们真正相信Mongo的“无架构”主张意味着,无需仔细设计数据模型,仔细研究索引并考虑如何访问数据。

In this post, I’d like to dissect the many “smells” (see Martin Fowler’s book) that a particular Java class was emitting, how this could have been avoided, and the many anti-patterns encountered.

在本文中,我想剖析特定Java类发出的许多“气味”(请参阅​​Martin Fowler的书 ),如何避免这种情况以及遇到的许多反模式。

My hope is that by reading this post, you will avoid making the same mistakes, and folks like me will not be required to come over and fix your stinkin’ code!

我希望通过阅读这篇文章,您将避免犯同样的错误,并且不需要像我这样的人来解决您的臭代码!

第一闻:没有风格 (First smell: no style)

I have previously ranted about the need to follow adequate (and widely accepted) code styles. In short:

之前,我曾抱怨需要遵循适当的(并被广泛接受的)代码样式。 简而言之:

  • New developers joining the team will have a less steep learning curve.加入团队的新开发人员的学习曲线不会那么陡峭。
  • Automated tools will be better able to provide insights.自动化工具将能够更好地提供见解。
  • Trivial, avoidable bugs will be easy to spot.琐碎的,可避免的错误将很容易发现。

And, yes, your code will look nicer and your self-respect will improve too. It’s hard to convince people of your brilliant insights when your code looks like you wrote it by bashing a caveman club against your keyboard.

而且,是的,您的代码将看起来更好,并且您的自尊心也将得到改善。 当您的代码看起来像您编写代码时,通过打击键盘上的穴居人俱乐部,很难让人相信您的出色见解。

Here are some of the hallmarks of the “keyboard caveman”:

以下是“键盘穴居人”的一些标志:

  • inconsistent use of spaces (sometimes before, sometimes after parentheses and around operators, or none at all)空格的使用不一致(有时在括号前,有时在括号后,运算符前后,或者根本没有)
  • inconsistent use of blank lines: sometimes one at random, sometimes two or more, then none空行使用不一致:有时是随机的,有时是两个或更多,然后是无
  • no regard for line-length (many lines longer than 100 columns, several longer than 200, and a 257 column record-breaking line)不考虑行长(许多行超过100列,几行超过200列,以及257列打破记录的行)
  • no use of Java 7's “diamond pattern” and some random uses of raw collections

    不使用Java 7的“钻石图案”,并且随机使用原始集合

  • variable names that have little to do with their true meaning变量名与其真实含义无关
  • inconsistent use of constants and hard-coded strings — sometimes the same hard-coded string repeated several times within a few lines of code — clearly the result of copy-and-paste

    常量和硬编码字符串的用法不一致-有时同一硬编码字符串在几行代码中重复多次-显然是复制粘贴的结果

and on and on…

还有……

Here are some tips for fixing your code style code smells:

以下是一些修复代码样式代码气味的提示:

  • Pick a good, widely know style guide and stick to it. Even better, have your IDE automatically enforce the coding style (both Eclipse and IntelliJ are very good at this).

    选择一个好的,广为人知的风格指南并坚持下去。 更好的是,让您的IDE自动执行编码样式(Eclipse和IntelliJ在这方面都非常擅长)。

  • If your programming language of choice supports it, consider using an automated tool to find out code style infractions (such as pylint, jslint). Even better, make style checks part of your automated CI builds (and prevent commits until those pass).

    如果您选择的编程语言支持,请考虑使用自动化工具找出违反代码风格的行为(例如pylint,jslint)。 更好的是, 将样式检查作为自动化CI构建的一部分 (并在提交通过之前阻止提交)。

  • Be consistent in the use of spaces, blank lines, and line length. You never know when a project-wide, regex-driven search-and-replace will be necessary.

    在使用空格,空白行和行长时要保持一致 。 您永远不会知道何时需要在项目范围内,由正则表达式驱动的搜索和替换。

  • Never use raw collections. Java generics are here for a reason. This one single point would deserve a post all of its own. I will get round to it in due course. In the meantime, please read Bloch’s excellent Effective Java.

    切勿使用原始集合。 Java泛型在这里是有原因的。 这一点很值得拥有。 我会在适当的时候解决它。 同时,请阅读Bloch的出色的Effective Java 。

  • Spare a thought for the poor soul who’ll have to fix your stinkin’ code. It may even be you in a few months’ time!

    为需要修复您的臭味代码的可怜灵魂保留思想。 几个月后甚至可能是

第二闻:无法测试的代码 (Second smell: untestable code)

I can’t tell you how many times I swore my code was bug-free, but wrote unit tests anyway. This has saved my butt so many times that it’s not even funny.

我无法告诉您我发誓我的代码没有错误的次数,但是无论如何都编写了单元测试。 这已经救了我很多次,甚至都不好笑。

People, do unit test your code. Just do it.

人们,对您的代码进行单元测试。 去做就对了。

For starters, if you want your code to be tested, your code should be, well, testable.

对于初学者来说,如果您希望对代码进行测试,那么您的代码应该是可测试的。

Here’s an example of what not to do: a class was implemented to serve a particular API call which — in response to a client query — would serve a very complex object, with deeply nested sub-objects, all couched in a mix of business logic and UI-related data.

这是一个不执行操作的示例:实现了一个类以服务于特定的API调用,该类调用(响应于客户端查询)将为非常复杂的对象提供服务,并带有深度嵌套的子对象,这些子对象都包含在业务逻辑中以及与UI相关的数据。

When serving such a complex object back, you would want to test it under several different scenarios and ensure that the returned object conforms to the API documented specs.

向后提供这种复杂的对象时,您需要在几种不同的情况下对其进行测试,并确保返回的对象符合API 记录的规范。

Except in this particular real-life case, there was virtually no documented API. In fact, not only there was also no unit testing, but the class was (and probably still remains) untestable.

除了在这种特殊的现实情况下,几乎没有文档化的API。 实际上,不仅没有单元测试,而且该类是(并且可能仍然)不可测试的。

What do I mean by this? well, take a look at this method:

我是什么意思 好吧,看看这个方法:

public static Map<String, Object> getSomeView(    Map<String, Object> queryParams) {  List<Map<String,Object>> results = SomeSearch.find(queryParams);  ...}

As you can see, this method (which returns a Map whose values are Objects — just one step above being untyped) is static — as is the very first method called, which will eventually execute the query against MongoDB.

如您所见,此方法(返回一个Map,其值为Objects -仅在未类型化的步骤之上)是静态的 - 第一个方法称为静态 方法 ,该方法最终将对MongoDB执行查询。

This makes it very difficult to mock the call, or the database query, or to construct a set of data-driven tests, which would have enabled us to test the data transformation and view generation logic in the method under several different scenarios.

这使得模拟调用,数据库查询或构造一组数据驱动的测试变得非常困难,这使我们能够在几种不同的情况下测试方法中的数据转换和视图生成逻辑。

There is today PowerMockito that can solve some of these issues, but doing so is extremely laborious and error-prone. In my experience, mocking static methods requires a ratio of 10:1 lines of mocking code to actual test code.

今天有PowerMockito可以解决其中的一些问题,但是这样做非常费力且容易出错。 以我的经验,模拟静态方法需要模拟代码与实际测试代码的比率为10:1。

It’s also worth noting that queryParams could be pretty much anything. In this case, it’s a paging/sorting set of options. The only way you could have found that out, though, would be to run the actual server in debug mode, then execute the API call from a client and see what comes up at the other end.

还值得注意的是queryParams几乎可以是任何东西。 在这种情况下,它是一组分页/排序选项。 但是,您可能发现的唯一方法是在调试模式下运行实际的服务器,然后从客户端执行API调用,然后查看另一端发生的情况。

Because, yes, you guessed it: there was absolutely no javadoc to explain what the method does.

因为,是的,您猜对了:绝对没有javadoc可以解释该方法的作用。

Consider, instead, if you’d had code similar to this:

考虑一下,如果您有类似以下的代码:

@InjectQueryAdapter query;...public Map<String, ?> getSomeView(boolean sort, int skip, int limit) {    List<Map<String,?>> results = query.find(sort, skip, limit);    ...

For a start, you can mock the query object and return whatever you want during a test run. Secondly, it’s clear what the various parameters are from their names. This also limits the possible permutations that need to be tested. For example: if sort is true, are the returned results sorted? If limit is 1, do you actually get only one result? And so on.

首先,您可以模拟查询对象并在测试运行期间返回所需的任何内容。 其次,很明显,各种参数的名称是什么。 这也限制了需要测试的可能排列。 例如:如果sorttrue ,返回的结果是否排序? 如果限制为1,您实际上只得到一个结果吗? 等等。

But most importantly, because none of the methods invoked is static, it is much easier to construct a test context that is easy to reason about.

但最重要的是,由于调用的方法都不是静态的,因此构造易于推理的测试上下文要容易得多。

Finally, if QueryAdapter were a Java interface, you can swap different implementations at runtime (perhaps driven by configuration), and still the code using and testing it remains perfectly valid.

最后,如果QueryAdapter是Java接口,则可以在运行时交换不同的实现(可能由配置驱动),并且使用和测试它的代码仍然完全有效。

外卖 (Takeaways)

  • use dependency injection (DI) wherever possible. Spring or Guice are really valid frameworks, and are critical in keeping your code clean, responsive to change and testable.

    尽可能使用依赖项注入 (DI)。 Spring或Guice是真正有效的框架,对于保持代码的清洁,对更改的响应和可测试性至关重要。

  • avoid, wherever possible, static methods, unless you absolutely have to. They make your code difficult to test, and really difficult to mock out. They also make your clients (the code that will use your classes/methods) extremely brittle, and equally untestable.

    除非绝对必要 ,否则应尽可能避免使用静态方法 它们使您的代码难以测试,并且真的很难被模拟出来。 它们还会使您的客户(使用您的类/方法的代码)非常脆弱,并且同样无法测试。

  • Java is a strongly typed language. There is a good reason for this. Let the compiler and the JVM do the legwork, and catch your (and others’) mistakes.Java是一种强类型语言。 这是有充分的理由的。 让编译器和JVM进行繁琐的工作,并捕获您(和其他人)的错误。
  • Avoid using String and Object everywhere as a poor man’s dynamic language. If you really cannot devise a data object model for your business entities, you should actually consider a true dynamic language (Python being an excellent choice).

    避免到处使用String和Object作为穷人的动态语言。 如果您确实无法为您的业务实体设计数据对象模型,那么您实际上应该考虑使用一种真正的动态语言 (Python是绝佳的选择)。

  • Document your code. Make sure all public methods have an adequate javadoc for others to understand and use. Also ensure that your classes’ javadoc explains clearly what the class does, how to use it, and also examples of correct (and, potentially, incorrect too) usage. And, most importantly, document you APIs, what the method expects to receive, and what they’re supposed to return.

    记录您的代码。 确保所有公共方法都有足够的javadoc供其他人理解和使用。 还要确保您的类的javadoc清楚地说明了该类的用途,用法以及正确(甚至可能不正确)用法的示例。 而且,最重要的是,记录您的API,该方法预期接收的内容以及它们应返回的内容。

This last point is particularly important: use your unit tests also as a means of demonstrating how your code’s API should be used (and how it shouldn’t be used).

最后一点特别重要:使用单元测试还可以说明如何使用代码的API(以及不应使用的API)。

第三气味:令人困惑的代码 (Third smell: confusing code)

It took me the best part of an hour to reverse-engineer the following method. Then I passed it on to my colleagues as a “Java Puzzler” and their guesses were wildly confused, too.

我花了一个小时的大部分时间来对以下方法进行反向工程。 然后,我将其作为“ Java Puzzler”传递给了我的同事,他们的猜测也大为困惑。

I have “obfuscated” some of the code to preserve confidentiality and avoid embarrassment, but trust me, the obfuscation does make this version less obscure than the original:

我有“混淆”的一些代码,以保持机密性,避免尴尬,但相信我, 混淆确实让这个版本模糊比原来:

// Much as it pains me, I've left the code style (or lack thereof) untouched// PLEASE DON'T DO THIS AT HOMEprivate static String getSomething(boolean isOnRoute,         List<Map<String,Object>> trip,        String primarySomething, String id){    boolean somethingKnown = isOnRoute;    if(!somethingKnown && trip!=null){        for(Map<String,Object> segment: trip){            Map<String,Object> line = (Map<String, Object>) thisLeg.get(Constants.LINE);            if(line!=null){                String something = (String) carrierLane.get(Constants.SOMETHING);                LOG.debug("Line something: "+something);                if(primarySomething.equals(something)){                    somethingKnown = true;                    break;                }            }        }    }    if(somethingKnown){        return primarySomething;    }    LOG.debug("Unknown something:: isOnRoute:'" + isOnRoute + "' ; primarySomething:'" +            primarySomething + "' ; id:'" + id + "'");    return null;}

Taking you out of your misery, all this method does is to return the value (primarySomething) that was originally given to it if the trip isOnRoute. If not, it will tell you so, then return null — and let us gloss for a moment on the fact that it requires the caller to pass in the id only to use it in a debug statement.

如果您的旅程是onRoute ,那么使您摆脱苦难的所有方法都是返回最初赋予它的值( primarySomething ) 如果不是,它将告诉您,然后返回null-让我们暂时了解一下,它要求调用者仅在调试语句中使用id时才传递id

Also, note the unnecessary use of a boolean variable (somethingKnown) only to use it in the break case, and then fall into a return statement. And there are so many other issues here. For example, why assign the value of a flag that indicates whether the trip is “on route” to a flag that indicates whether that elusive something was found?

另外,请注意不必要只使用布尔变量( somethingKnown )才能在break情况下使用它,然后落入return语句中。 这里还有很多其他问题。 例如,为什么将指示旅行是否“在途中”的标志的值分配给指示是否找到了难以捉摸的东西的标志?

In the end, we figured out that this method was entirely pointless, and we removed it entirely.

最后,我们发现此方法完全没有意义,因此我们将其完全删除。

Sad as it sounds, hitting the DEL key on that pile of crap was the high point of my day.

听起来很伤心,敲那堆废话的DEL键是我今天的重点。

Here are some tips for avoiding confusing code:

以下是一些避免混淆代码的技巧:

  • Try and make the intent of your method(s) obviously clear to the readers of your code. Follow agreed-upon naming conventions, clear arguments lists, clear algorithm implementations, and help your co-workers do the same.尝试使您的方法的意图对您的代码读者显而易见。 遵循商定的命名约定,清除参数列表,清除算法实现,并帮助您的同事进行相同的操作。
  • Even doing that, always add a Javadoc to explain what the intent is, what the arguments are supposed to be, and what the expected outcome should be (including possible side-effects).

    即使这样做,也总是添加一个Javadoc来解释意图是什么,参数应该是什么,以及预期的结果是什么(包括可能的副作用)。

  • Avoid shortcuts that will render the logic of your code obscure to other developers.避免使用会使其他开发人员难以理解代码逻辑的快捷方式。
  • And, for God’s sake, avoid concatenating strings. The String.format() and slf4j string composition pattern are there for a reason:

    而且,看在上帝的份上,避免串联字符串。 出现String.format()slf4j字符串组成模式是有原因的:

// Log4J:LOG.debug(String.format(“The result is: %d”, result));
// logback or slf4j: this is more desirable, as you won't incur// the cost of building the string, unless the log level// will actually cause the log message to be emittedLOG.error(“The gizmo [{}] was in {}, but then fritzed at {}, {}”,   gizmoId, state, foo, bar)

第四味:复制并粘贴代码 (Fourth smell: Copy & pasted code)

You remember when you were a kid and you were asked to “spot the difference” between two pictures that seemed identical? Let’s play the opposite game. Can you spot the similarities?

您还记得您小时候被问到“发现两幅看上去相同的照片之间的差异”吗? 让我们玩相反的游戏。 您能发现相似之处吗?

// Again, I've left the code style (or lack thereof) untouched// PLEASE DON'T DO THIS AT HOMEList<Map<String, Object>> originSiteEvents = null;List<Map<String, Object>> destinationSiteEvents = null;List<Map<String, Object>> inventoryEvents = null;try{    originSiteEvents = (List<Map<String, Object>>) ((Map<String, Object>) ((Map)entry.get(ModelConstants.INVENTORY)).get(ModelConstants.ORIGIN)).get(ModelConstants.EVENTS);} catch (Exception e){    //No origin events avaislable}try{    inventoryEvents = (List<Map<String, Object>>)  ((Map)entry.get(ModelConstants.INVENTORY)).get(ModelConstants.EVENTS);} catch (Exception e){    //No inventory events available}try{    destinationSiteEvents = (List<Map<String, Object>>) ((Map<String, Object>) ((Map)entry.get(ModelConstants.INVENTORY)).get(ModelConstants.DESTINATION)).get(ModelConstants.EVENTS);} catch (Exception e){    //No destination events available}if(events != null){    List<Map<String, Object>> eventListForLeg = (List<Map<String, Object>>) events;    for(int j=eventListForLeg.size()-1; j>=0; j--){        Map<String, Object> event = eventListForLeg.get(j);        if(event.get("category")!=null && event.get("category").equals("GPS")){            event.put("isLastGPSEvent", true);            break;        }    }}if(destinationSiteEvents != null){    for(int j=destinationSiteEvents.size()-1; j>=0; j--){        Map<String, Object> event = destinationSiteEvents.get(j);        if(event.get("category")!=null && event.get("category").equals("GPS")){            event.put("isLastGPSEvent", true);            return;        }    }}if(inventoryEvents != null){    for(int j=inventoryEvents.size()-1; j>=0; j--){        Map<String, Object> event = inventoryEvents.get(j);        if(event.get("category")!=null && event.get("category").equals("GPS")){            event.put("isLastGPSEvent", true);            return;        }    }}if(originSiteEvents != null){    for(int j=originSiteEvents.size()-1; j>=0; j--){        Map<String, Object> event = originSiteEvents.get(j);        if(event.get("category")!=null && event.get("category").equals("GPS")){            event.put("isLastGPSEvent", true);            return;        }    }}

As you are probably aware, copy & pasting code is a blatant violation of the DRY principle (“Don’t Repeat Yourself”). Not to mention, it’s pretty awful to look at. It will also make you look like a lazy chump to anyone who knows the first thing about software development.

您可能已经知道,复制和粘贴代码公然违反了DRY原则(“不要重复自己”)。 更不用说,它看起来很糟糕。 对于任何了解软件开发的第一手知识的人,它也会让您看起来像个懒惰的家伙。

It’s worth mentioning that the same exact pattern (navigating nested maps according to sequence of strings) was scattered all over the 600+ lines of code of this class. So, you’d be forgiven for coming up with a utility method like the one I hacked together in less than 10 minutes to replace the abomination above:

值得一提的是,同一类完全相同的模式(根据字符串序列导航嵌套映射)分散在此类的600多行代码中。 因此,您提出了一种实用程序方法,例如我在不到10分钟的时间内就砍掉了上面的可憎性的一种实用程序方法,这是可以原谅的:

/** * Navigates the {@literal path} in the given map and tries * to retrieve the value as a list of objects. * * <p>This is safe to use, whether the path exists or not,  * and relatively safe against {@link java.lang.ClassCastException} * errors (to the extent that this kind of code can be). * * @param map the tree-like JSON * @param path the path to the desired object * @return the list of objects, or {@literal null} if any of the *     segments does not exist */@SuppressWarnings("unchecked")public static List<Map<String, ?>> tryPath(        Map<String, ?> map, List<String> path) {    List<Map<String, ?>> result = null;    Map<String, ?> intermediateNode = map;    String tail = path.remove(path.size() - 1);    for (String node : path) {        if (intermediateNode.containsKey(node)) {            Object o =  intermediateNode.get(node);            if (o instanceof Map) {                intermediateNode = (Map<String, ?>) o;            } else {                LOG.error("Intermediate node {} cannot be " +                    "converted to a Map ({})", node, o);                return null;            }        } else {            return null;        }    }    if (intermediateNode.containsKey(tail)) {        Object maybeList = intermediateNode.get(tail);        if (maybeList instanceof List) {            return (List<Map<String, ?>>) maybeList;        }    }    return null;}

The result is that the sequence of lookups (which, again, when first encountered looked like a riddle wrapped in an enigma) now looks something like:

结果是查找的顺序(再次遇到时,看起来就像是一个被谜团包裹的谜语)现在看起来像:

List<List<String>> paths = Lists.newArrayList();paths.add(Lists.newArrayList(Lists.asList(                ModelConstants.INVENTORY, new String[] {                ModelConstants.EVENTS})));paths.add(Lists.newArrayList(Lists.asList(                ModelConstants.INVENTORY, new String[] {                ModelConstants.ORIGIN,                ModelConstants.EVENTS})));paths.add(Lists.newArrayList(Lists.asList(                ModelConstants.INVENTORY, new String[] {                ModelConstants.DESTINATION,                ModelConstants.EVENTS})));List<Map<String, ?>> events = null;for (List<String> path : paths) {    events = tryPath(entry, path);    if (events != null) {        break;    }}if (events != null) {    for (int j = events.size() - 1; j >= 0; --j) {        Map<String, Object> event = (Map<String, Object>) events.get(j);        if (event.contains("category") && Constants.GPS.equals(event.get("category"))) {            event.put(Constants.isLastGPSEvent, true);            return;        }    }}

This is still not as clean as I’d like it to be, but I mostly blame Java for lacking a factory class similar to Scala’s Lists:

这仍然不像我希望的那样干净,但是我主要归咎于Java缺少类似于Scala的Lists的工厂类:

val paths = List(List(ModelConstants.INVENTORY,                       ModelConstants.EVENTS,                 List(ModelConstants.INVENTORY,                      ModelConstants.ORIGIN,                      ModelConstants.EVENTS),                 List(ModelConstants.INVENTORY,                       ModelConstants.DESTINATION,                      ModelConstants.EVENTS))

Also a quick note the anti-pattern of using a try-catch block to filter out potentially valid code paths, and avoiding writing null and key-existence checks.

还要快速注意一下使用try-catch块来过滤出可能有效的代码路径并避免编写空值和键存在检查的反模式。

By factoring out the checks and the class casts (with proper type safety checks) in one place, you avoid having to choose between two bad options, which are: scatter the same repetitive, tedious checks all over the place, or mask out possible error codes by casting a very wide net.

通过将检查和类强制转换(带有适当的类型安全检查)分解为一个地方,您可以避免在两个错误的选项之间进行选择,这些错误的选项是:在整个地方分散相同的重复,乏味的检查,或者掩盖可能的错误通过投射非常宽的网络进行编码。

If you let through potentially erroneous conditions, you will make it exceedingly difficult to find out root causes of unexpected bugs.

如果您让潜在的错误条件经受了考验,那么将很难找出意外错误的根本原因。

There’s an entire blog post to be written about “swallowing” exceptions and error conditions.

有整篇博客文章要写,涉及“吞咽”异常和错误条件。

So:

所以:

  • don’t copy & paste code: if you see commonality, factor out the common parts and re-use them (in a utility class or a helper method);

    不要复制和粘贴代码 :如果您看到通用性,请分解出通用部分并重新使用它们(在实用程序类或帮助器方法中);

  • don’t avoid safety checks in your code by indiscriminately “swallowing” exceptions: try-catch blocks should be there for a reason, and the reason should be (the giveaway being in the name) exceptional.

    不要通过随意地“吞咽”异常来避免代码中的安全检查: try-catch块应该是有原因的,而原因应该是(赠品是特例 ) 异常

Code is still largely a craft. A deep understanding of issues related to distributed computing, a capacity for critical reasoning and abstract thinking, and an understanding of operational issues related to scalable systems — these are critical these days for becoming a great software developer. Still, the mastery of the craft is as important as it was in Renaissance Italy.

代码在很大程度上仍然是一种技巧 。 对与分布式计算有关的问题的深刻理解,对关键推理和抽象思维的能力以及对与可伸缩系统有关的操作问题的理解,这些对于如今成为一名出色的软件开发人员而言至关重要。 尽管如此,对手艺的掌握与意大利文艺复兴时期一样重要。

Write well-designed, clearly structured and properly documented code — and be proud of your craft!

编写设计良好,结构清晰且文档正确的代码,并为您的手艺感到骄傲!

And if you want to make fun of my code, just head to my github repositories, I’m sure there’s still plenty of smells there (but, hopefully, also a few nuggets of good code that will hopefully inspire you!)

而且,如果您想取笑我的代码,只需转到我的github存储库 ,我肯定那里仍然有很多气味(但是,希望还有一些好的代码能够激发您的灵感!)

Originally published at codetrips.com on January 25, 2015.

最初于2015年1月25日发布在codetrips.com 。

翻译自: https://www.freecodecamp.org/news/do-not-allow-bad-smells-in-your-java-code-4e8ad244393/

使您的Java代码闻起来很新鲜相关推荐

  1. Java基础学习总结(164)——别让Lombok使你的Java代码处于“亚健康”状态

    如果您正在阅读此文,想必您对Project Lombok已经有了一段时间的了解.您是否正准备拥抱Lombok?还是正准备将如此酷炫的项目推荐给你的团队?如果您准备那么做,不妨听听我在使用Lombok一 ...

  2. Java代码 怎么写很精简的代码 如何写又短又能满足需求的代码 如何写看起来很NB的代码 代码之极简之道

    目录 利用语法 1.利用三元表达式 2.利用 for-each 语句 3.利用 try-with-resource 语句 4.利用 return 关键字 5.利用 static 关键字 6.利用 la ...

  3. JVM优化Java代码时都做了什么?

    专栏的前几篇文章了解了JVM的内存模型,GC调优的思路,让我们对于Java底层有了一定的了解,那么采用这种思路去提高JVM的性能,减少JVM额外消耗的同时,JVM究竟做了哪些工作,使我们的Java代码 ...

  4. 一些防止 Java 代码被反编译的方法

    欢迎关注方志朋的博客,回复"666"获面试宝典 由于Java字节码的抽象级别较高,因此它们较容易被反编译.本节介绍了几种常用的方法,用于保护Java字节码不被反编译.通常,这些方法 ...

  5. 面试被问 | 防止 Java 代码被反编译的方法有几种?

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 后台回复"k8s",可领取k8s资料 由于Java字节 ...

  6. Java代码防止被反编译的4中方法

    你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 urlify.cn/2u2yEb 推荐:https://www.xttblog. ...

  7. java代码发送请求并传参_如何优化您的请求请求并使代码审核人员满意

    java代码发送请求并传参 Imagine reading a novel, but having the chapters sliced up and reordered alphabeticall ...

  8. java 代码换行_作为一名资深的程序员,你见过哪些很想吐槽的代码

    作为一名入行很多年的老程序员肯定会见到一些让人想吐槽又想笑的代码,很多程序员说:最想吐槽的是多年以前自己写的代码,现在看着这些代码就会特别想问一句,这是什么鬼,就好想删.但是呢,有些代码咋们不得不吐槽 ...

  9. 使java方法全部折叠_Intellij IDEA中一次性折叠所有Java代码的快捷键设置

    Intellij IDEA中一次性折叠所有Java代码的快捷键设置 问题:在Java文件中,想把所有的Java方法代码都一次性给折叠起来,用哪个点开哪个. 问题来源:在新建model bean的时候, ...

最新文章

  1. 数学之美 系列十一 - Google 阿卡 47 的制造者阿米特.辛格博士
  2. vue适配移动端px自动转化为rem
  3. 【推荐】技术人必看的音视频学习资源清单
  4. 哈哈哈,弟弟被卡桶里了......
  5. win7系统 普通管理员被删除,超级管理员被禁用。普通用户下。怎么激活administrator
  6. 使用开源的openssl的md5头文件,实现对于文件的md5代码
  7. php面试宝典1000题,【PHP面试宝典1000题】HTTP中的请求头(深圳小美网络科技)
  8. spark学习-53-Spark下Java版HBase下的根据权重获取最真实数据
  9. Yarn篇--搭建yarn集群
  10. MyBatis条件查询
  11. ElasticSearch中minimum_should_match详细介绍
  12. 怎么看别人的qq空间怎么看加密的qq空间
  13. 关于flash分区打印信息jffs2: jffs2_scan_eraseblock(): Magic bitmask 0x1985
  14. bbr29_TCP BBR导致性能下降
  15. 【附源码】Java计算机毕业设计基于微信小程序停车系统(程序+LW+部署)
  16. 原型软件 (Prototype Development)开发指南
  17. 独角推荐,只需一个邮箱号就可以注册购买阿里云国际版
  18. blackbox_exporter端口及网络监控
  19. 环境质量现状调查与分析
  20. 有道打赏视频/支付已对接/自带资源

热门文章

  1. 讲的真透彻!还有人不知道什么是AndroidX的吗?已拿offer入职
  2. ZOJ-Crashing Balloon
  3. [原创]java获取word里面的文本
  4. IOS上传文件给java服务器,返回报错unacceptable context-type:text/plain
  5. 读C#开发实战1200例子记录-2017年8月14日10:03:55
  6. SQL 必知必会·笔记14更新和删除数据
  7. Anti-Forgery Request Recipes For ASP.NET MVC And AJAX 防伪验证,防伪请求
  8. SELECT语句,去除某个字段的重复信息
  9. ElasticSearch、Logstash和Kiabana三个开源工具。
  10. 基于SpringBoot+Mybatis+Thymeleaf商品信息管理系统