Creo que la verdadera razón por la que muchas personas sienten que las clases deberían ser final
/ sealed
es que la mayoría de las clases extensibles no abstractas no están debidamente documentadas .
Déjame elaborar. Comenzando desde lejos, existe la opinión entre algunos programadores de que la herencia como herramienta en OOP es ampliamente utilizada en exceso y abusada. Todos hemos leído el principio de sustitución de Liskov, aunque esto no nos impidió violarlo cientos (tal vez incluso miles) de veces.
La cuestión es que a los programadores les encanta reutilizar el código. Incluso cuando no es una buena idea. Y la herencia es una herramienta clave en esta "reutilización" de código. Volver a la pregunta final / sellada.
La documentación adecuada para una clase final / sellada es relativamente pequeña: usted describe lo que hace cada método, cuáles son los argumentos, el valor de retorno, etc. Todas las cosas habituales.
Sin embargo, cuando documenta correctamente una clase extensible, debe incluir al menos lo siguiente :
- Dependencias entre métodos (qué método llama a cuáles, etc.)
- Dependencias de variables locales.
- Contratos internos que la clase extendida debe cumplir
- Convención de llamada para cada método (p. Ej., Cuando lo anula, ¿llama a la
super
implementación? ¿Lo llama al principio del método o al final? Piense en constructor vs destructor)
- ...
Estos están justo en la parte superior de mi cabeza. Y puedo proporcionarle un ejemplo de por qué cada uno de estos es importante y omitirlo arruinará una clase extendida.
Ahora, considere cuánto esfuerzo de documentación debería dedicarse a documentar adecuadamente cada una de estas cosas. Creo que una clase con 7-8 métodos (que podría ser demasiado en un mundo idealizado, pero es muy poco en el mundo real) también podría tener una documentación de 5 páginas, solo texto. Entonces, en cambio, nos agachamos hasta la mitad y no sellamos la clase, para que otras personas puedan usarla, pero tampoco la documenten correctamente, ya que tomará una gran cantidad de tiempo (y, ya sabes, podría nunca se extenderá de todos modos, entonces, ¿por qué molestarse?).
Si está diseñando una clase, puede sentir la tentación de sellarla para que la gente no pueda usarla de una manera que no haya previsto (y preparado). Por otro lado, cuando está usando el código de otra persona, a veces desde la API pública no hay una razón visible para que la clase sea final, y podría pensar "Maldición, esto solo me costó 30 minutos en busca de una solución".
Creo que algunos de los elementos de una solución son:
- Primero, asegurarse de extender es una buena idea cuando es cliente del código, y favorecer realmente la composición sobre la herencia.
- Segundo, lea el manual en su totalidad (nuevamente como cliente) para asegurarse de que no esté pasando por alto algo que se menciona.
- Tercero, cuando está escribiendo un código que usará el cliente, escriba la documentación adecuada para el código (sí, a lo largo). Como ejemplo positivo, puedo dar los documentos de iOS de Apple. No son suficientes para que el usuario siempre se extienden adecuadamente sus clases, sino que incluyen al menos alguna información sobre la herencia. Lo cual es más de lo que puedo decir para la mayoría de las API.
- Cuarto, intenta extender tu propia clase para asegurarte de que funcione. Soy un gran defensor de incluir muchas muestras y pruebas en las API, y cuando realiza una prueba, también podría probar las cadenas de herencia: ¡después de todo, son parte de su contrato!
- Quinto, en situaciones en las que tenga dudas, indique que la clase no debe extenderse y que hacerlo es una mala idea (tm). Indique que no debe rendir cuentas por ese uso no intencionado, pero aún así no selle a la clase. Obviamente, esto no cubre los casos en que la clase debe estar sellada al 100%.
- Finalmente, al sellar una clase, proporcione una interfaz como un enlace intermedio, de modo que el cliente pueda "reescribir" su propia versión modificada de la clase y trabajar alrededor de su clase 'sellada'. De esta manera, puede reemplazar la clase sellada con su implementación. Ahora, esto debería ser obvio, ya que es un acoplamiento suelto en su forma más simple, pero aún vale la pena mencionarlo.
También vale la pena mencionar la siguiente pregunta "filosófica": ¿Es si una clase es sealed
/ final
parte del contrato para la clase, o un detalle de implementación? Ahora no quiero pisar allí, pero una respuesta a esto también debería influir en su decisión de sellar una clase o no.
Open/Closed principle
, no elClosed Principle.