Что не так с прямым доступом к DBI? - PullRequest
6 голосов
/ 12 января 2011

Я сейчас читаю Эффективное программирование на Perl (2-е издание). Я наткнулся на фрагмент кода, который был описан как плохо написанный, но я пока не понимаю, что в этом плохого, или как его следует улучшить. Было бы здорово, если бы кто-то мог объяснить мне этот вопрос.

Вот код, о котором идет речь:

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, чтобы заменить реальную вещь.

Вероятно, я не понял многого из того, чему книга до сих пор пыталась научить меня, или я пропустил раздел, относящийся к пониманию того, что является плохим поведением в приведенном выше коде ... Что ж, заранее спасибо за вашу помощь !

Ответы [ 5 ]

9 голосов
/ 12 января 2011

Поскольку глава посвящена тестированию, учтите следующее:

При тестировании своей функции вы также (неявно) тестируете DBI.Вот почему это плохо.

Хорошее тестирование всегда проверяет только одну функциональность.Чтобы гарантировать это, потребуется не использовать DBI напрямую, а вместо этого использовать фиктивный объект.Таким образом, если ваш тест не пройден, вы знаете, что это ваша функция, а не что-то другое в другом модуле (например, DBI в вашем примере).

6 голосов
/ 12 января 2011

Я думаю, что Брайан пытался сказать «плохо написанным», что у вас нет разделения между бизнес-логикой и кодом доступа к данным (и механикой соединения с базой данных, пока она здесь).

Правильный подход к написанию функций заключается в том, что функция (или метод) должна делать одну вещь , а не 3 вещи одновременно.

В результате этого большого количества функций, при тестировании , вам необходимо одновременно протестировать ВСЕ ТРИ ТРИ, что сложно (см. Обсуждение использования "test 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 (например, суммируя данные), вам просто нужно смоделировать передаваемые на него данные, что очень просто (во многих случаях вы просто сохраняете несколько наборов тестовых данных в некоторой конфигурации теста). ); в отличие от насмешливого поведения сложного объекта DBI, используемого для извлечения данных.

5 голосов
/ 12 января 2011

Метод с именем sum_values_per_key должен интересоваться суммированием значений некоторых ключей, а не извлечением данных для суммирования.

Он не соответствует S (принцип единой ответственности) SOLID программирования. http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29

Это означает, что это одновременно:

  • Не может использоваться повторно, если вы хотите использовать другие исходные данные.
  • Сложно проверить в среде без подключения к базе данных.
5 голосов
/ 12 января 2011

Нет ничего плохого в использовании самого DBI.

Подсказка в том, что это глава тестирования. Я предполагаю, что проблема заключается в том, что функция сама открывает и закрывает соединение с базой данных. Вместо этого он должен ожидать дескриптор базы данных в качестве параметра и просто выполнять запросы к нему, оставляя любые проблемы с открытием и закрытием соединения с базой данных для вызывающего. Это сделает работу функции более узкой, что сделает ее более гибкой.

Это, в свою очередь, также облегчает проверку функции: просто передайте ей фиктивный объект в качестве дескриптора базы данных. Как написано в настоящее время, вам нужно как минимум переопределить DBI::connect, чтобы проверить его, что не сложно, но определенно грязно.

2 голосов
/ 12 января 2011

1) Предположим, у вас есть дюжина объектов с дюжиной методов, подобных этому.Двадцать из этих методов будут вызваны во время выполнения основной программы.Вы установили 20 соединений с БД, где вам нужно только одно.

2) Предположим, вы недовольны исходным DBI и расширили его с помощью My :: DBI.Теперь вам нужно переписать 144 функции в 12 файлах.

(примером может служить Apache :: DBI).

3) Вы должны передавать 3 позиционных параметра при каждом вызове в эти 144 функции.Человеческий мозг хорошо работает с 7 объектами одновременно;Вы только что талили почти половину этого пространства.Это делает код менее поддерживаемым.

...