Resumen DAL - ¿Usar interfaz con clase interna?

8

Tenemos una capa de lógica de negocios (BLL) que está estrechamente unida a nuestra capa de acceso a datos (DAL). Hacemos llamadas como esta:

using (FooData data = new FooData())
{
  data.DoSomething();
}

Es importante tener en cuenta que todas nuestras clases de datos están internaly están en el mismo ensamblaje que las clases lógicas, de modo que solo el BLL puede acceder al DAL.

Para desacoplarlos (para ayudar a facilitar las pruebas unitarias), una idea es crear interfaces IDataClass, como IFooData. Al principio pensé que podría ser un problema porque tendríamos que hacer públicas nuestras clases de datos para implementar las interfaces. Pero deberíamos poder mantener las clases de datos internas, a pesar de que todavía necesitamos que los métodos sean públicos:

public interface IFooData
{
  void DoSomething();
}

internal class FooData : IFooData  // Notice the class is internal
{
  public void DoSomething();  // Method is public, but that's ok because class is internal
}

Entonces, aunque el método es público, dado que la clase en sí es interna, deberíamos permitir solo nuestro acceso BLL.

¿Hay algo inherentemente malo con este enfoque? ¿Existe una mejor manera de abstraer el DAL, para pruebas unitarias, sin abrir el DAL al mundo haciéndolo público?

Bob Horn
fuente
1
¿Qué estás tratando de lograr manteniendo tu DAL encerrado así? ¿Por qué mantenerlo en secreto? Expone un montón de métodos, no debería importarle exactamente quién los está llamando. Si tiene un problema con la llamada desde el lugar equivocado, entonces tiene un problema de revisión de código.
slugster
Al hacer que los métodos sean internos, solo el BLL puede acceder a él, lo que obliga a todos los desarrolladores a pasar por el BLL. Cuando tienes un gran equipo de desarrolladores, cualquier cosa que puedas hacer para minimizar los errores es algo bueno.
Bob Horn
Me doy cuenta de que estás tratando de mantener una cierta mano en los desarrolladores que usan el código, pero al hacerlo, te has creado otro problema. Por eso hice mi comentario: no hay nada de malo en hacer públicos los métodos (es una práctica normal). Si las personas los usan de manera incorrecta, entonces tiene un problema de revisión de código.
slugster
Estoy en desacuerdo. ¿Por qué entonces tener modificadores internos y privados? ¿Por qué no simplemente hacer público todo?
Bob Horn el
Bob, te has perdido totalmente el punto. El DAL expone cosas públicas a través de un CONTRATO que es su interfaz. La práctica estándar de OO significa que es ajeno a quién lo está llamando. Digámoslo una vez más: puede hacer que la clase sea interna, pero no es necesario . El resto del mundo logra sobrevivir con este enfoque, ¿por qué no tú? También te has disparado en el pie en el momento en que tienes un BL o servicio diferente que necesita llamar al mismo DAL: ¿vas a seguir agregando conjuntos de amigos?
slugster

Respuestas:

13

Extraer una interfaz y garantizar que BLL esté usando solo tipos de interfaz es una buena manera de hacer que BLL sea comprobable sin la base de datos.

Lo que estás haciendo está absolutamente bien.

Oded
fuente
3
Agregaré que lo que estás haciendo está bien, pero es solo el primer paso. Si lo que está haciendo ahora está using (IFooData data = new FooData())en su BLL, realmente no ha realizado ningún cambio, excepto la introducción de la interfaz. Ahora tendrá que encontrar una manera de crear una instancia IFooDataexterna, de lo contrario, sus pruebas BLL seguirán acopladas al DAL.
Avner Shahar-Kashtan
Convenido. Realmente estoy haciendo algo como esto ahora:DataAccessFactory.GetDataInterface<IFooData>().DoSomething();
Bob Horn
1

Puede usar el InternalsVisibleToAttributepara separar su DAL de su Business Layer, pero aún así mantener los métodos DAL internos. Terminarás creando tu DAL como un ensamblaje amigo .

Puede que este no sea un gran enfoque, ya que tendría que especificar en el Assembly.csarchivo de su DAL qué ensamblajes pueden acceder al DAL. Entonces todavía hay algo de acoplamiento involucrado (DAL sabe sobre BLL).

Entonces, para responder a su pregunta, no hay nada de malo en su enfoque. Pero aprovechar los ensamblajes de amigos puede proporcionarle un poco más de abstracción y probablemente ayudarlo a hacer que su código sea más comprobable.

Espero que esto ayude.

David Hoerster
fuente
Realmente no necesito eliminar el DAL del BLL; Solo necesito implementar una interfaz.
Bob Horn
De paso, gracias. Estoy familiarizado InternalsVisibleToy si necesito mover el DAL a otra asamblea, eso sería de gran ayuda.
Bob Horn
@BobHorn, así que no lo entiendo. ¿Cuál es el problema para hacer que tus clases de dal sean internas? Incluso puede hacer que sus interfaces DAL sean internas. No veo problemas aquí.
Marcelo De Zen
Que las clases DAL sean internas es lo que tenemos actualmente y lo que queremos tener en el futuro. Las interfaces deberán ser públicas para que el BLL pueda pedirle a una fábrica una implementación concreta, y no le importe si es el DAL real o un simulacro. Supongo que toda mi pregunta es realmente un control de cordura. ¿Me estoy perdiendo una mejor manera de hacer esto?
Bob Horn
1

