Hola
Tengo una duda sobre como ordenar OrderedCollections cuando quiero tener más de un criterio. Por ejemplo, yo tengo una OC de Manzanas, Mandarinas y Naranjas y quiero que me quede ordenada de tal forma que Manzana>Naranja>Mandarina, donde cada una es una clase distinta.
Lo que estoy haciendo para tratar de ordenar las cosas es esto:
frutas sort: [ :a :b :c |
a Manzana > b Naranja.
b Naranja > c Mandarina.
].
Pero el problema es que me resalta en rojo las clases Manzana, Naranja, Mandarina y no ordena la OC.
¿Es esta la manera correcta de indicarle que quiero que una clase sea mayor a otra?
Muchas gracias, Casimiro.
Hola Casimiro,
Tu código falla por varios motivos pero principalmente porque no estás pensando que todo es un objeto (o un mensaje).
Cuando vos hacés (1 < 2)
no estás haciendo una comparación de dos tipos de datos enteros mediante un operador de comparación. En realidad estás instanciando la clase SmallInteger con el valor 1 y le estás pasando el mensaje <
con otra instancia de esa clase (con valor 2) como parámetro y ese mensaje devuelve un booleano.
Por lo tanto, si vos querés mandarle el mensaje >
; a una instancia de la clase Manzana y que devuelva un booleano, deberás implementar ese mensaje en tu clase dado que en Pharo solamente está implementado en las clases hijas de la clase Magnitude (y una manzana no es una magnitud).
Otro problema que tenés es que en el mensaje sort: de tu objecto frutas (que asumo es instancia de OrderedCollection) le estás pasando un bloque de código que recibe tres valores cuando se espera que haya únicamente dos. Si te fijás la implementación de ese mensaje con el SystemBrowser verás que hace un merge sort y que utiliza el bloque recibido como criterio de comparación entre dos elementos, no tres.
Por otro lado, el ordenamiento que vos querés hacer no es de dos criterios como decís, sino de uno solo, que es según el tipo del objeto. Por dos criterios sería si quisieras ordenar las frutas primero por su peso y luego por su antigüedad, por ejemplo.
Entonces asumo que la prueba que vos querés hacer pasar es más o menos así:
testOrdenarUnaColeccionDeFrutasPorManzanaNaranjaMandarina
| frutas frutasOrdenadas manzana naranja mandarina |
manzana := Manzana new.
naranja := Naranja new.
mandarina := Mandarina new.
frutas := OrderedCollection new.
frutas add: naranja.
frutas add: mandarina.
frutas add: manzana.
frutasOrdenadas := OrderedCollection new.
frutasOrdenadas add: manzana.
frutasOrdenadas add: naranja.
frutasOrdenadas add: mandarina.
frutas sort: [ :fruta1 :fruta2 | fruta1 > fruta2 ].
self assert: frutas equals: frutasOrdenadas.
Ahora bien, para resolver esto tenemos que hacer que las clases Manzana, Naranja y Mandarina entiendan el mensaje >
y devuelvan el booleano adecuado según el parámetro recibido.
Una solución rápida y trivial sería hacer algo como esto en la clase Manzana:
> otraFruta
(otraFruta esManzana) ifTrue: [ ^ false ].
(otraFruta esNaranja) ifTrue: [ ^ true ].
(otraFruta esMandarina) ifTrue: [ ^ true ].
y luego lo mismo en las otras dos clases modificando los booleanos según corresponda.El problema de esto es que no es una solución orientada a objetos porque necesitaríamos implementar en cada clase los métodos esManzana
, esNaranja
y esMandarina
que violan el encapsulamiento del objeto, dado que revelan de qué tipo es. Luego estaríamos usando esa respuesta para poder determinar qué hacer mediante varias estructuras de control, cosa que también queremos evitar.
Una mejor solución sería aprovechando la naturaleza polimórfica de Smalltalk:
> otraFruta
^ otraFruta esMenorQueManzana.
La instancia de la clase Manzana recibe otro objeto y le delega la responsabilidad a ese objeto recibido de determinar quién es mayor que quién. Las clases Naranja y Mandarina tendrán implementaciones similares pero preguntándole al objeto recibido si es menor que naranja y que mandarina respectivamente. Para que esto funcione las tres clases deberán tener implementados los métodos esMenorQueManzana
, esMenorQueMandarina
y esMenorQueNaranja
y cada una devolverá los booleanos acordes a tu criterio de ordenamiento. Como querés que las manzanas vayan primero entonces todos sus métodos deberán devolver false. La naranja y la mandarina deberán devolver true cuando se les pregunte si son menores que la manzana. Para el caso de la mandarina quedaría así:esMenorQueManzana
^ true.
esMenorQueNaranja
^ true.
esMenorQueMandarina
^ false.
Esto es un ejemplo muy sencillo de un patrón de diseño muy conocido llamado Double Dispatch que veremos más en detalle dentro de algunas clases y no se me ocurre una situación del TP1 donde sea estrictamente necesario aplicarlo.Saludos,
Tomás
Muchas gracias por la respuesta, quedó muy claro!
Saludos, Casimiro.