remarks cref c# java .net oop

c# - cref - ¿Debo recomendar el sellado de clases por defecto?



remarks c# (13)

"... considerar [ing] cómo deben clasificarse sus clases ..." no debería importar.

Al menos media docena de veces en los últimos años me he encontrado maldiciendo a un equipo de código abierto u otro por una mezcla descuidada de protección y privacidad, por lo que es imposible simplemente extender una clase sin copiar la fuente de toda la clase principal. (En la mayoría de los casos, anular un método en particular requiere acceso a miembros privados).

Un ejemplo fue una etiqueta JSTL que casi hizo lo que yo quería. Necesito anular una pequeña cosa. No, lo siento, tuve que copiar completamente la fuente del padre.

En un gran proyecto para el que trabajo, estoy considerando recomendar a otros programadores que siempre sellen sus clases si no han considerado cómo deberían clasificarse sus clases. Muchas veces los programadores menos experimentados nunca consideran esto.

Encuentro extraño que en java y c # classed no estén sellados / no sean predeterminados. Creo que hacer clases selladas mejora en gran medida la legibilidad del código.

Tenga en cuenta que este es el código de la casa que siempre podemos cambiar en caso de que se produzca un caso raro de que necesitemos una subclase.

¿Cuáles son tus experiencias? Encuentro bastante resistencia a esta idea. ¿Las personas que son perezosas no pueden molestarse en escribir "selladas"?


De acuerdo, como tantas otras personas han pesado en ...

Sí, creo que es completamente razonable recomendar que las clases estén selladas por defecto.

Esto va junto con la recomendación de Josh Bloch en su excelente libro Effective Java, 2da edición :

Diseñe para herencia, o prohíba.

Diseñar para la herencia es difícil y puede hacer que su implementación sea menos flexible, especialmente si tiene métodos virtuales, uno de los cuales llama al otro. Tal vez son sobrecargas, tal vez no lo son. El hecho de que uno llame al otro debe estar documentado; de lo contrario, no puede anular ninguno de los métodos de manera segura: no sabe cuándo se lo llamará, o si puede llamar al otro método sin correr el riesgo de un desbordamiento de la pila.

Ahora, si luego quiere cambiar qué método llama y cuál en una versión posterior, no puede - potencialmente romperá las subclases. Entonces, en nombre de la "flexibilidad", en realidad ha hecho que la implementación sea menos flexible y haya tenido que documentar los detalles de su implementación más de cerca. Eso no me parece una gran idea.

