7

En mi antiguo trabajo en C++, siempre teníamos mucho cuidado en encapsular las variables miembro, y solo las exponíamos como propiedades cuando era absolutamente necesario. Tendríamos constructores realmente específicos que se aseguraron de que construyera completamente el objeto antes de usarlo.Encapsulación en la era de los frameworks

En estos días, con marcos ORM, inyección de dependencias, serialización, etc., parece que es mejor confiar en el constructor predeterminado y exponer todo sobre su clase en propiedades, para que pueda inyectar cosas, o construir y poblar objetos de forma más dinámica.

En C#, se ha dado un paso más con los inicializadores de objetos, que le dan la capacidad de definir básicamente su propio constructor. (Sé que los inicializadores de objetos no son realmente constructores personalizados, pero espero que entiendan mi punto).

¿Existe alguna preocupación general en esta dirección? Parece que la encapsulación comienza a perder importancia a favor de la conveniencia.

EDIT: Sé que todavía puede encapsular cuidadosamente los miembros, pero siento que cuando intenta generar algunas clases, o bien tiene que sentarse y pensar cuidadosamente cómo encapsular cada miembro, o simplemente exponerlo como una propiedad, y preocuparse de cómo se inicializa más tarde. Simplemente parece que el enfoque más fácil en estos días es exponer cosas como propiedades, y no ser tan cuidadoso. Tal vez estoy completamente equivocado, pero esa ha sido mi experiencia, especialmente con las nuevas características del lenguaje C#.

+0

No dejes que nadie te engañe, esos inicializadores de objetos son solo azúcar sintáctico. – BobbyShaftoe

+0

Sé que los inicializadores de objetos no son realmente constructores, pero ese no es mi punto. Le permiten construir un objeto usando el constructor predeterminado y establecer propiedades a mano. –

+0

No estoy seguro de qué diferencia hay entre esas y simplemente escribiendo cada declaración para establecer cada propiedad. Pero está bien. – BobbyShaftoe

Respuesta

3

No estoy de acuerdo con su conclusión. Hay muchas buenas formas de encapsular en C# con todas las tecnologías mencionadas anteriormente, como para mantener buenas prácticas de codificación de software. También diría que depende de la demo de tecnología que estés viendo, pero al final todo se reduce a reducir el espacio de estado de tus objetos para que puedas asegurarte de que mantienen invariantes en todo momento.

Tome objetos marcos relacionales; la mayoría de ellos le permiten especificar cómo van a hidratar las entidades; NHibernate, por ejemplo, le permite decir access="property" o access="field.camelcase" y similares. Esto le permite encapsular sus propiedades.

Dependency injection trabaja en los otros tipos que tiene, principalmente aquellos que no son entidades, aunque puede combinar AOP + ORM + IOC de maneras muy agradables para mejorar el estado de estas cosas. IoC se usa a menudo desde capas encima de las entidades de tu dominio si estás creando una aplicación basada en datos, que supongo que eres, ya que estás hablando de ORM.

Ellos ("ellos" son servicios de aplicación y de dominio y otras clases intrínsecas del programa) exponen sus dependencias, pero de hecho pueden encapsularse y probarse en un aislamiento aún mejor que anteriormente desde los paradigmas de diseño por contrato/diseño -by-interface que a menudo usa cuando se burla de las dependencias en las pruebas basadas en simulacro (junto con IoC), lo moverá hacia la "semántica" clase-componente. Quiero decir: cada clase, cuando se construya usando lo anterior, estará mejor encapsulada.

Actualizado para la urig: Esto es válido tanto para la exposición de hormigón dependencias e interfaces que exponen. Primero sobre las interfaces: lo que estaba insinuando anteriormente era que los servicios y otras clases de aplicaciones que tienen dependencias, pueden con OOP depender de contratos/interfaces en lugar de implementaciones específicas. En C/C++ y en los idiomas más antiguos no existía la interfaz, y las clases abstractas solo pueden ir tan lejos. Las interfaces le permiten vincular diferentes instancias de tiempo de ejecución a la misma interfaz sin tener que preocuparse por la fuga del estado interno, que es lo que está tratando de evitar cuando abstrae y encapsula. Con las clases abstractas todavía puede proporcionar una implementación de clase, solo que no puede crear una instancia, pero los herederos todavía necesitan saber sobre las invariantes en su implementación y eso puede arruinar el estado.

