Como desarrollador de C ++, estoy bastante acostumbrado a los archivos de encabezado de C ++, y encuentro beneficioso tener algún tipo de "documentación" forzada dentro del código. Por lo general, tengo un mal momento cuando tengo que leer un código C # debido a eso: no tengo ese tipo de mapa mental de la clase con la que estoy trabajando.
Asumamos que como ingeniero de software estoy diseñando el marco de un programa. ¿Sería una locura definir cada clase como una clase abstracta no implementada, de manera similar a lo que haríamos con los encabezados de C ++, y dejar que los desarrolladores la implementen?
Supongo que puede haber algunas razones por las cuales alguien podría encontrar que esta es una solución terrible, pero no estoy seguro de por qué. ¿Qué debería uno considerar para una solución como esta?
fuente
Respuestas:
La razón por la que esto se hizo en C ++ tuvo que ver con hacer que los compiladores sean más rápidos y fáciles de implementar. Este no fue un diseño para facilitar la programación .
El propósito de los archivos de encabezado era permitir al compilador hacer un primer paso súper rápido para conocer todos los nombres de funciones esperados y asignarles ubicaciones de memoria para que puedan ser referenciados cuando se invoquen en archivos cp, incluso si la clase que los definió tenía No se ha analizado todavía.
¡No se recomienda intentar replicar una consecuencia de las antiguas limitaciones de hardware en un entorno de desarrollo moderno!
Definir una interfaz o clase abstracta para cada clase reducirá su productividad; ¿Qué más podrías haber hecho con ese tiempo ? Además, otros desarrolladores no seguirán esta convención.
De hecho, otros desarrolladores pueden eliminar sus clases abstractas. Si encuentro una interfaz en el código que cumple estos dos criterios, la elimino y la refactorizo fuera del código:
1. Does not conform to the interface segregation principle
2. Only has one class that inherits from it
La otra cosa es que hay herramientas incluidas con Visual Studio que hacen lo que pretendes lograr automáticamente:
Class View
Object Browser
Solution Explorer
puede hacer clic en triángulos para gastar clases para ver sus funciones, parámetros y tipos de retorno.Pruebe uno de los anteriores antes de dedicar tiempo a replicar archivos de encabezado de C ++ en C #.
Además, hay razones técnicas para no hacer esto ... hará que su binario final sea más grande de lo necesario. Repetiré este comentario de Mark Benningfield:
Además, mencionado por Robert Harvey, técnicamente, el equivalente más cercano de un encabezado en C # sería una interfaz, no una clase abstracta.
fuente
Primero, comprenda que una clase puramente abstracta es realmente solo una interfaz que no puede hacer herencia múltiple.
Escribir clase, extraer interfaz, es una actividad con muerte cerebral. Tanto es así que tenemos una refactorización para ello. Lo cual es una pena. Seguir este patrón de "cada clase tiene una interfaz" no solo produce desorden, sino que pierde el punto por completo.
Una interfaz no debe considerarse simplemente como una reformulación formal de lo que la clase pueda hacer. Una interfaz debe considerarse como un contrato impuesto por el uso del código del cliente que detalla sus necesidades.
No tengo ningún problema para escribir una interfaz que actualmente solo tiene una clase que la implementa. De hecho, no me importa si ninguna clase lo implementa todavía. Porque estoy pensando en lo que necesita mi código de uso. La interfaz expresa lo que exige el código de uso. Lo que venga después puede hacer lo que quiera siempre y cuando satisfaga estas expectativas.
Ahora no hago esto cada vez que un objeto usa otro. Hago esto cuando cruzo un límite. Lo hago cuando no quiero que un objeto sepa exactamente con qué otro objeto está hablando. Cuál es la única forma en que funcionará el polimorfismo. Lo hago cuando espero que el objeto del código de mi cliente pueda cambiar. Ciertamente no hago esto cuando lo que estoy usando es la clase String. La clase String es agradable y estable y no siento la necesidad de evitar que me cambie.
Cuando decide interactuar directamente con una implementación concreta en lugar de hacerlo a través de una abstracción, predice que la implementación es lo suficientemente estable como para confiar en no cambiar.
Ahí está la forma en que atenúo el Principio de Inversión de Dependencia . No deberías aplicar esto ciegamente a todo fanáticamente. Cuando agrega una abstracción, realmente está diciendo que no confía en que la elección de implementar la clase sea estable durante la vida del proyecto.
Todo esto supone que está tratando de seguir el Principio Abierto Cerrado . Este principio es importante solo cuando los costos asociados con la realización de cambios directos al código establecido son significativos. Una de las principales razones por las que las personas no están de acuerdo sobre la importancia de los objetos de desacoplamiento es porque no todos experimentan los mismos costos al realizar cambios directos. Si volver a probar, volver a compilar y redistribuir su base de código completa es trivial para usted, entonces resolver una necesidad de cambio con modificación directa es probablemente una simplificación muy atractiva de este problema.
Simplemente no hay una respuesta cerebral a esta pregunta. Una interfaz o clase abstracta no es algo que debe agregar a cada clase y no puede simplemente contar el número de clases de implementación y decidir que no es necesario. Se trata de lidiar con el cambio. Lo que significa que estás anticipando el futuro. No te sorprendas si te equivocas. Hazlo lo más simple posible sin retroceder en una esquina.
Entonces, por favor, no escriba abstracciones solo para ayudarnos a leer el código. Tenemos herramientas para eso. Use abstracciones para desacoplar lo que necesita desacoplamiento.
fuente
Sí, eso sería terrible porque (1) introduce código innecesario (2) confundirá al lector.
Si desea programar en C #, debería acostumbrarse a leer C #. El código escrito por otras personas no seguirá este patrón de todos modos.
fuente
Pruebas unitarias
Sugeriría encarecidamente que escriba pruebas unitarias en lugar de saturar el código con interfaces o clases abstractas (excepto donde esté justificado por otros motivos).
Una prueba unitaria bien escrita no solo describe la interfaz de su clase (como lo haría un archivo de encabezado, clase abstracta o interfaz) sino que también describe, por ejemplo, la funcionalidad deseada.
Ejemplo: donde podría haber escrito un archivo de encabezado myclass.h como este:
En cambio, en C # escriba una prueba como esta:
Esta prueba muy simple transmite la misma información que el archivo de encabezado. (Deberíamos tener una clase llamada "MyClass" con una función sin parámetros "Foo") Si bien un archivo de encabezado es más compacto, la prueba contiene mucha más información.
Una advertencia: tal proceso de tener un ingeniero de software senior que proporcione pruebas (fallidas) para que otros desarrolladores resuelvan choques violentamente con metodologías como TDD, pero en su caso sería una gran mejora.
fuente