Pareces inamovible con tus ideas que expusiste en tu pregunta, pero responderé de todos modos; alguien puede encontrarlo útil en algún momento en el futuro.

Si insiste en mantener su DAL interno pero aún desea obtener algunas pruebas unitarias y no le importa usar las herramientas de Pruebas unitarias de Microsoft, intente el enfoque PrivateObject . Este es un contenedor para la reflexión que siempre puede codificar usted mismo, pero le ahorra algunas líneas de escritura.

Tenga en cuenta que el uso de InternalsVisibleTo es un enfoque viable, pero es torpe: si lo está utilizando, es posible que ya esté en un rápido descenso cuesta abajo. internalsignifica que algo es público solo dentro de los límites de su ensamblaje, ese modificador se usa normalmente porque no quieres cosas que se puedan usar desde afueraesa asamblea, entonces rápidamente te das la vuelta y violas ese concepto al declarar un InternalsVisibleTo ... esa es una gran bandera roja sucia que está gritando por alguna refactorización. El desacoplamiento es una pérdida de tiempo cuando solo un ensamblaje llama al otro, también puede pegarlos. El desacoplamiento infiere que múltiples conjuntos están llamando al objetivo (la introducción de pruebas unitarias no cuenta para el 'múltiple' porque hay formas de llegar al conjunto objetivo como ya he mencionado).

slugster
fuente
¿Por qué parezco inamovible en mis ideas? Estoy dispuesto a estar de acuerdo contigo; Solo necesito una discusión para convencerme primero. Y no me digas que violo el concepto al usarlo InternalsVisibleTo. No lo estoy usando. Por alguna razón estás asumiendo que lo estoy usando. No entiendo por qué.
Bob Horn
Por cierto, aprecio tus puntos de vista porque me está haciendo reconsiderar los míos. Sin embargo, recuerde, también se me permite tener mi opinión. No hay necesidad de ponerse sarcástico porque no estoy inmediatamente de acuerdo contigo.
Bob Horn
@Bob, lo siento, me enganché a ese ángulo en particular, veo en su comentario 'y si necesito mover el DAL a otro ensamblado', es probable que su DAL esté justo donde habría sugerido que debería ser para un elemento interno. Cuando hayas decidido qué hacer, asegúrate de escribirlo aquí (si no hay respuestas adecuadas), será interesante ver lo que finalmente se te ocurre.
slugster
1

Técnicamente, su diseño no tiene nada de malo.

Sin embargo, consideraría un enfoque alternativo: en lugar de desacoplar su BLL de su DAL, desacople mejor su DAL de su base de datos. Permita la creación de objetos DAL en la memoria, y si su BLL necesita cargar objetos DAL desde algún lugar, use el patrón de repositorio (vea aquí ) para evitar el contacto directo del BLL con la base de datos. De esa manera, puedes

  • cree pruebas unitarias para sus objetos DAL fácilmente sin la necesidad de una base de datos
  • cree pruebas unitarias para su BLL al proporcionar objetos DAL creados en memoria a través de un repositorio simulado como datos de prueba (ok, ahora puede argumentar si esas pruebas realmente deberían llamarse pruebas unitarias )

De hecho, lo que pierde es la capacidad de probar el BLL con simulacros de DAL. Pero en realidad, esos objetos simulados DAL se parecerán mucho a sus objetos DAL reales para proporcionar una base para pruebas útiles del BLL. En mi humilde opinión, evita una gran cantidad de duplicación de código y un esfuerzo innecesario al reutilizar sus objetos DAL reales para pruebas automatizadas de BLL.

Doc Brown
fuente
0

El enfoque de la interfaz es bueno / general para burlarse de su DAL durante las pruebas de consumidores como el BLL.

Sin embargo, una cosa a tener en cuenta es que al hacer que sus clases de concreto DAL sean internas, es posible que esté haciendo que su DAL concreto sea más difícil de probar directamente, ya que esto ahora implica que las pruebas de la unidad DAL también deben estar en este ensamblaje sin necesidad de usar puertas traseras como reflejo

Además, en algún momento en el futuro, puede considerar el uso de un contenedor de IoC para construir la implementación detrás de una interfaz, que también podría tropezar con la interna.

Puede imitar la encapsulación que está tratando de lograr con 'solo interno' agregando una regla de convención / LINT que su BLL / otras capas deben acoplarse al DAL a través de la interfaz, no en la implementación concreta.

StuartLC
fuente
Gracias, pero las pruebas unitarias de Visual Studio pueden probar métodos internos y privados en otros ensamblados.
Bob Horn