Lo siguiente es la inmutabilidad: me gustan los tipos inmutables. Los considero más fáciles de razonar que los tipos mutables. Es una de las razones por la cual Joda Time API es más agradable que usar Date and Calendar en Java. Pero nunca se puede saber que una clase no sellada es inmutable. Si acepto un parámetro de tipo Foo , puedo confiar en que las propiedades declaradas en Foo no se modifiquen con el tiempo, pero no puedo confiar en que el objeto en sí no se modifique; podría haber una propiedad mutable en el subclase. Que Dios me ayude si esa propiedad también es utilizada por una anulación de algún método virtual. Despídase de muchos de los beneficios de la inmutabilidad. (Irónicamente, Joda Time tiene jerarquías de herencia muy grandes, a menudo con elementos que dicen "las subclases deben ser inmutables. La gran jerarquía de herencia de Chronology hace que sea difícil de entender cuando se transfiere a C #.)

Finalmente, está el aspecto del uso excesivo de la herencia. Personalmente prefiero la composición sobre la herencia cuando sea posible. Me encanta el polimorfismo para las interfaces, y ocasionalmente uso la herencia de la implementación, pero rara vez encaja perfectamente en mi experiencia. Hacer las clases selladas evita que se deriven de forma inapropiada de donde la composición sería un mejor ajuste.

EDITAR: También me gustaría señalar a los lectores en la publicación del blog de Eric Lippert de 2004 sobre por qué muchas de las clases marco están selladas. Hay muchos lugares en los que desearía que .NET proporcionara una interfaz con la que pudiéramos trabajar para la capacidad de prueba, pero esa es una solicitud ligeramente diferente ...


El propósito principal de una clase sellada es quitarle la característica de herencia al usuario para que no pueda derivar una clase de una clase sellada. ¿Está seguro de que quiere hacer eso? ¿O quieres empezar a tener todas las clases como selladas y luego cuando necesites hacerlas heredables las cambiarás? Bueno, eso podría estar bien cuando todo esté en casa y en un equipo, pero en el caso de que otros equipos usen tus dlls en el futuro no será posible recompilar el código fuente completo cada vez que una clase deba ser abierta ... No lo recomendaré, pero esa es solo mi opinión


En mi experiencia, creo que las clases selladas / finales son bastante raras. Ciertamente, no recomendaría sugerir que todas las clases sean selladas / definitivas por defecto. Esa especificación hace una determinada afirmación sobre el estado del código (es decir, que está completo) que no necesariamente es siempre cierto durante el tiempo de desarrollo.

También agregaré que dejar una clase sin sellar requiere más esfuerzo de diseño / prueba para asegurarme de que los comportamientos expuestos estén bien definidos y probados; la prueba de unidad pesada es crítica, IMO, para lograr un nivel de confianza en el comportamiento de la clase que parece ser deseado por los proponentes de "sellado". Pero la OMI, ese mayor nivel de esfuerzo se traduce directamente en un alto nivel de confianza y en un código de mayor calidad.


En mi opinión, las decisiones de diseño arquitectónico se toman para comunicar a otros desarrolladores (incluidos futuros desarrolladores de mantenimiento) algo importante.

Las clases de sellado comunican que la implementación no debe ser anulada. Comunica que la clase no debe ser suplantada. Hay buenas razones para sellar.

Si toma el enfoque inusual de sellar todo (y esto es inusual), entonces sus decisiones de diseño ahora comunican cosas que realmente no son importantes, como que la clase no fue pensada para ser heredada por el desarrollador original / autor.

Pero ¿cómo le comunicaría a otros desarrolladores que la clase no debería ser heredada por algo? Realmente no puedes. Estás atascado.

Además, sellar una clase no mejora la legibilidad. Simplemente no veo eso. Si la herencia es un problema en el desarrollo de OOP, entonces tenemos un problema mucho más grande.


Esta es una pregunta muy obstinada que probablemente genere respuestas muy dogmáticas ;-)

Dicho esto, en mi opinión, prefiero NO hacer que mis clases sean selladas / definitivas, especialmente al principio. Hacer esto hace que sea muy difícil inferir los puntos de extensibilidad previstos, y es casi imposible obtenerlos desde el principio. En mi humilde opinión, el uso excesivo de la encapsulación es peor que el uso excesivo de polimorfismo.


Francamente, creo que las clases que no están selladas por defecto en c # son algo extrañas y fuera de lugar con la forma en que el resto de los valores predeterminados funcionan en el idioma.

Por defecto, las clases son internal . Por defecto, los campos son private . Por defecto los miembros son private .

Parece haber una tendencia que apunta al acceso menos plausible por defecto. Sería lógico pensar que una palabra clave unsealed debería salir en c # en lugar de ser sealed .

Personalmente prefiero que las clases estén selladas por defecto. En la mayoría de las ocasiones, cuando alguien escribe una clase, no la está diseñando teniendo en cuenta las subclases y todas las complejidades que la acompañan. Diseñar para futuras subclases debe ser un acto consciente y, por lo tanto, prefiero que explícitamente lo menciones.


Me gustaría pensar que soy un programador con experiencia razonable y, si no he aprendido nada más, es que soy notablemente malo en predecir el futuro.

Escribir sellado no es difícil, simplemente no quiero irritar a un desarrollador en el futuro (¿quién podría ser yo?) Que descubre que un problema podría resolverse fácilmente con un poco de herencia.

