【问题标题】:What is wrong with accessing DBI directly?直接访问 DBI 有什么问题?
【发布时间】:2011-06-07 18:33:51
【问题描述】:

我目前正在阅读Effective Perl Programming(第二版)。我遇到了一段被描述为写得很糟糕的代码,但我还不明白它有什么不好的地方,或者应该如何改进它。如果有人能向我解释这件事,那就太好了。

这是有问题的代码:

sub sum_values_per_key {
   my ( $class, $dsn, $user, $password, $parameters ) = @_;
   my %results;

   my $dbh = 
     DBI->connect( $dsn, $user, $password, $parameters );

   my $sth = $dbh->prepare(
     'select key, calculate(value) from my_table');
   $sth->execute();

   # ... fill %results ...

   $sth->finish();
   $dbh->disconnect();

   return \%results;
}

该示例来自测试代码一章(第 324/325 页)。让我想知道如何改进代码的一句话如下:

由于代码写得不好并且直接访问 DBI,你必须创建一个假的 DBI 对象来代替真实的东西。

到目前为止,我可能还没有理解这本书试图教给我的很多内容,或者我跳过了与理解上述代码的不良做法相关的部分......好吧,提前感谢您的帮助!

【问题讨论】:

  • +1 我也想听听这个答案。我似乎无法在这里找出任何“可怜”的东西......
  • 到目前为止给出的所有答案都很好地解释了这个问题,所以感谢大家的快速回复!我确实是瞎了眼……

标签: perl unit-testing coding-style dbi


【解决方案1】:

由于这一章是关于测试的,所以考虑一下:

在测试您的功能时,您也在(隐式)测试 DBI。这就是它不好的原因。

良好的测试总是只检查一项功能。为了保证这一点,需要 不直接使用 DBI,而是使用模拟对象。这样,如果您的测试失败,您 知道这是您的功能,而不是另一个模块中的其他功能(例如您的示例中的 DBI)。

【讨论】:

  • +1 用于指出该章的内容。这个问题突然变得很简单了。
【解决方案2】:

我认为 Brian 试图通过“写得不好”来表达的是,您没有将业务逻辑和数据访问代码(以及数据库连接机制,同时在其中)分开。

编写函数的正确方法是函数(或方法)应该做一件事,而不是一次做三件事。

由于这一大块功能,在测试时,您必须同时测试所有三个,这很困难(请参阅那些段落中关于使用“测试 SQLite DB”的讨论) )。或者,作为替代方案,做本章专门介绍的内容,并模拟 DBI 对象以通过假装数据访问和数据库设置以某种方式工作来测试业务逻辑。

但是模拟像 DBI 这样行为复杂的对象是非常非常复杂的。

如果无法访问数据库怎么办?如果有阻塞怎么办?如果您的查询有语法错误怎么办?如果执行查询时数据库连接超时怎么办?万一……

良好的测试代码可以测试所有这些错误情况等等。


更正确的代码方法(模式)是:

my $dbh = set_up_dbh();
my $query = qq[select key, calculate(value) from my_table];
my $data = retrieve_data($dbh, $query);
    # Now, we don't need to test setting up database connection AND data retrieval
my $calc_results = calculate_results($data);

这样,要测试calculate_results中的逻辑(例如对数据求和),您只需要模拟传递给它的DATA,这非常容易(在许多情况下,您只需在一些测试配置中存储几组测试数据);而不是模拟用于检索数据的复杂 DBI 对象的行为。

【讨论】:

    【解决方案3】:

    单独使用 DBI 没有任何问题。

    线索在于这是测试章节。我认为要指出的问题是该函数本身打开和关闭数据库连接。相反,它应该期望一个数据库句柄作为参数并只对其运行查询,而将任何关于打开和关闭数据库连接的问题留给它的调用者。这会使函数的工作范围更窄,从而使函数更灵活。

    这反过来也使函数更易于测试:只需将模拟对象作为数据库句柄传递给它。按照目前的写法,你至少需要重新定义DBI::connect来测试它,这并不难,但肯定是乱七八糟的。

    【讨论】:

      【解决方案4】:

      一个名为sum_values_per_key 的方法应该对某些键的值求和,而不是获取要求和的数据。

      不符合SOLID编程的S(单一责任原则)。 http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29

      这意味着它是两者:

      • 如果您希望使用不同的源数据,则不可重复使用。
      • 很难在没有数据库连接的环境中进行测试。

      【讨论】:

        【解决方案5】:

        1) 假设您有十几个对象,每个对象都有十几个这样的方法。其中 20 个方法将在主程序执行期间被调用。您现在已经建立了 20 个 DB 连接,而您只需要一个。

        2) 假设您对原始 DBI 不满意,并使用 My::DBI 对其进行了扩展。您现在必须在 12 个文件中重写 144 个函数。

        (Apache::DBI 可能是这里的一个例子)。

        3) 每次调用这 144 个函数时,您必须携带 3 个位置参数。人脑一次可以很好地处理大约 7 个物体;你刚刚缩小了那个空间的一半。这使得代码的可维护性降低。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-07-16
          • 2020-04-19
          • 1970-01-01
          • 1970-01-01
          • 2020-07-02
          • 2012-02-07
          • 2011-09-10
          • 2014-05-17
          相关资源
          最近更新 更多