Skip to content

重构 C# 代码 - 4 种基本技术简化版

Published: at 12:00 AM

重构 C# 代码 - 4 种基本技术简化版

摘要

通过这 4 种简单的技术学习如何高效地重构 C# 代码!通过代码示例随时了解重构方法。

原文链接


我已经写了 20 多年的代码,一路走来,我写了大量我需要回过头来重构的代码。这种情况会发生。实际上,如果您想在开发过程中保持代码库的活力,几乎可以肯定。因此,我进行了大量的 C# 代码重构,我想与您分享一些简单的重构技术,这些技术如果您刚开始学习可能特别有用。


1 - 提取方法

对于软件工程师来说,重构代码是一项重要技能,因为它有助于提高我们代码库的可读性和可维护性。一个可以大大帮助我们实现这一目标的技术是提取方法(Extract Method)技术。我使用这个技术如此频繁,以至于几乎我写了些代码立马就这样做了!

提取方法的概念

提取方法技术涉及将某个方法内的代码块提取到一个单独的方法中。通过这样做,我们可以通过将代码分解为更小、更易管理的部分来提高代码的清晰度和可理解性。千万别等到您有一个 2000 行的方法后,您才弄明白这样做的益处!

为了演示这种技术,考虑以下 C# 中的例子:

public class Calculator
{
    public int Add(int a, int b)
    {
        Console.WriteLine($"Adding {a} and {b}...");
        int result = a + b;
        Console.WriteLine("The result is: " + result);
        return result;
    }
}

在上面的代码中,Add 方法负责:

这是一个非常简单的方法,但它已经做了一些事。通过应用提取方法技术,我们可以将关注点分离出来,创建一个新方法,专门负责显示信息。

public class Calculator
{
    public int Add(int a, int b)
    {
        DisplayOperationInfo(a, b);
        int sum = a + b;
        DisplaySum(sum);
        return sum;
    }

    private void DisplayOperationInfo(int a, int b)
    {
        // TODO: 我们能否走得更远,传入
        // 操作字符串?!
        Console.WriteLine($"Adding {a} and {b}...");
    }

    private void DisplayResult(int result)
    {
        Console.WriteLine("The result is: " + result);
    }
}

在这种特殊情况下,这种技术没有帮助我们减少现有代码的重复(现在),但它确实帮助我们为代码带来了一些组织。考虑到这是一个非常简单的例子,真正的好处很难观察到——我的意思是,我增加了更多的代码来实现这一点。然而,我试图说明我们可以概念性地将代码分组到方法中,以更好的表达我们试图实现的目标。展示超级简单例子的难点就在这里!

提取方法的好处

使用提取方法(Extract Method)技术提供了几个好处。首先,它通过创建更小、自解释的方法来提高了代码的可读性,这些方法专注于单一责任。这使得其他开发人员更容易理解和维护代码。这正是我在之前的示例代码中试图说明的。

其次,它促进了代码可重用性。通过将代码块提取到一个单独的方法中,我们可以在代码库的多个地方轻松地重用它。这减少了重复并执行了 DRY(Don’t Repeat Yourself)原则。如果我们要为我们的计算器添加其他运算符,我们可以重新使用 DisplayResult 方法,甚至可以重构 DisplayOperationInfo 方法,传入一个参数来指定我们正在执行哪种运算!

提取方法在何种场景下有用

提取方法技术特别适用于以下场景:

  1. 方法中的代码块变得过长和复杂——通过将代码提取到一个单独的方法中,我们可以使原方法更简洁,更容易理解。
  2. 需要在不同方法中重用常见功能——将常见功能提取到一个单独的方法中可以促进代码重用,消除重复。
  3. 一个方法负责多重关注点——通过将方法的特定部分提取到单独的方法中,我们可以提高代码的清晰度和可维护性。

提取方法技术是一种基本的重构技术,一旦您开始使用,它几乎成为您编码方式的第二天性。通过将复杂的方法分解为更小、更专注的方法,我们可以创建更易于理解和维护的代码。


2 – 重命名变量和方法

在软件工程中,清晰和描述性的命名非常重要,因为它们提高了代码可读性和可维护性。我们程序员的很多时间都花在阅读代码上,因此理解如何编写表达力和简洁性都很强的代码是至关重要的。在命名变量和方法时,选择准确反映其目的和功能的名称非常重要。

