使您的Java代码闻起来很新鲜

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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/396518.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL时间戳与日期格式的相互转换

MySQL时间戳与日期格式的相互转换&#xff0c;PHP时间戳与日期格式的相互转换 MySQL: 获取当前时间SELECT NOW(); // 2018/10/11 14:22:51 时间日期格式转换成时间戳格式&#xff0c;UNIX_TIMESTAMP()SELECT UNIX_TIMESTAMP(NOW()); // 1539238930 时间戳格式转换成时间日期格式…

Linux内存分配机制之伙伴系统和SLAB

转载请注明原文地址&#xff1a;http://www.cnblogs.com/ygj0930/p/6539590.html 内核内存管理的一项重要工作就是如何在频繁申请释放内存的情况下&#xff0c;避免碎片的产生。这就要求内核采取灵活而恰当的内存分配策略。通常&#xff0c;内存分配一般有两种情况&#xff1a…

this.$modal.confirm 自定义按钮关闭_自定义函数,让你玩转Excel得心应手

让“自动更正”输入统一的文本&#xff0c;你是不是经常为输入某些固定的文本,如《电脑报》而烦恼呢?那就往下看吧。1.执行“工具→自动更正”命令,打开“自动更正”对话框。2.在“替换”下面的方框中输入“pcw”(也可以是其他字符,“pcw”用小写),在“替换为”下面的方框中输…

php mysql 排名算法_MySQL PHP:优化排名查询和计数子查询

这是原始数据,并希望根据得分(count(tbl_1.id))对它们进行排名.[tbl_1]id | name1 | peter2 | jane1 | peter2 | jane3 | harry3 | harry3 | harry3 | harry4 | ron因此,制作临时表(tbl_2)来计算每个id的分数.SELECT id, name, COUNT( id ) AS scoreFROM tbl_1GROUP BY idORDER…

CCF-CSP 最大的矩形

问题描述在横轴上放了n个相邻的矩形&#xff0c;每个矩形的宽度是1&#xff0c;而第i&#xff08;1 ≤ i ≤ n&#xff09;个矩形的高度是hi。这n个矩形构成了一个直方图。例如&#xff0c;下图中六个矩形的高度就分别是3, 1, 6, 5, 2, 3。请找出能放在给定直方图里面积最大的矩…

Stack Overflow 2016年对50,000名开发人员进行的调查得出的见解

Today, Stack Overflow released the results of their 2016 survey of more than 50,000 developers.今天&#xff0c;Stack Overflow发布了他们2016年对50,000多名开发人员进行的调查的结果。 I’ve combed through this big document to bring you the most surprising ins…

web管理

1.站点根目录下查找是否被放置webshell***根据语句判断是不是PHP***脚本# find /storage/www/ -name "*.php" | xargs grep-in --color "eval("# grep -i --include*.php -r system\s*\( /storage/www/2.统计访问日志中来自同ip出现的次数分析盗链、***、机…

MySQL的主从复制云栖社区_MySQL-主从复制

前言前篇说了作为运维在数据库块最起码要会两大技能&#xff0c;今天来说说第二技能--主从复制随着业务的增长&#xff0c;一台数据库服务器以满足不了需求了&#xff0c;负载过重&#xff0c;这时候就需要减压&#xff0c;实现负载均衡读写分离&#xff0c;一主一从或一主多从…

数据存储(SharedPreferences存储)

SharedPreferences是通过 键值对 的方式存储数据SharedPreferences是通过键值对的方式存储的 将数据存储到SharedPreferences中有3种方法&#xff1a;1.Context类中的getSharedPreferences()方法2.Activity类中的getPreferences()方法3.PreferencesManager类中的getDefaultShar…

编程程序的名称要记住吗_学习编程时要记住的5件事

编程程序的名称要记住吗by Kurt由库尔特 学习编程时要记住的5件事 (5 Things to Remember When You’re Learning to Program) Learning to program is challenging. Aside from choosing a language or setting up a development environment that you know nothing about, t…