En segundo lugar, acerca de las clases concretas como propiedades: debe tener cuidado con los tipos de tipos;) que expone como propiedades. Digamos que tienes una lista en tu instancia; entonces no expongas IList como la propiedad; esto probablemente se filtre y no se puede garantizar que los usuarios de la interfaz no agreguen cosas ni eliminen cosas de las que dependas; en su lugar, exponga algo como IEnumerable y devuelva una copia de la Lista, o mejor aún, hágalo como método: public IEnumerable MyCollection {get {return _List.Enum(); }} y puede estar 100% seguro de obtener tanto el rendimiento como la encapsulación. Nadie puede agregar o eliminar ese IEnumerable y todavía no tiene que realizar una costosa copia de matriz. El método de ayuda correspondiente:

static class Ext { 
    public static IEnumerable<T> Enum<T>(this IEnumerable<T> inner) { 
     foreach (var item in inner) yield return item; 
    } 
} 

Así que mientras usted no puede obtener el 100% de encapsulación en, por ejemplo la creación de una sobrecarga igual a operadores/método que puede acercarse con sus interfaces públicas.

También puede usar las nuevas características de .Net 4.0 basado en SpeC# para verificar los contratos de los que hablé anteriormente.

La serialización siempre estará allí y lo ha estado durante mucho tiempo. Anteriormente, antes del área de Internet se usaba para guardar su gráfico de objetos en el disco para su posterior recuperación, ahora se usa en servicios web, en semántica de copia y al pasar datos a, p. un navegador. Esto no necesariamente rompe la encapsulación si coloca algunos atributos [No serializados] o los equivalentes en los campos correctos.

Los inicializadores de objetos no son lo mismo que los constructores, son solo una manera de colapsar unas pocas líneas de código. Los valores/instancias en {} no se asignarán hasta que se hayan ejecutado todos sus constructores, por lo que en principio es lo mismo que no usar los inicializadores de objetos.

Supongo que lo que debe vigilar es desviarse de los buenos principios que aprendió de su trabajo anterior y asegurarse de mantener sus objetos de dominio llenos de lógica de negocios encapsulada detrás de buenas interfaces y lo mismo para su servicio -capa.

+0

Hola Henrik, ¿Puedes dar un ejemplo de cómo "exponen sus dependencias, pero de hecho pueden ser encapsulados y probados en un aislamiento aún mejor que antes"? Si una clase expone sus dependencias en propiedades públicas, ¿cómo puede eso no violar la encapsulación? Gracias! urig – urig

+0

Volviendo, mirando esto, veo que estaba confundido en lo que quise decir. – Henrik

0

Creo que la encapsulación sigue siendo importante, ayuda más en las bibliotecas que cualquier cosa. Puede crear una biblioteca que haga X, pero no necesita que todos sepan cómo se creó X. Y si quisiera crearlo más específicamente para ofuscar la forma en que crea X. La forma en que aprendí sobre la encapsulación, también recuerdo que siempre debe definir sus variables como privadas para protegerlas de un ataque de datos. Para protegerse contra un pirata informático que rompe su código y accede a las variables que no deberían usar.

1

Los miembros privados siguen siendo increíblemente importantes. Controlar el acceso a los datos internos del objeto siempre es bueno y no se debe ignorar.

Muchas veces los métodos privados que he encontrado son excesivos. La mayoría de las veces, si el trabajo que estás haciendo es lo suficientemente importante como para salir, puedes refactorizarlo de tal forma que a) el método privado sea trivial, o b) sea una parte integral de otras funciones.

Además, con la prueba unitaria, tener muchos métodos privados hace que sea muy difícil probar la unidad. Hay formas de evitar eso (hacer amigos de objetos de prueba, etc.), pero añaden dificultades.

Sin embargo, no descartaría los métodos privados.Cada vez que hay algoritmos internos importantes que realmente no tienen sentido fuera de la clase, no hay razón para exponer esos métodos.