但猜猜怎么着?事情改变和发展。您今天选的很好的名称可能在 12 个月后不会那么好,当您试图阅读您的代码并理解发生了什么时。这很酷,因为不仅仅是因为它显示您正在成长和改进,而是因为我们拥有可以轻松做出这种更改的工具。

清晰和描述性命名的重要性

使用清晰和描述性的名称来命名变量和方法,使其他开发者能够迅速理解它们的目的及如何使用。这不仅提高了代码的可读性,还使其更易于维护和调试。

考虑以下示例:

int x = 10;
int y = 5;
int z = x + y;

在这段代码片段中,变量 xyz 的目的并不是立即清晰。通过将变量重命名为更具描述性的名称,我们可以使代码更加自解释:

int dollarsFromJack = 10;
int dollarsFromJill = 5;
int totalSpendingMoney = dollarsFromJack + dollarsFromJill;

现在,很明显 dollarsFromJackdollarsFromJill 代表正在相加的数字,totalSpendingMoney 代表它们的结果-而且我们得到了一些为什么我们使用这些数字的上下文。

使用 Visual Studio 进行重命名

重命名变量和方法时,重要的是遵循一致的命名约定。在 C# 中,通常的做法是对方法名使用 PascalCase(帕斯卡命名法)对变量名使用 camelCase(驼峰命名法)。但是如果你选了一个糟糕的名字又会怎样?如果您忘记了命名约定,并需要在 100 个地方更新某些东西的使用,又会怎样?

在 Visual Studio 中,我们可以使用重命名重构的快捷方式。默认情况下,这在您的键盘上是 F2。只需:

您可以重复这个过程来做许多事情,如类、接口、方法名……任何事情都有一个名称!(有意的双关语?)

重命名事物是我们可以做的最安全的一种重构类型 如果(这是一个非常重要的如果)你在消费代码的所有地方都在重命名。所以对于很小的项目来说,尤其是如果你不由名字动态加载任何类型,也不大量使用反射,你可能还好,如果没有人消费你的库。但是如果你正在创建一个公共 API,或者无法同时更新所有的消费代码路径,那么你可能会破坏事物。

命名约定和最佳实践

命名约定最终将取决于可能因团队而异的风格选择。然而,有几个通用的原则适用于考虑你如何命名:

  1. 使用描述性的名称,准确地反映变量或方法的目的。
  2. 避免使用单字符或含糊不清的名称,因为它们会使代码更难理解。
  3. 考虑使用名词用于变量和动词用于方法,以清楚它们的目的。
  4. 避免使用保留词或常见语言结构作为名称。
  5. 考虑减少名称中的冗余。

记得与您的团队讨论他们在命名方面的价值观及遵循的风格。


3 - 提取接口

提取接口重构技术是我在自己的开发中严重依赖的另一件事。它涉及创建一个定义一组方法或属性契约的接口,然后从现有代码中提取该接口。这项技术可以帮助增加模块化,提高代码可重用性,并增强可测试性。我通常从类本身开始,这样我可以专注于我需要输入什么和我可以提供什么,我发现这有助于我为接口提出一个更好的 API 契约。

让我们考虑一个例子,我们有一个名为 Calculator 的类,执行各种数学运算:

public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Subtract(int a, int b)
    {
        return a - b;
    }

    public int Multiply(int a, int b)
    {
        return a * b;
    }

    public int Divide(int a, int b)
    {
        return a / b;
    }
}

在这种情况下,我们可以提取一个接口,称之为 ICalculator,它将定义这些数学运算的契约:

public interface ICalculator
{
    int Add(int a, int b);

    int Subtract(int a, int b);

    int Multiply(int a, int b);

    int Divide(int a, int b);
}

一旦我们有了接口,我们就可以修改我们的 Calculator 类来实现它:

public class Calculator : ICalculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Subtract(int a, int b)
    {
        return a - b;
    }

    public int Multiply(int a, int b)
    {
        return a * b;
    }

    public int Divide(int a, int b)
    {
        return a / b;
    }
}

现在,我们可以通过使用 ICalculator 来编程到接口,而不是直接使用 Calculator 类:

