Cursosā€Ž > ā€Žmiercoles-Objetosā€Ž > ā€Ž

Clase 6 - Responsabilidad y Delegacion

Hoy hablamos de:

- Diagramas de clases

    Hablamos de como dibujar una clase, con su nombre, atributos y métodos.
    Hablamos de como escribir un método de clase y una variable de clase (poniéndoles una '(C)' al lado)
Ā Ā Ā  Hablamos de como relacionar las clases.Ā  Son importantes las flechas y la cardinalidad.
    ¿Qué pasa si hay una cajita sin métodos? Quiere decir que las instancias de esa clase no entienden ningun mensaje.... Eso nos indica algo malo.

- Responsabilidad y delegación, con el ejercicio de trenes y vagones:

¿Qué objetos tienen que tener qué responsabilidad?  Necesitabamos obtener la cantidad de pasajeros para una formación determinada.  Para ello, le dimos la responsabilidad de darnos dicho número a las formaciones.  En un workspace:

formacion cantidadDePasajeros

¿Por qué a las formaciones y no a los depósitos o a la boletería?  La formaciones no hablan...

Porque nosotros estamos haciendo un modelo... La formación que programamos no necesariamente es la formación de posta.  Le damos la responsabilidad a las formaciones porque suena natural hacerlo así.  Si queremos saber la cantidad de pasajeros de una formación, se lo preguntamos a la formación.

Entonces empezamos a escribir el mƩtodo que resuelva eso:

Formacion>> cantidadDePasajeros
Ā  ^( self vagones collect: [:vagon | vagon cantidadDePasajeros] ) sum.
Ā 
Decidimos tener toooodos los vagones en una sola colección.  Eso nos obliga a tratarlos todos polimorficamente, sean de carga o de pasajeros.

Ahora necesitamos modelar a los vagones, porque tienen que entender el mensaje cantidadDePasajeros.Ā  Y tenemos varios tipos de vagones: de pasajeros y de carga.

VagonDePasajeros>> cantidadDePasajeros
Ā Ā Ā  ^metrosDeAncho <= 2.5 Ā Ā Ā  ifTrue: [ metrosDeLargo * 8 ]
Ā Ā Ā  Ā Ā Ā  Ā Ā Ā  Ā Ā Ā  Ā Ā Ā  Ā Ā Ā  Ā Ā Ā  ifFalse: [ metrosDeLargo * 10 ]

VagonDeCarga>> cantidadDePasajeros
Ā Ā Ā  "Un vagon de carga no lleva pasajeros, entiende este mensaje para ser polimorfico con los vagones de pasajeros"
Ā Ā Ā  ^0
Ā Ā Ā 
--------------
Ahora completemos elĀ  workspace:

formacionCorta := Formacion new.
vagonPasajeros1 := VagonDePasajeros new.
vagonPasajeros1 metrosDeAncho: 2.
vagonPasajeros1 metrosDeLargo: 10.

vagonPasajeros2 := VagonDePasajeros new.
vagonPasajeros1 metrosDeAncho: 3.
vagonPasajeros1 metrosDeLargo: 10.

vagonCarga := VagonDeCarga new.

formacionCorta agregaVagon: vagonPasajeros1.
formacionCorta agregaVagon: vagonPasajeros2.
formacionCorta agregaVagon: vagonCarga.

---------------------

Hay algo feo aca... :S.Ā  Cada vez que creamos un vagon de pasajeros, nos tenemos que acordar de agregarle sus metros de ancho y sus metros de largo!Ā  Fua, si tenemos que crear 30... nos vamos a equivocar seguro.Ā  EstarĆ­a reee bueno tener un objeto al que pueda decirle "che, dame un vagon de carga de 2 de ancho y 10 de largo" onda:

objetoQueMeVaAConstruirVagones dameUnNuevoVagonConAncho: 2 yLargo: 10

¿Pero qué objeto?
Y.. hasta ahora el que tenia la responsabilidad de crearme los vagones de pasajeros era la clase VagonDePasajeros.  Demosle entonces la responsabilidad nueva también!! Y así reemplazamos

vagonPasajeros1 := VagonDePasajeros dameUnNuevoVagonConAncho: 2 yLargo: 10
vagonPasajeros2 := VagonDePasajeros dameUnNuevoVagonConAncho: 3 yLargo: 10

¿Y ese método como se escribe en smalltalk, donde lo meto?
Clase VagonDePasajeros >> dameUnNuevoVagonConAncho: ancho yLargo: largo
Ā Ā Ā  | vagon |
Ā Ā Ā  vagon :=self new.
Ā Ā Ā Ā  vagon ancho: ancho.
Ā Ā Ā  vagon largo: largo.
Ā Ā Ā  ^vagon
Ā Ā Ā 
Ese mƩtodo que escribimos en la parte de "clase", que se va a ejecutar cuando le mandemos un mensaje a la clase, se llama mƩtodo de clase.

---------------------

Ahora encaremos el segundo punto.  Saber la cantidad de vagones livianos de una formación.

