它闻起来是因为可能有很多情况可以对其进行编辑或改进。
大多数这些气味只是一些可能出错的暗示。因此,它们本身不需要固定......(不过你应该调查一下。)
您可以在这里找到所有以前的代码味道(第一部分 - XXVIII) 。
让我们继续...
你在野外见过 IEngine 吗?
TL;DR: 不要给你的类添加前缀或后缀
某些语言具有与数据类型、抽象类或接口相关的文化习俗。这些名称为我们的模型加载了难以理解的认知翻译。
我们必须亲吻。
public interface IEngine { void Start(); } public class ACar { } public class ImplCar { } public class CarImpl { }
public interface Engine { void Start(); } public class Vehicle { } public class Car { }
如果我们有同义词库,我们可以指出难懂的名字。
在 C# 中,将“I”放在接口名称中是一种常见的做法,因为如果没有它,您将无法分辨它是接口还是类。
这是一种语言气味。
为您的模特使用真实姓名。
Tim Mossholder 在 Unsplash 上拍摄的照片
有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。
杰米扎文斯基
在域对象中访问数据库是一种代码味道。在构造函数中执行它是一种双重气味。
TL;DR:构造函数应该构造(并可能初始化)对象。
在遗留代码中,数据库没有正确地与业务对象分开。
构造函数不应该有副作用。
根据单一职责原则,他们应该只构建有效的对象
public class Person { int childrenCount; public Person(int id) { childrenCount = database.sqlCall("SELECT COUNT(CHILDREN) FROM PERSON WHERE ID = " . id); } }
public class Person { int childrenCount; // Create a class constructor for the Main class public Person(int id, int childrenCount) { childrenCount = childrenCount; // We can assign the number in the constructor // Accidental Database is decoupled // We can test the object } }
我们的 linters 可以在构造函数上找到 SQL 模式并警告我们。
关注点分离是关键,耦合是我们在设计健壮软件时的主要敌人。
<span> Callum Hill在Unsplash上拍摄的照片</span>
我的信念仍然是,如果你得到正确的数据结构和它们的不变量,大部分代码都会自己编写。
彼得·德斯奇
有些对象总是在一起。我们为什么不把它们分开?
TL;DR:让有凝聚力的原始对象一起移动
这种气味是原始迷恋的朋友。
如果将两个或多个原始对象粘在一起,业务逻辑和规则在它们之间重复,我们需要找到双射的现有概念。
public class DinnerTable { public DinnerTable(Person guest, DateTime from, DateTime to) { Guest = guest; From = from; To = to; } private Person Guest; private DateTime From; private DateTime To; }
public class TimeInterval { public TimeInterval(DateTime from, DateTime to) { // We should validate From < To From = from; To = to; } } public DinnerTable(Person guest, DateTime from, DateTime to) { Guest = guest; Interval = new TimeInterval(from, to); } // Even Better... public DinnerTable(Person guest, Interval reservationTime) { Guest = guest; Interval = reservationTime; }
基于内聚模式的检测可用于一些 linter。
在正确的位置对行为进行分组并隐藏原始数据。
Dynamic Wang 在 Unsplash 上拍摄的照片
该软件的核心是它能够为用户解决与领域相关的问题。所有其他功能,尽管它们可能很重要,但都支持这个基本目的。
埃里克埃文斯
我们听说过很多关于 NFT 的信息。现在,我们掌握了 Fungible 的概念。
TL;DR:尊重MAPPER 。使现实世界中的可替代性变得可替代,反之亦然。
根据维基百科:
可互换性是一种商品或商品的属性,其各个单元本质上是可以互换的,并且每个部分都无法与另一部分区分开来。
在软件中,我们可以用其他对象替换可替代对象。
在将我们的对象与真实对象进行映射时,我们有时会忘记部分模型并构建设计。
public class Person implements Serializable { private final String firstName; private final String lastName; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } } shoppingQueueSystem.queue(new Person('John', 'Doe'));
public class Person { } shoppingQueueSystem.queue(new Person()); // The identity is irrelevant for queue simulation
这是一种语义气味。
我们需要了解模型以检查它是否正确。
让可替代的东西变得可替代,反之亦然。
听起来很简单,但需要设计技巧并避免意外的复杂性。
Andrey Metelev在Unsplash上拍摄的照片
人们认为计算机科学是天才的艺术,但实际情况恰恰相反,只是很多人在做事情,就像一堵小石头墙。
唐纳德高德纳
不要使用布尔计算作为提高可读性的捷径。
TL;DR:不要对副作用函数使用布尔比较。
聪明的程序员喜欢编写复杂和晦涩的代码,即使没有强有力的证据证明这种改进也是如此。
过早的优化总是会损害可读性。
userIsValid() && logUserIn(); // this expression is short circuit // Does not value second statement // Unless the first one is true functionDefinedOrNot && functionDefinedOrNot(); // in some languages undefined works as a false // If functionDefinedOrNot is not defined does // not raise an error and neither runs
if (userIsValid()) { logUserIn(); } if(typeof functionDefinedOrNot == 'function') { functionDefinedOrNot(); } // Checking for a type is another code smell
我们可以检查功能是否不纯并将短路更改为 IF。
一些实际的 linters 警告我们这个问题
不要试图看起来很聪明。
我们不再是 50 年代了。
成为团队开发人员。
Michael Dziedzic 在 Unsplash 上拍摄的照片
计算机是一台愚蠢的机器,却有能力做难以置信的聪明事情,而计算机程序员是聪明的人,却有能力做难以置信的愚蠢事情。简而言之,他们是绝配。
比尔布赖森
下一篇:另外 5 种代码味道。