mysql 数据分析的步骤_数据分析8个主要步骤

# 在对数据进行分析时&#xff0c;主要细分为明确目标、应用思维和如下8个具体步骤&#xff1a;1、读取数据2、清洗数据3、操作数据4、转换数据5、整理数据6、分析数据7、展现数据8、总结报告接下来将介绍使用python来具体处理数据&#xff0c;包括上面几个步骤的实现&#xff…

python学习的一个定位_python学习之——selenium元素定位

web自动化测试按步骤拆分&#xff0c;可以分为四步操作&#xff1a;定位元素&#xff0c;操作元素&#xff0c;获取返回结果&#xff0c;断言(返回结果与期望结果是否一致)&#xff0c;最后自动出测试报告。其中定位元素尤为关键&#xff0c;此篇是使用webdriver通过页面各个元…

Invoker

Invoker 是实体&#xff0c;dubbo外其他对象的转化。转载于:https://www.cnblogs.com/gtaxmjld/p/9786894.html

如何在开源社区贡献代码_如何在15分钟内从浏览器获得您的第一个开源贡献

如何在开源社区贡献代码Matt Mullenweg, founder of Automattic, recently offered this advice to aspiring developers: “Contribute to open source.”Automattic的创始人Matt Mullenweg最近向有抱负的开发人员提供了以下建议 &#xff1a;“ 致力于开源。 ” Mullenweg —…

小心情。

从一开始学习html到现在的nodejs&#xff0c;也有段时间了&#xff0c;那个时候什么都不知道&#xff0c;记得一两年之前还沉迷在一些网络技术的圈子里面&#xff0c;每天看着那些大牛&#xff0c;感觉都很是厉害&#xff0c;每一项技术总是那样的让我着迷&#xff0c;从易语言…

一、win7下安装yii2

作者&#xff1a;PHP学习网 出处&#xff1a;http://www.viphper.com/?p1159 本文版权归作者&#xff0c;欢迎转载&#xff0c;但未经作者同意必须保留此段声明&#xff0c;且在文章页面明显位置给出原文连接&#xff0c;否则保留追究法律责任的权利。 之前在liunx上安装过yii…

js获取浏览器滚动条距离顶端的距离

js获取浏览器滚动条距离顶端的距离 一、jQuery获取的相关方法 jquery 获取滚动条高度获取浏览器显示区域的高度 &#xff1a;$(window).height(); 获取浏览器显示区域的宽度 &#xff1a;$(window).width(); 获取页面的文档高度 &#xff1a;$(document).height(); 获取页面的文…

vs dll必须和exe在同一个目录_Win10系统丢失 .dll 文件的三种解决方案教程

有时候开机或打开一个软件时&#xff0c;系统会提示无法启动程序&#xff0c;这是怎么回事呢&#xff1f;这是因为计算机丢失某个或某些dll文件&#xff0c;由于系统本身不存在这些运行库文件&#xff0c;需要进行添加才能使用该软件。方法一&#xff1a;下载丢失的.dll文件&am…

datagrid页面获取表单一条数据的例子

【问题背景】 最近在做ITOO考评的时候想从页面获取表单选中的数据&#xff1a; 【代码】 在数据网格&#xff08;datagrid&#xff09;组件包含两种方法来检索选中行数据&#xff1a; getSelected&#xff1a;取得第一个选中行数据&#xff0c;如果没有选中行&#xff0c;则返回…

utf-8转换gbk代码_将代码转换为现金-如何以Web开发人员的身份赚钱并讲述故事。...

utf-8转换gbk代码by Kurt由库尔特 将代码转换为现金-如何以Web开发人员的身份赚钱并讲述故事。 (Turning code to cash — How to make money as a Web Developer and live to tell the tale.) So you just learnt to code. You’re eager and anyone who can’t code thinks …