2011-01-12 16 views
5

Actualmente estoy leyendo Effective Perl Programming (2da edición). He encontrado un fragmento de código que se describió como mal escrito, pero todavía no entiendo qué tiene de malo ni cómo debería mejorarse. Sería genial si alguien pudiera explicarme el asunto.¿Qué hay de malo en acceder a DBI directamente?

Aquí está el código en cuestión:

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; 
} 

El ejemplo viene del capítulo de probar su código (p 324/325.). La frase que me ha dejado pensando en cómo mejorar el código es el siguiente:

Dado que el código fue mal escrito y accede directamente DBI, que tendrá que crear un objeto DBI falsa para sustituir a la verdadera cosa.

Probablemente no he entendido mucho de lo que el libro hasta ahora ha estado tratando de enseñarme, o me salté la sección relevante para entender qué es una mala práctica sobre el código anterior ... Bueno, gracias de antemano ¡para su ayuda!

+0

+1 Me gustaría escuchar esa respuesta, también. Parece que no puedo distinguir nada "malo" aquí ... –

+0

Todas las respuestas dadas hasta ahora han explicado el asunto muy bien, ¡así que gracias a todos por sus rápidas respuestas! De hecho, he sido bastante ciego ... – canavanin

Respuesta

8

Dado que se trata el capítulo de pruebas, considere esto:

Al probar su función, también está (implícitamente) DBI pruebas. Es por eso que es malo.

Las buenas pruebas siempre solo comprueban una funcionalidad. Para garantizar esto, se requerirá para no usar DBI directamente, pero use un objeto simulado en su lugar. De esta manera, si su prueba falla, usted sabe que es su función y no otra cosa en otro módulo (como DBI en su ejemplo).

+0

+1 para señalar de qué trata ese capítulo. De repente, esa pregunta es bastante simple. –

4

No hay nada malo con el uso de DBI por sí mismo.

La clave está en el hecho de que este es el capítulo de pruebas. Supongo que el problema que se señala es que la función abre y cierra una conexión de base de datos. En su lugar, debe esperar un identificador de base de datos como parámetro y simplemente ejecutar consultas en él, dejando cualquier preocupación sobre abrir y cerrar una conexión de base de datos a su llamador. Eso hará que el trabajo de la función sea más estrecho, por lo que hace que la función sea más flexible.

Eso a su vez también hace que la función sea más fácil de probar: simplemente páselo por un objeto simulado como identificador de base de datos. Como está escrito actualmente, necesita al menos redefinir DBI::connect para probarlo, lo cual no es difícil, pero definitivamente es complicado.

5

Creo que lo que Brian estaba tratando de decir por "mal escrito" es que no tiene una separación entre la lógica comercial y el código de acceso a datos (y la mecánica de conexión de la base de datos).

Un enfoque correcto para escribir funciones es que una función (o método) debería hacer una cosa, no 3 cosas a la vez.

Como resultado de este gran conjunto de funcionalidades, al probar, debe probar TODAS LAS TRES al mismo tiempo, lo cual es difícil (consulte la discusión sobre el uso de "prueba SQLite DB" en esos párrafos). O, como alternativa, haga a lo que se dedicaba el capítulo y fingir el objeto DBI para probar la lógica de negocios pretendiendo que el acceso a los datos y la configuración de la BD funcionaron de cierta manera.

Pero burlarse de un objeto de comportamiento complicado como DBI es muy y muy complicado de hacer bien.

¿Qué sucede si no se puede acceder a la base de datos? ¿Qué pasa si hay bloqueo? ¿Qué pasa si su consulta tiene un error de sintaxis? ¿Qué pasa si la conexión DB se agota al ejecutar la consulta? ¿Qué pasa si ...

Un buen código de prueba prueba TODAS esas situaciones de error y más.


Un enfoque más correcto (patrón) para el código sería:

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); 

De esta manera, para poner a prueba la lógica en calculate_results (por ejemplo, la suma de los datos), simplemente hay que burlarse de datos pasados a él, que es muy fácil (en muchos casos, solo almacena varios conjuntos de datos de prueba en alguna configuración de prueba); en lugar de burlarse del comportamiento de un objeto DBI complicado utilizado para recuperar los datos.

4

Un método llamado sum_values_per_key debería estar interesado en sumar los valores de algunas claves, no buscando los datos que se sumarán.

No cumple con el S (principio de responsabilidad única) de la programación SOLID. http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29

Esto significa que es a la vez:

  • no reutilizable si desea utilizar diferentes fuentes de datos.
  • Difícil de probar en un entorno sin una conexión a la base de datos.
+0

Gracias por el enlace SOLIDO. – Dallaylaen

1

1) Supongamos que tiene una docena de objetos cada uno con una docena de métodos como este. Veinte de esos métodos serán llamados durante la ejecución del programa principal. Ahora ha realizado 20 conexiones DB donde solo necesita una.

2) Supongamos que no está contento con DBI original y lo extendió con My :: DBI. Ahora debe reescribir 144 funciones en 12 archivos.

(Apache :: DBI podría ser un ejemplo aquí).

3) Tiene que llevar 3 parámetros posicionales en cada llamada a esas 144 funciones. El cerebro humano funciona bien con aproximadamente 7 objetos a la vez; acabas de vaciar casi la mitad de ese espacio. Esto hace que el código sea menos sostenible.

Cuestiones relacionadas