不健康的代码:原始代码过度使用 原始代码过度使用的识别 欺骗性布尔值 如何保持代码健康 保持联系 导航您的软件开发职业通讯

2025-06-07

不健康的代码:过度使用原始代码

原始过度使用的识别

欺骗性布尔值

如何保持代码健康

保持联系

导航你的软件开发职业通讯

经典的“代码异味”之一被称为原始过度使用。

它看似简单。


注意:这是我的书《重构 TypeScript:保持代码健康》的摘录。


重构 TypeScript 书籍


原始过度使用的识别

以此代码为例:

const email: string = user.email;

if(email !== null && email !== "") {
    // Do something with the email.
}
Enter fullscreen mode Exit fullscreen mode

注意到我们正在处理电子邮件的原始数据吗?

或者,考虑一下:

const firstname = user.firstname || "";
const lastname = user.lastname || "";
const fullName: string = firstname + " " + lastname;
Enter fullscreen mode Exit fullscreen mode

注意到所有额外的检查,以确保用户名不被篡改了吗null?你肯定见过这样的代码。

这里出了什么问题?

这段代码有什么问题?有几点需要思考:

  • 这种逻辑是不可共享的,因此会在各处被复制

  • 在更复杂的场景中,很难看出底层业务概念代表什么(这导致代码难以理解)

  • 如果存在一个潜在的商业概念,那么它是隐含的,而不是明确的

偶然的商业理念

上述代码示例中的业务概念类似于用户的显示名称全名

然而,这个概念只是暂时存在于一个恰好被正确命名的变量中。它会在其他地方被同样命名吗?如果你的团队里还有其他开发人员——很可能不会

我们的代码从业务角度来看可能难以掌握,在复杂场景中难以理解,并且无法与应用程序中的其他地方共享。

我们该如何处理这个问题?

欺骗性布尔值

原始类型应该是我们在代码中创建更有用的面向业务的概念/抽象的构建块。

这有助于每个特定的业务概念将其所有逻辑集中在一个地方(这意味着我们可以更轻松地共享和推理它),实现更强大的错误处理,减少错误等。

我想看看我经历过的导致原始过度使用的最常见原因。我经常看到这种情况。

设想

假设我们正在开发一个网络应用程序,帮助客户在线销售二手物品。

我们被要求在系统中验证用户身份的部分添加一些额外的规则。

目前,系统仅检查用户是否已成功验证。

const isAuthenticated: boolean = await userIsAuthenticated(username, password);

if(isAuthenticated) {
    redirectToUserDashboard();
} else {
    returnErrorOnLoginPage("Credentials are not valid.");
}
Enter fullscreen mode Exit fullscreen mode

新商业规则

我们公司现在要求我们检查用户是否处于活跃状态。不活跃的用户将无法登录。

许多开发人员会做这样的事情:

const user: User = await userIsAuthenticated(username, password);
const isAuthenticated: boolean = user !== null;

if(isAuthenticated) {
    if(user.isActive) {
        redirectToUserDashboard();
    } else {
        returnErrorOnLoginPage("User is not active.");
    }
} else {
    returnErrorOnLoginPage("Credentials are not valid.");
}
Enter fullscreen mode Exit fullscreen mode

哦不!我们引入了代码异味,我们知道这会导致可维护性问题!

我们现在有一些空检查和嵌套条件(它们都是不健康代码的标志,在《重构 TypeScript》一书中有所提及。)

因此,让我们首先通过应用(a)特殊情况模式和(b)保护子句来重构它(这两种技术在书中都有详细解释。)

// This will now always return a User, but it may be a special case type
// of User that will return false for "user.isAuthenticated()", etc.
const user: User = await userIsAuthenticated(username, password);

// We've created guard clauses here.
if(!user.isAuthenticated()) {
    returnErrorOnLoginPage("Credentials are not valid.");
}

if(!user.isActive()) {
    returnErrorOnLoginPage("User is not active.");
}

redirectToUserDashboard();
Enter fullscreen mode Exit fullscreen mode

好多了。

更多规则...

现在您的经理已经看到您能够多快地添加新的业务规则,他们还需要一些其他规则。

  1. 如果用户的会话已经存在,则将用户发送到特殊的主页。

  2. 如果用户由于登录尝试次数过多而锁定了他们的帐户,则将他们发送到一个特殊页面。

  3. 如果这是用户首次登录,则将他们发送到特殊的欢迎页面。

哎呀!

如果您已经在这个行业工作了几年,那么您就会知道这种情况有多么普遍!

乍一看,我们可能会做一些天真的事情:

// This will now always return a User, but it may be a special case type
// of User that will return false for "user.isAuthenticated()", etc.
const user: User = await userIsAuthenticated(username, password);

// We've created guard clauses here.
if(!user.isAuthenticated()) {
    returnErrorOnLoginPage("Credentials are not valid.");
}

if(!user.isActive()) {
    returnErrorOnLoginPage("User is not active.");
}

if(user.alreadyHadSession()) {
    redirectToHomePage();
}

if(user.isLockedOut()) {
    redirectToUserLockedOutPage();
}