Tampoco tengo idea de cómo sellar una clase lo hace más legible. ¿Estás tratando de obligar a las personas a preferir la composición a la herencia?


No debería haber nada malo en heredar de una clase.

Deberías sellar una clase solo cuando haya una buena razón por la cual nunca debería ser heredada.

Además, si los sella a todos, solo disminuirá el mantenimiento. Cada vez que alguien quiera heredar de una de sus clases, verá que está sellada, luego eliminará el sello (un lío con código con el que no debería meterse) o algo peor: creará una implementación deficiente de su clase para sí mismo.

Entonces tendrás 2 implementaciones de la misma cosa, una probablemente peor que la otra, y 2 piezas de código para mantener.

Mejor solo mantenerlo sin sellar. No hay daño en que se abra.


No me gusta esa manera de pensar. Java y c # están hechos para ser lenguajes OOP. Estos idiomas están diseñados de manera que una clase puede tener un padre o un hijo. Eso es.

Algunas personas dicen que siempre debemos comenzar desde el modificador más restrictivo (privado, protegido ...) y configurar su miembro en público solo cuando lo usa externamente. Estas personas son, para mí, perezosas y no quieren pensar en un buen diseño al comienzo del proyecto.

Mi respuesta es: diseña tus aplicaciones de una buena manera ahora. Establezca su clase para sellar cuando debe ser sellado y privado cuando necesita ser privado. No los haga sellados por defecto.


Solo cierro clases si estoy trabajando en un componente reutilizable que pretendo distribuir, y no quiero que el usuario final herede de él, o como arquitecto de sistemas si sé que no quiero otro desarrollador en el equipo heredar de eso. Sin embargo, generalmente hay alguna razón para ello.

Solo porque no se hereda una clase, no creo que deba marcarse automáticamente como sellada. Además, me molesta muchísimo cuando quiero hacer algo complicado en .NET, pero luego me doy cuenta de que MS marca toneladas de sus clases selladas.


Tu casa, tu regla.

También puede tener la regla complementaria en su lugar: una clase que puede ser subclasificada debe ser anotada; nadie debería subclasificar una clase que no esté anotada así. Esta regla no es más difícil de seguir que tu regla.


© Jeffrey Richter

Hay tres razones por las que una clase sellada es mejor que una clase no sellada:

  • Versiones : cuando una clase está sellada originalmente, puede cambiar para abrirse en el futuro sin romper la compatibilidad. Sin embargo, una vez que se libera una clase, nunca se puede cambiar a sellado en el futuro ya que esto rompería todas las clases derivadas. Además, si la clase no sellada define cualquier método virtual no sellado, el ordenamiento de las llamadas al método virtual debe mantenerse con las nuevas versiones o existe la posibilidad de romper los tipos derivados en el futuro.
  • Rendimiento : como se discutió en la sección anterior, llamar a un método virtual no funciona tan bien como llamar a un método no virtual porque el CLR debe buscar el tipo de objeto en tiempo de ejecución para determinar qué tipo define el método para llamar. Sin embargo, si el compilador JIT ve una llamada a un método virtual utilizando un tipo sellado, el compilador JIT puede producir un código más eficiente llamando al método no virtualmente. Puede hacerlo porque sabe que no puede haber una clase derivada si la clase está sellada.
  • Seguridad : y previsibilidad Una clase debe proteger su propio estado y no permitir que se corrompa. Cuando una clase es abierta, una clase derivada puede acceder y manipular el estado de la clase base si cualquier campo de datos o métodos que manipulan los campos de forma interna son accesibles y no privados. Además, un método virtual puede ser reemplazado por una clase derivada, y la clase derivada puede decidir si llama a la implementación de la clase base. Al hacer un método, propiedad o evento virtual, la clase base deja de tener cierto control sobre su comportamiento y su estado. A menos que se lo haya pensado cuidadosamente, esto puede hacer que el objeto se comporte de manera impredecible, y abre posibles agujeros de seguridad.