No history yet

硬编码实例化问题

硬编码实例化的问题

在编程中,我们经常需要创建对象的实例来使用。最直接的方法就是使用 new 关键字。当我们在代码中直接写下 new ClassName() 时,这个过程就被称为“硬编码实例化”。

// 这是一个硬编码实例化的例子
public class Car {
    public void start() {
        // 启动汽车引擎
        Engine engine = new Engine(); 
        engine.turnOn();
    }
}

这种方式简单明了,在很多小程序或简单场景中完全够用。例如,在一个处理用户请求的类中,你可能会直接创建一个日志记录器对象来记录信息,或者在一个游戏的 main 函数中创建主角对象。

然而,随着项目变得越来越复杂,这种看似无害的做法会逐渐暴露出它的缺点,让代码变得脆弱和难以维护。

紧密相连的代价

硬编码实例化的主要问题在于它会造成“高耦合”。耦合度是衡量代码模块之间依赖程度的指标。高耦合意味着两个模块紧密地绑定在一起,像被强力胶水粘住了一样。修改其中一个,另一个很可能也需要跟着修改。

在上面的 Car 例子中,Car 类直接依赖于具体的 Engine 类。如果有一天,我们想给汽车换一个 ElectricEngine(电动引擎),而 ElectricEngine 的创建方式和 Engine 不一样(比如它的构造函数需要一个电池参数),那我们该怎么办?

// 新的电动引擎类
public class ElectricEngine {
    public ElectricEngine(Battery battery) {
        // ...
    }
    // ...
}

我们必须回到 Car 类的代码中,把 new Engine() 修改成 new ElectricEngine(new Battery())。如果项目里有几十个地方都这样创建了 Engine 对象,那修改起来就是一场灾难。代码的灵活性和可重用性大大降低。

Lesson image

一个棘手的例子:数据库连接

数据库操作是硬编码实例化带来麻烦的典型场景。假设我们正在开发一个应用,需要连接到 MySQL 数据库。我们可能会在数据访问类中这样写代码:

public class UserRepository {
    private MySqlConnection connection;

    public UserRepository() {
        // 直接创建了一个特定的数据库连接
        this.connection = new MySqlConnection("connection_string_for_mysql");
    }

    public User findById(int id) {
        // 使用 connection 执行查询...
        return null;
    }
}

这段代码现在可以正常工作。但是,它存在几个严重的局限性:

  1. 难以更换实现:如果客户决定从 MySQL 迁移到 PostgreSQL 怎么办?MySqlConnection 必须被替换成 PostgreSqlConnection。这意味着我们需要找到所有硬编码创建 MySqlConnection 的地方,然后逐一修改。这非常繁琐且容易出错。

  2. 测试困难:在进行单元测试时,我们通常不希望连接到真实的数据库,因为这会使测试变慢,并且依赖于外部环境。理想情况下,我们会用一个“模拟”的数据库连接来代替。但由于 UserRepository 自己创建了 MySqlConnection 实例,我们无法从外部替换它,导致测试变得非常困难。

硬编码实例化将你的代码与具体的实现细节“锁死”,使其在面对变化和测试时缺乏弹性。

简单来说,硬编码实例化虽然直接,但它牺牲了代码的灵活性和可维护性。当系统需要扩展或适应新需求时,这种做法往往会成为开发的瓶颈。