Título: Duda carga de objetos POO Publicado por: Devilkeeper en 8 Enero 2018, 21:35 pm Hola compañeros:
Estoy retomando la Programación Orientada a Objetos, y conociendo ya las bases y demás, me surje una duda a la hora de encarar un proyecto. Imaginemos que tengo una empresa con distintos puestos, cada uno de los cuales trabaja con unas referencias, que se almacenan en unas estanterías, y cada puesto cuenta con sus propios empleados. La cosa es, si yo cargo un objeto del puesto 1, ¿debería cargar todos los objetos relacionados?. Si mi clase Puesto tiene un campo que es una lista de objetos tipo Referencias, ¿al cargar el puesto 1 debería cargar en esa lista todas las Referencias de ese puesto?. Y lo mismo para el resto de cosas, si la clase Puesto tiene un campo que es una lista de trabajadores... ¿Tendría que cargar la lista de trabajadores, cargando a su vez los posibles objetos relacionados de la clase Trabajador?. Direcciones por ejemplo. No sé si es una manera correcta de trabajar, o es mejor cargar los Puestos y según necesite ir cargando y descargando las listas. Si quisiera cargar todos los puestos, tendría en memoría muchísimos datos, y no sé si eso está bien. Gracias. Título: Re: Duda carga de objetos POO Publicado por: Serapis en 9 Enero 2018, 01:31 am No importa lo complejo de un sistema, la cuestión es que solo se 'carga' lo que se necesita usar. Por ello el diseño debe ser eficiente.
Las dependencias deben ser realistas... si es objetiva y realmente dependiente, se hace dependiente, si no, no. Una rueda tiene atributos, como numero de radios, diámetro, ancho, número de tornillos, marca, etc... luego es razonable que todos esos atributos sean propiedades de las ruedas. Luego la cargar una rueda (crearla, referenciarla) todos sus atributos estarán disponibles. Y todo vehículo tiene una cierta cantidad de ruedas, luego las ruedas será algún tipo de colección, que tienen todos los vehículos, pero que cada implementación de vehículo tiene un valor diferente. Sin embargo si cambias una rueda, deberá del mismo tamaño que el resto, esto es debe encajar... así que debiera haber constructores específicos para cada tipo de vehículo. Piensa con cosas cotidianas, manejables que puedas fácilmente entender el porqué de ello. Un simple ejemplo: Código: Interfaz Vehículo Código: Clase Bicicleta Implementa Vehículo Como se puede ver algo simple, pero fácil de entender y seguir el ejemplo... tiene varios atributos de solo lectura, y tan solo el color es lectura y escritura. Como es una bicie, no tiene motor ni puertas, además fuera de la interfaz que implementa se han añadido dos atributos aparte, NúmeroDePiñones y NumeroDePlatos... faltaría a la interfaz un método 'cambiarDeMarcha' y añadir un atributo 'NumeroDeMarchas'... la bicileta también tiene... Como ves cuando se 'carga' un vehículo, con él se carga todo lo necesario, porque forma un 'objeto', todo englobado en la clase, un único objeto engloba a todo, pero puede estar formado por varios objetos, un motor podría ser otro objeto en sí mismo, lo mismo que las ruedas y las puertas... básicamente cualquier cosa que sea más compleja que una mera propiedad. Fíjate como aunque no hemos definido una clase Rueda, al crearla sin embargo le estamos pasando algunos atributos necesarios para crearla, y tras ello, incluso modificamos un atributo (donde está situada la rueda), que no se incluye en el 'constructor'... en cambio al crear el objeto bicicleta, solo se reclama el atributo color, el número de ruedas es prefijado por el diseño propio de la bici, lo mismo sucede con 'motorizado' y 'tienepuertas'. Un diseño mejor sería tener un atributo llamado NumeroDePuertas, en sustitución de 'TienePuertas', ya que 'número identifica cuantas son, y al caso '0', es lo mismo que 'no tiene'. Es decir una primera aproximación se define y luego de repensarlo haces cambios y ajustes... Como ves no es necesario cargar nada más que lo que respecta al vehículo... pero eso está en su propio diseño. si ahora creas un objeto ciudad, tendrás por un lado objetos calle, objetos edificios y objetos vehículos, incluso objetos garaje. Cada uno de ellos sería una colección, pero incluso así, una ciudad tendría atributos como 'nombre', 'extension', 'ubicacion', etc... pero es necesario cargar todos los vehículos de la ciudad?... No, tan solo los de la calle actual. Siendo calles una colección de la clase 'Calle', y poseyendo cada calle una colección vehículos (que circulan por ella), bastaría cargas solo esos. A su vez cada vehículo podría tener un atributo 'calle', para referenciar al objeto calle en el que se encuentra. Cuando se cambia de calle (otra distinta), se descarga la previa para cargar la nueva... Intenta diseñar un objeto calle, edificio y ciudad. Una colección es una lista de objetos que puede ser tanto homogéneos como heterogéneos, el diseño estbalece lo deseable y al caso, suelen abundar las colecciones cuyo contenido son del mismo tipo "Colección de tipo x"... una colección tiene métodos tales como: añadir (al final), eliminar (del final), insertar (en cierta posición), Borrar (de cierta posición), vaciar (por completo), listar (desde x hasta y), enumerar (todos), Devolver (el enésimo objeto en la colección), y algunos más complejos como 'UnirAOtraColeccion, Clonar, Copiar, etc... y suele tener atributos del tipo: Vacia, Cantidad, Tipo para señalar si la colección está vacía, cuantos objetos contiene y el tipo de objetos que aloja. Incluso puede hacerse uno como actual y tener un atributo IndiceActual, que puede ser leído y cambiado... Según la necesidad se crea o no, por ejemplo en una bicicleta, la colección ruedas, no precisa un atributo 'indiceActual', además siendo sólo 2 es fácl acceder a una y saber cual de ellas es (trasera, delantera), pero (por ejemplo) en una ciudad saber el nombre de la calle actual seleccionada, si resulta conveniente... ya que por ejemplo podría ser deseable 'conducir' un vehículo a dicho destino. Así, un método Conducir (a la calle x)... movería un vehívulo hacia dicha calle, haciéndolo más complejo, debería encontrar el trazado desde su ubicación actual hasta el destino, y al llegar allí detenerse y avisar... Piensa en objetos de la vida real y trata de 'visualizarlo' todo desde esa perspectiva de la manipulación, propiedades que tienen y lo que se puede hacer con ello... Título: Re: Duda carga de objetos POO Publicado por: Eleкtro en 9 Enero 2018, 13:48 pm No sé si es una manera correcta de trabajar, o es mejor cargar los Puestos y según necesite ir cargando y descargando las listas. Si quisiera cargar todos los puestos, tendría en memoría muchísimos datos, y no sé si eso está bien. Es precisamente por esa necesidad que existe la interfáz IEnumerable<T> (o IEnumerable(Of T) en VB.NET) de evaluación vaga y el tipo Lazy<T> (o Lazy(Of T) en VB.NET) para la inicialización/instanciación vaga de objetos. La interfáz IEnumerable, o colección enumerable, provee lo que se conoce como un enumerador, que es un mecanismo para obtener el elemento actual en la colección enumerable, mover al siguiente elemento, y reiniciar. El enumerador solamente debe preocuparse por saber como obtener el siguiente elemento en la colección enumerable, por lo que no es necesario que la colección entera esté alojada en memoria ni saber cuantos elementos hay en total; este comportamiento o implementación se traduce en una optimización de varios aspectos, empezando por el más obvio: el consumo de memoria. Practicamente todas las colecciones disponibles en los espacios de nombre de la librería de .NET Framework implementan la la interfáz IEnumerable, como los tipos List, Dictionary, Array, Stack, Collection y etcétera, pero ojo, eso no quiere decir que todos los tipos de colecciones sean de evaluación vaga, cada tipo tiene una implementación distinta para servir a un propósito específico, así que si quieres asegurarte de beneficiarte de la evaluación vaga entonces usa directamente la interfáz IEnumerable. El tipo Lazy<T>, por lo general lo usarías en escenarios de programación asincrónica y para prevenir la inicialización de una instancia que sea bastante pesada... hasta que realmente necesites acceder/leer ese objeto. Te recomiendo unas lecturas importantes para comprender los conceptos básicos:
Citar Lazy initialization of an object means that its creation is deferred until it is first used. (For this topic, the terms lazy initialization and lazy instantiation are synonymous.) Lazy initialization is primarily used to improve performance, avoid wasteful computation, and reduce program memory requirements. Citar By initializing objects in a lazy manner, you can avoid having to create them at all if they are never needed, or you can postpone their initialization until they are first accessed Bien, ahora que ya he explicado que es cada cosa, te mostraré como puedes reproducir las diferencias a través de varios ejemplos (básicos) basados en el escenario o problema que propones... Primero de todo, para estos ejemplos crearemos un tipo como éste para representar la información básica de un cliente: Código
...es solo un ejemplo cualquiera, mi intención al escribir este ejemplo ha sido que una instancia del tipo Customer ocupe bastantes bytes. Bien, por lo general, podriamos decidir crear una colección genérica de tipo List<T> (o List(Of T) en VB.NET) para almacenar varias instancias de la clase Customer, e iterar la colección para mostrar cualquier tipo de información de cada Customer, quedando algo parecido a esto: Código
¿Qué ocurre con esto?, pues que todos los elementos de la colección se inicializan, se asigna un espacio en el bloque de memoria para cada elemento de la colección, y evidentemente si tenemos en cuenta el tamaño en bytes de una única instancia de la classe Customer pues... 100.000 instancias simultaneas resultan en un gran consumo de memoria... (https://i.imgur.com/BbrdH2T.png) Cuando un elemento Customer ha sido inicializado y ya hemos trabajado con él, no necesitamos que siga albergando espacio en memoria, queremos desechar ese consumo adicional que ya no necesitamos para nada, y aquí es donde .NET Framework nos ofrece la solución al problema: IEnumerable<T>. Este código de aquí abajo es practicamente lo mismo que el anterior con el uso de List<T>, solo que ha sido adaptado para el uso de IEnumerable<T> mediante una función iteradora y así demostrar el beneficio que nos interesa obtener... Código
El beneficio en la optimización del consumo de memoria queda bastante claro: (https://i.imgur.com/q7CjIa3.png) Te muestro un ejemplo para el uso del tipo Lazy: Código
Por supuesto podemos combinar el tipo Lazy<T> e IEnumerable<T>: Código
IEnumerable y Lazy son dos propuestas para fines distintos, aunque combinables para un mismo fin, y yo solo te he mostado ejemplos básicos de su utilización en escenarios sincrónicos; al final la solución más óptima y adecuada a tu problema siempre dependerá de lo que realmente quieras hacer y como sea realmente necesario hacerlo... Espero que esto haya servido de ayuda, al menos. Saludos. Título: Re: Duda carga de objetos POO Publicado por: Devilkeeper en 12 Enero 2018, 19:08 pm Muchísimas gracias a los dos por vuestras respuestas tan elaboradas.
Aunque en mi caso no voy a cargar ni 100 registros, voy a implementar el Lazy Loading para ir aprendiendo su uso. Ya iré contando que tal, he encontrado otros recursos para seguir documentándome. Muchas veces buscas algo que tiene un nombre concreto, pero como no lo conoces, no encuentras nada de informacion. Un saludo. |