¿A quien le damos esa responsabilidad? Y... suena lógico dÔrsela a la formación, porque es ella quien conoce a los vagones.  Esta bueno ver como ahora la responsabilidad surgió a partir de las responsabilidades que ya tenían nuestros objetos.  Como una formación ya conoce a sus vagones, entonces le damos la responsabilidad de filtrar los livianos a ella.  Así también respetamos el encapsulamiento, haciendo que la única responsable de trabajar con la coleccion de vagones de una formación sea esa misma formación.

Ahora aca tenemos 2 variantes:

1)
Formacion>> cantidadVagonesLivianos
Ā Ā Ā  ^( vagones select:[:v | v pesoMaximo <= 2500 ] ) size.

2)
Formacion>> cantidadVagonesLivianos
Ā Ā Ā  ^( vagones select:[:v | v esLiviano ] ) size.
Ā Ā Ā 
Recalcamos ambas versiones, porque es necesario contar que la primera tiene varios problemas:  no respeta en encapsulamiento, estÔ tomando responsabilidades que le pertenecen a los vagones, me impide usar polimorfismo (si tuviera vagones que calculan la liviandad de distinta manera), y fomenta la repetición de código.

En cambio, en la segunda es mÔs sencilla definir la liviandad distinta para cada tipo de vagón, sin que nos interese como se decide eso.
Entonces:

Version 1) :(
Version 2) :D

Ahora pasemos a escribir el mƩtodo esLiviano en los vagones:

VagonPasajeros>>esLiviano
Ā Ā Ā  ^self pesoMaximo <= 2500
Ā Ā Ā 
Ā Ā Ā  >>pesoMaximo
Ā Ā Ā  Ā Ā Ā  ^self cantidadTotalPasajeros * 80
Ā Ā Ā 
VagonCarga>>esLiviano
Ā Ā Ā  ^self pesoMaximo <= 2500
Ā Ā Ā 
Ā Ā Ā  >>pesoMaximo
Ā Ā Ā  Ā Ā Ā  ^self cargaMaxima + 160
Ā Ā Ā  Ā Ā Ā 
PEROOOOOO, ”””Estamos repitiendo código!!!  Lamentablemente, les vamos a pedir que compren eso hasta la semana que viene.  Ahí vamos a resolver este problema con una herramienta nueva.  Aunque si quieren, pueden pensar como se resolvía ese problema con el object browser, con clones y prototipos (porque lo que vamos a ver la semana que viene tiene una mecÔnica parecida).


Ahora que tenemos resuelto el punto 2, encontramos que hay un par de números mÔgicos.  "MÔgicos" porque no sabemos de donde provienen.  Por ejemplo el 2500, el 160, el 80.  Ok, en el enunciado lo dice, pero no en el código...  Podemos buscar de reemplazarlos por algo mÔs feliz.

PodrĆ­amos usar una variable que contenga esos valores.Ā  Por ejemplo:

- limiteDeLiviandad para el 2500
- pesoPersona para el 80
- el 160 podrĆ­amos reemplazarlo por (pesoPersona * 2)

Pero si son variables de instancia, me deberĆ­a ocupar de cargarlas en cada instancia, cosa que puede ser engorroso...
Antes, cuando teníamos código repétido, lo subíamos a la clase.  ¿Por qué ahora que tenemos variables con valores repetidos no lo subimos a la clase también?

Ese concepto de tener una variable en la clase, vamos a llamarlo variable de clase, y tiene la particularidad que es una variable compartida por todas las instancias.
¿Quién tiene la responsabilidad de setear esa variable? La clase... que lo haga una instancia es medio chancho.  Acuerdensé del encapsulamiento.  Con las clases también juega.  ¿Y como lo hace una clase?  A través de un método de clase :).

Cosas a tener en cuenta:
Ā Ā Ā  -si asigno la variable de clase, cambia la referencia para todas las instancias.
Ā Ā Ā  -las variables de clase empiezan en mayĆŗsculas.
Ā Ā Ā  -las instancias pueden acceder directamente a las variables de su clase
Ā Ā Ā 
Entonces el código queda:

VagonPasajeros(vc LimiteLiviandad, PesoPersona)
Ā Ā Ā  >>esLiviano
Ā Ā Ā  Ā Ā Ā  ^self pesoMaximo <= LimiteLiviandad
Ā Ā Ā 
Ā Ā Ā  >>pesoMaximo
Ā Ā Ā  Ā Ā Ā  ^self cantidadTotalPasajeros * PesoPersona
Ā Ā Ā 
VagonCarga(vc LimiteLiviandad, PesoPersona, CantidadGuardas)
Ā Ā Ā  >>esLiviano
Ā Ā Ā  Ā Ā Ā  ^self pesoMaximo <= LimiteLiviandad
Ā Ā Ā 
Ā Ā Ā  >>pesoMaximo
Ā Ā Ā  Ā Ā Ā  ^self cargaMaxima + self pesoGuardas
Ā Ā Ā 
Ā Ā Ā  >>pesoGuardas
Ā Ā Ā  Ā Ā Ā  ^PesoPersona * CantidadGuardas

- Tarea:

Tienen que traer hecho en mÔquina e impreso para la clase que viene (dentro de 3 semanas) en smalltalk el siguiente ejercicio.  El mismo tiene que contener:
  - Código
Ā  - Workspace
Ā  - Diagrama de clases (pueden hacerlo en Visio o en alguna otra herramienta como ArgoUML, StarUML o yuml)