if(user.isFirstLogin()) {
    redirectToWelcomePage();
}

redirectToUserDashboard();
Enter fullscreen mode Exit fullscreen mode

注意到了吗,因为我们引入了保护子句,所以在这里添加新逻辑变得容易多了?这是提高代码质量的一大好处——它能让将来的代码修改和添加新逻辑变得更容易。

但是,在这种情况下,存在一个问题。你能发现吗?

我们的User课程正在成为所有身份验证逻辑的垃圾场。

真的那么糟糕吗?

有那么糟糕吗?是的。

想想看:你的应用中还有哪些地方需要这些数据?没有地方——都是身份验证逻辑。

一种重构方法是创建一个名为的新类,AuthenticatedUser并在该类中只放置与身份验证相关的逻辑。

这将遵循单一责任原则。

但是,针对这个特定场景,我们可以采取更简单的解决方法。

只使用枚举

每当我看到这种模式(方法的结果是一个布尔值,或者是一个具有布尔值的对象,这些布尔值会被立即检查/测试)时,用枚举替换布尔值是一种更好的做法。

从上面的最后一个代码片段中,让我们将方法更改userIsAuthenticated为更准确地描述我们正在尝试做的事情:tryAuthenticateUser

并且,我们不会返回 aboolean或 a User- 我们将返回一个枚举,告诉我们确切的结果是什么(因为这就是我们感兴趣的)。

enum AuthenticationResult {
    InvalidCredentials,
    UserIsNotActive,
    HasExistingSession,
    IsLockedOut,
    IsFirstLogin,
    Successful
}
Enter fullscreen mode Exit fullscreen mode

我们的新枚举将指定尝试验证用户的所有可能结果。

接下来,我们将使用该枚举:

const result: AuthenticationResult = await tryAuthenticateUser(username, password);

if(result === AuthenticationResult.InvalidCredentials) {
    returnErrorOnLoginPage("Credentials are not valid.");
}

if(result === AuthenticationResult.UserIsNotActive) {
    returnErrorOnLoginPage("User is not active.");
}

if(result === AuthenticationResult.HasExistingSession) {
    redirectToHomePage();
}

if(result === AuthenticationResult.IsLockedOut) {
    redirectToUserLockedOutPage();
}

if(result === AuthenticationResult.IsFirstLogin) {
    redirectToWelcomePage();
}

if(result === AuthenticationResult.Successful) {
    redirectToUserDashboard();
}
Enter fullscreen mode Exit fullscreen mode

注意到这样可读性提高了吗?而且,我们不再User用一堆不必要的额外数据来污染我们的类了!

我们返回一个值。这是简化代码的好方法。

这是我最喜欢的重构之一!希望你也会觉得它有用。

奖励:策略模式

每当我使用这种重构时,我就会自然而然地知道策略模式可能会对我们提供更多帮助。

想象一下上面的代码有更多的业务规则和路径。

我们可以使用策略模式的一种形式进一步简化它:

const strategies: any = [];

strategies[AuthenticationResult.InvalidCredentials] = 
    () => returnErrorOnLoginPage("Credentials are not valid.");
strategies[AuthenticationResult.UserIsNotActive] = 
    () => returnErrorOnLoginPage("User is not active.");
strategies[AuthenticationResult.HasExistingSession] = 
    () => redirectToHomePage();
strategies[AuthenticationResult.IsLockedOut] = 
    () => redirectToUserLockedOutPage();
strategies[AuthenticationResult.IsFirstLogin] = 
    () => redirectToWelcomePage();
strategies[AuthenticationResult.Successful] = 
    () => redirectToUserDashboard();

strategies[result]();
Enter fullscreen mode Exit fullscreen mode

如何保持代码健康

这篇文章摘自《重构 TypeScript》,它旨在成为一种平易近人且实用的工具,帮助开发人员更好地构建高质量的软件。

重构 TypeScript 书籍

保持联系

不要忘记通过以下方式与我联系:

导航你的软件开发职业通讯

一封电子邮件简报,助您提升软件开发职业水平!您是否想过:

✔ 软件开发人员通常经历哪些阶段?
✔ 我如何知道自己处于哪个阶段?如何进入下一个阶段?
✔ 什么是技术领导者?如何成为技术领导者?
✔ 有人愿意陪伴我并解答我的疑问吗?

听起来很有趣?加入社区吧!

文章来源:https://dev.to/jamesmh/unhealthy-code-primitive-overuse-7mh
PREV
What Are The Highest Paying Software Developer Jobs & How Can I Land One? Let's Talk About Real-World Salaries Real-World Salary Calculations More Considerations What Does Work Then? Thought Experiment To Drive This Home Leverage Points What Are Some Common Leverage Points? In The End Keep In Touch Navigating Your Software Development Career Newsletter AWS GenAI LIVE!
NEXT
功能导向的代码组织,改变人生(并节省时间!)的魔力!让我来告诉你一个故事:典型的应用程序是如何构建的?以及为什么这会成为一个问题。一个更好的代码组织和维护方法:你的应用程序实际功能与实现方式。其他优势:后续步骤:规划你的软件开发职业生涯