public void Calculate(ICalculator calculator)
{
    int result = calculator.Add(5, 3);
    // 对结果做一些事情
}

是的 - 我知道这是一个简单的例子,界面对此可能过于杀鸡用牛刀。然而,请记住,我在这些示例开头包括的计算器?那个写入控制台的?是的,如果你想换掉一个不至于炸掉你的控制台输出的东西,至少你可以了!但严肃地讲,这只是为了向初学者说明,接口定义了一个 API,而类具有实现。

提取接口的优点

在软件设计和开发中使用接口有几个优点:

您应该何时使用提取接口技术?当多个类共享一组通用方法或属性时,此技术特别有用。通过提取一个接口,您可以确保这些类之间的一致性,并简化它们的使用。

当然,如果你像我一样编程,你可能过度使用了接口。有时候,为类打上一个界面真的没有什么好处。真的。我是那种喜欢在 单元测试时模拟任何依赖关系(我有其他测试用于真正的依赖关系) 的人,我 依然 不认为把一个界面用于任何事都是有意义的。所以用你的判断力!


4 – 简化条件表达式

编写代码时,清晰且简洁的条件表达式非常重要。复杂的条件逻辑可能会导致混淆,使代码更难以阅读和维护。当条件表达式太长时,它们很难理解。当条件表达式太简洁并且使用了难以理解的缩写或单字符变量时,它们也难以理解。所以,找到适当的冗长度来清晰表达你的意图是很重要的。

使用三元运算符:

三元运算符是一种编写简单条件表达式的简洁方法。它的形式是 condition ? trueExpression : falseExpression。通过使用三元运算符,你可以用单行代码替换 if-else 语句。这里有一个例子:

bool isEven = (number % 2 == 0) ? true : false;

什么不是好玩的?那就是将这些重叠在一起。尽量避免这样做。

将复杂条件提取到独立的布尔变量中:

如果您有包含多个逻辑运算符的复杂条件,一眼看出其含义可能会很有挑战性。为了提高可读性,请考虑将条件提取到具有描述性名称的单独布尔变量中。这里有一个例子:

bool hasEnoughStock = (quantity > 0 && stockCount > 0);
bool isEligibleForDiscount = (customerAge >= 60 || isEmployee);
if (hasEnoughStock && isEligibleForDiscount)
{
    // 在这里应用折扣逻辑
}

与尝试直接在 if 语句中放置所有四个条件相比,这段代码容易得多,尤其是因为我们通过清晰的变量名称得到了额外的上下文!

使用 Null-条件运算符:

Null-条件运算符(?.)是在 C# 6.0 中引入的一个实用功能。它允许你安全地访问对象的成员,即便该对象本身为 null。这个运算符可以通过避免不必要的 if 语句来简化检查 null 的代码。这里有一个例子:

// 如果 'text' 为 null,则结果为 null
int? length = text?.Length;

这一点与您的团队进行讨论是很好的。这里的简写可能对人们来说不是很明显,特别是如果他们不习惯在代码库中使用这种语法的话。


常见问题解答:重构 C# 代码的技术

为什么在软件工程中重构很重要?

重构在软件工程中很重要,因为它提高了代码的可读性、可维护性和整体软件质量。它有助于消除代码异味,减少技术债务,并且使添加新功能或修复缺陷更容易。

使用 Extract Method 重构技术的一些好处是什么?

Extract Method 技术允许代码重用,通过将复杂的方法分解成更小、更专注的方法来提高可读性,并使得更容易推理和测试代码。

重命名变量和方法如何提升代码质量?

清晰和描述性的命名增强了代码的可读性,使代码更容易理解和维护。重命名变量和方法提高了代码沟通性,减少了含糊不清,并有助于与编码标准保持一致。

何时在重构 C# 代码时应该使用 Extract Interface 技术?

当多个类共享通用行为,或当你想要从客户端解耦实现细节时,Extract Interface 技术很有用。它促进了松耦合,提高了可测性,并使实现的替换变得容易。

重构 C# 代码时,如何简化条件表达式?

通过使用三元运算符、switch 语句或将复杂条件提取到单独的变量或方法中,可以简化条件表达式。这有助于提高代码可读性,减少逻辑错误的机会,并增强可维护性。