Patrón State – Videotutorial

En esta entrega vamos a abordar en profundidad uno de los patrones GoF más utilizados, el patrón Estado o State. Pero antes de entrar a analizar los detalles del patrón debemos preguntarnos que se entiende en si por estado. Un sinónimo bastante próximo a lo que se entiende por estado es memoria. Y en lo que se refiere a memoria, en el caso de un programa, este es el terreno de las variables en general: campos, variables, argumentos de entrada, etc. De ahí que podamos decir que el estado de un programa en un momento dado sea el valor de todas y cada una de la variables del programa; del mismo modo, el estado de un objeto sería el valor que toman en un momento dado todas sus «variables», es decir, sus campos de instancia.

Sabemos que en un lenguaje como java, diseñado para favorecer la programación mediante el paradigma de la orientación a objetos, la clase es la entidad de nivel superior que encapsula al resto de miembros.
Estos miembros pueden representar datos (información), los llamados campos, o pueden representar comportamiento, los llamados métodos (encapsulando una serie de instrucciones).

Conocemos tambien que en lo que respecta a campos de una clase, cuyo papel como hemos dicho es almacenar información en memoria, algunos pueden ser compartidos por todas las instancias de la clase o estaticos, mientras para otros cada instancia tiene su propia versión o copia del campo, los campos de instancia.

Dicho todo esto, si en una clase, nada más se define comportamiento, es decir, sus miembros son únicamente métodos, entonces, este objeto carece de estado. Y esto tiene una implicación práctica muy importante. Por un lado, no importa cuantas veces se llame a un mismo método de ese objeto, si los argumentos de llamada son los mismos el resultado o comportamiento será siempre el mismo. Por otro lado, dará igual también el orden en el que se llame a los métodos de ese objeto porque su comportamiento no depende de si se ha llamado a otro método previamente. Como no hay campos de instancia, el objeto carece de estado, y sin estado «no hay memoria sobre el pasado».

Por tanto, cuando hablamos de estado, nos estamos refiriendo al valor que contienen en un momento dado los campos de una clase. Si el ámbito de estos campos ha sido declarado privado (como preconizan las buenas prácticas de la POO), entonces la única forma de modificar el valor de alguno de estos campos del objeto es mediante la llamada a alguno de sus métodos, siendo el caso más característico de todos ellos un método «setter».

En este punto, llegamos al caso de que algún método de la clase quiera asegurarse de que se ha llamado previamente a algún otro método, cosa que podrá averiguar indirectamente comprobando el valor de algún campo del objeto, o dicho de otra forma, comprobando el estado del objeto.

La necesidad de comprobar el estado del objeto es un hecho bastante común en programación, puesto que muchas veces para poder llevar a cabo su labor el método invocado necesita estar seguro de que se cumplen unas determinadas precondiciones, ya que de no ser asi, puede no tener sentido realizar su trabajo. Y aqui está «la madre del cordero». La comprobación de que se cumplen estas precondiciones, a partir de analizar el valor de los campos que sea necesario comprobar, puede requirir el uso de estructuras condicionales, cuyo nivel de anidamiento y ramificaciones hagan muy complicado seguir la pista de la lógica de esas instrucciones. Estas bifurcaciones, si bien pueden compilar sin errores de sintaxis pueden no estar haciendo lo correcto desde un punto de vista semántico, en cada combinación de valores posible que puedan tomar los campos del objeto, el estado.
Ademas de esto, es posible que una de estas precondiciones pueda ser necesario comprobarla en mas de un método, lo cual conlleva copiar y pegar la expresión condicional y/o ramificaciones idénticas en más de un sitio del código. Bienvenidos a la pesadilla del mantenimiento del código!!!!

Existen técnicas alternativas más eficientes, en base a constatar que básicamente lo que estamos haciendo es seguir la pista a algunas de las combinaciones de valores que pueden tomar algunos de los campos del objeto. Cuando se da alguna combinación de estas decimos que el objeto se encuentra en el estado X, si se da otra de estas combinaciones diremos que su estado es Y, etc.

Llegados a este punto es posible que tengamos un número finito de estados. En este caso, estamos ante lo que se conoce como una máquina de estados finita o FSM. Nuestra objeto, por tanto, no es más que una máquina de estados finita, y la podremos programar siguiendo algunas de las técnicas para implementar una FSM. Entre todas las diferentes técnicas que existen, la que mejor saca partido de la POO es precisamente la que consiste en aplicar el patrón State. Por ello, podemos decir que el patrón State no es más que la implementación de una FSM mediante el uso de la POO.

Aqui teneis el videotutorial sobre el patrón de diseño State.

Patrón Observer (II) – Métodos Pull y Push para el intercambio de información entre sujeto y observador

Seguimos con el ejemplo donde aplicabamos el patrón de diseño Observer a un código que realizaba la autenticación de usuarios. En este ejemplo vimos que la manera de desacoplar los componentes era mediante el uso de este patrón.

En el ejemplo, los componentes observadores reciben la información necesaria para realizar su labor cuando reciben la notificación junto con esta en forma de parámetros de entrada del método update. Cuando junto con la notificación, los observadores reciben la información de estado del sujeto, aquella que necesitan para llevar a cabo su tarea, a esta forma de comunicación de la información se la denomina «Push», dado que el sujeto «empuja» la información de su estado junto con la propia notificación.

El mayor inconveniente que puede tener el método push, tal y como lo hemos implementado, para hacer llegar la información de estado desde el sujeto a los observadores que la requieran es que no es flexible ante el cambio. Si las especificaciones futuras hicieran cambiar la información de estado, por ejemplo, ampliandola para informar adicionalmente sobre el password introducido, número de intentos de autenticación realizados, etc. ello implicaria modificar la firma del método update() en la interfaz Observer, lo que provocaría la rotura del código de todos los componentes observadores actualmente implementados, puesto que la firma de la implementación de sus métodos update() ya no coincidiría con la firma tal como quedara redefinida en la interfaz.

Método Pull

Ante esto, existe la posibilidad de no comunicar la información de estado del sujeto junto con la notificación, sino que la notificación simplemente sirva para indicar al observador que ahora es el momento de pedirle la información de estado al sujeto porque acaba de realizar la acción. Es entonces el observador, quien mediante una referencia al sujeto, invocará los métodos necesarios para recuperar la información de estado del sujeto. El observador «estira» de la información por eso el método se denomina Pull.

En el método Pull el sujeto debe de proporcionar una interfaz (una serie de métodos) para que los observadores puedan obtener la información de estado que necesitan. En el ejemplo, esto mismo es llevado a cabo mediante la creación de dos «getters» getUser() y isSuccess() de ambito público. El observador, mediante un referencia al sujeto, puede invocar estos métodos y «estirar» de la información.

El observador necesita contar con una referencia al sujeto al cual está observando que pueda untilizar dentro de su implementación del método update(). Existen varias aproximaciones para que el observador cuente con esa referencia. Podemos bien pasársela en el constructor, o bien crear un setter para establecerla, o como última posiblidad que se reciba como un parámetro del propio método update() así: void update(Subject source). Hacerlo en el constructor es muy rígido porque implica que el obervador no puede cambiar de sujeto durante toda su vida. Hacerlo mediante un setter si permite cambiar de sujeto, pero como ya veremos más adelante, implica que vamos a observar un solo sujeto a la vez. Por último, si decidimos que la referencia al sujeto nos llegue como parámetro de entrada en el método update, podremos registrar el componente para que observe varios sujetos al mismo tiempo ya que gracias a este parámetro se podrá identificar que instacia de sujeto concretamente está notificando. En el ejemplo solo existe un objeto sujeto, la instancia de AuthenticationManager, por tanto tiene sentido definir un setter.
Este setter (implementado de una forma perfecta y realista) deberia comprobar si el componente ya está actualmente observando otro sujeto y desregistrarse de su lista de observadores y a continuación registrarse para observar el nuevo sujeto.

public void setSubject(Subject subject) {
	if(this.subject != null) this.subject.unregister(this);
	this.subject = subject;
	subject.register(this);
}

Dentro del método update el inconveniente que se nos presenta es que no podemos obtener la información de estado del sujeto a no ser que convirtamos con éxito la referencia de tipo Subject más abstracta a una más concreta del tipo concreto del sujeto AuthenticationManager.
Para asegurarnos de que el downcasting tiene exito podemos comprobar si la referencia almacenada dentro el campo subject es de tipo AutheticationManager haciendo uso del operador instanceof de java.

if (subject instanceof AuthenticationManager) {
	AuthenticationManager authMng = (AuthenticationManager) subject;
        
        //Aqui código que recupera informacion de estado del sujeto concreto
}

Este es el mayor inconveniente que presenta el método pull, a parte de obligar al observador a solicitar la información de estado que necesita, este debe conocer el tipo concreto del sujeto o tratar de averiguarlo, con lo cual estará acoplado con una implementación concreta, rompiendo el principio de diseño de programar para una interfaz y no para una implementación.

Método Push

Por otra parte tenemos en método push, que ya hemos comentado basa su estrategia en propagar la información de estado junto con la notificación. El problema como hemos apuntado es que la información de estado en si puede ser motivo de cambio en futuras ampliaciones, lo que conlleva cambios en la firma del método update. No obstante podemos solventar este problema si encapsulamos toda la información de estado que vamos a propagar en una instancia de un objeto diseñado precisamente para encapsular toda esta información de estado. Con esto, la firma del método update solamente necesita contar con un parámetro de este tipo. En el ejemplo este papel lo desempeña la interfaz NotificationInfo.

public interface NotificationInfo {	
	String getUser();	
	boolean isSuccess(); 
}

Normalmente y en previsión de que un observador puede observar más de una instancia de tipo sujeto al mismo tiempo, se establece otro parámetro en la firma del método update de la interfaz Observer para, en caso de estar observando varios sujetos, poder identificar de que sujeto se trata, en el caso de que debamos interaccionar con él. Quedando, por tanto, la interfaz Observer de la siguiente manera:

public interface Observer {	
	void update(Subject source, NotificationInfo state);
}

El código que implementa el método update en una clase observador puede acceder a la información de estado, en el ejemplo el usuario y el resultado de la validación mediante el parámetro state.

En definitiva, si en lugar de colocar cada pieza de información asociada como un parámetro independiente en la lista de parámetros del método update los encapsulamos en un nuevo tipo de objeto que cuente con una interfaz para acceder a todos ellos, no será necesario modificar nada en el código que ya tuvieramos desarrollado si posteriormente hubiera que añadir más información de estado, como sucedia con el método pull. Pero, al contrario que en el método pull, el observador no está obligado a conocer y trabajar con referencias del tipo concreto de sujeto. Por este motivo, casi todos los sistemas utilizan esta aproximación basada en push más un objeto que encapsula la información de estado.

Aqui teneis el video donde podeis ver el desarrollo de ambos ejemplos.

Patrón Observer – Ejemplo de aplicación del patrón en Java

El patrón de diseño Observer se caracteriza principalmente por la reducción en el acoplamiento entre distintos componentes de una aplicación. Si nos encontramos en una situación en la que uno de estos componentes se acopla con el resto de ellos, como en el ejemplo de código que vamos a ver, siguiendo el patrón de diseño Observer daremos a este componente el rol de sujeto, el resto serán observadores.

La ventaja que proporciona el uso del patrón observador es que el código de la clase que hace de sujeto, que es la que tiene la información de cuando y por qué se deben hacer ciertas «tareas», quedará liberada de llamar directamente a los componentes que realizan estas «tareas». Dado que, llamarlos directamente implica conocer el tipo concreto de estos componentes y su interfaz concreta, se produce el acoplamiento con estos.

En lugar de esto, la clase sujeto notificará a los componentes cuando lo estime conveniente porque se da el motivo para ello. El patrón Obervador facilita que la notificación se produzca a través de una interfaz bien definida y común a todos estos componentes que se va a notificar. Esta interfaz común viene dada mediante la interfaz Observer que cuenta con un método que se suele denominar update().

Es tarea del sujeto mantener una lista de objetos interesados en ser notificados, con una serie métodos (register y unregister) para que los componentes observadores se registeren y se den de baja en esta lista. El sujeto pues, ya no necesita conocer el tipo concreto de los componentes, el tipo de las referencias con las que trabaja para notificar a los componentes es del tipo abstracto Observer. Tampoco depende de cuantos componentes estén registrados y si reciben la notificación ni de como la procesan.

En conclusión, todo esto permite que al programa al que aplicamos el patrón observador, podamos añadir nuevos componentes adicionales que amplien la funcionalidad con nuevas tareas en futuras modificaciones del código sin que sea necesario hacer cambios en el código de la clase notificadora o sujeto. Bastará simplemente con instanciar un objeto de este nuevo componente y registrarlo para que observe al sujeto.

En el ejemplo, tenemos una clase para autenticar usuarios, su responsabilidad es comprobar unas credenciales (usuario y contraseña) y validarlas, concluyendo si son correctas o no. Por otro lado, existen tareas adicionales, dependientes de si la validación ha tenido exito o no, son variadas y abarcan desde la carga de archivos del perfil, el registro de auditoria, la monitorización de ataques, etc.

Si todo esto lo hacemos en el código de la clase de autenticación estamos rompiendo el principio de responsabilidad única, y lo peor, estamos creando una dependencia en esta clase por cada uno de estos componentes que maneja directamente. Ampliaciones en este sistema para que trabaje con un nuevo compomente implica cambios en el código de la clase de autenticación. Los cambios en los componentes existentes tambien pueden afectar al código de la clase de autenticación.

Daremos a la clase que autentica el rol de sujeto, porque es la que tiene conocimiento de CUANDO suceden la acción que da lugar a que sea necesario escribir una entrada en el registro de eventos, cargar perfiles, activar la monitización de ataques, etc. y el rol de observadores al resto de componentes porque son los que tienen el conocimiento de COMO registrar, cargar, monitorizar, etc. Estos se registran en la lista de observadores del sujeto y este les noficará cuando deben realizar su labor, además de la información adicional necesaria.

Aqui os dejo el video tutorial.

El asunto de la comunicación adicional que el observador necesita para realizar su trabajo no es una cuestión menor y existen varias posibilidades. Estas variantes del patrón Obsever en función de como se facilita la información adicional a los observadores se denominan Push y Pull. En el próximo post analizaremos cada una de ellas para ver sus ventajas e inconvenientes.

Creando el módelo y un controlador para la entrada mediante el ratón

Continuamos el videotutorial sobre el desarrollo de videojuegos. En esta segunda entrega se llevan a cabo principalmente 2 tareas. La primera consiste en eliminar todo el código contenido dentro de la clase Gameloop relacionado con el almacenamiento y la actualización de la información necesaria para dibujar la pelota en la posición correspondiente en cada frame. La segunda tarea añadirá nueva funcionalidad al programa demostración aportando la posibilidad de que el usuario interaccione con la pelota mediante el uso del ratón.

Como primera tarea hemos dicho que vamos a eliminar de la clase Gameloop la responsabilidad de manejar la información necesaria para dibujar la pelota y actualizar su posición. Toda esta responsabilidad recaerá en una nueva clase, GameObject, que formará parte del conjunto de clases cuya cometido consiste principalmente, como ya se ha apuntado, en modelar las características intrínsecas que definen los elementos de que se compone el juego. Por tanto, le conciernen tareas como la de almacenar toda la información asociada a los componentes del juego y definir los métodos necesarios para definir la lógica interna, manipular y acceder a esta información.
Este grupo de clases forman lo que se denomina MODELO en el patrón de arquitectura de aplicaciones conocido como MVC (Modelo – Vista – Controlador).
En esta arquitectura, las clases que componen el modelo, tiene la resposabilidad que hemos mencionado anteriormente.
En nuestra demo, la clase GameObject, será de momento la única clase definida en el modelo, para que este sea lo más simple posible a efectos de que el código de la demo se mantenga sencillo.

En la segunda parte del video, se añadirá la funcionalidad al programa para que el usuario haga click en un punto de la pantalla del juego y la pelota cambie la dirección y se dirija a este punto en que el usuario hizo click. La pelota describirá un movimiento parabólico desde el punto inicial en el que se encuentra en el momento en que usuario ha efectuado el click hasta ese nuevo punto destino, donde alcanzará su altura máxima y a partir de ahi empezará su descenso.

En la aplicación de ejemplo, la clase en la que recaerá la responsabilidad de controlar la respuesta a la entrada del usuario mediante el ratón será la clase MouseGameController. Esta clase cumple el papel de un controlador según el patrón MVC. Según las directrices del MVC, los controladores son los objetos que se encargan de lidiar con la entrada del usuario. Las acciones que el usuario realiza, click de ratón, pulsaciones de teclas, etc. el controlador las traduce en una llamada a alguno de los métodos del modelo. Por tanto, es misión del controlador interpretar la acción realizada por el usuario y ordenar al modelo realizar una acción en repuesta a la acción del usuario.

El controlador no cuenta directamente con las instrucciones para responder a la acción que usuario demanda, eso es tarea del modelo. Pero si sabe cuando el usuario da la orden porque permanece a la escucha de la entrada del usuario. La clase MouseGameController para realizar su labor debe observar a la vista, en nuestro caso concreto el JFrame, que es quien hace el papel de sujeto. MouseGameController es un observador y el JFrame es el sujeto, he aquí un caso de uso del patrón observador. De momento, vamos a suscribir al objeto observador (la instancia de MouseController) en el método init() del Gameloop. El controlador necesita una referencia al modelo para proceder a invocar el método que este haya considerado necesario despues de interpretar la entrada del usuario, dicha referencia se la proporcionaremos en el constructor.

Para terminar de cerrar la breve descripción del patrón MVC, diremos que la misión de la vista consiste en plasmar de una manera visual la información que mantiene el modelo. Por tanto, la responsabilidad de cualquier clase que forma parte de la vista es «pintar» la información. En el caso del juego, la vista la componen las clases que trabajando con una referencia a objetos del modelo, leen su información y a partir de ella «dibujan» en un espacio de presentación como la ventana de un JFrame. En los siguientes videos, está será la siguiente responsabilidad que habrá que extraer de la clase GameLoop pues todavia se nos ha quedado en ella.

Aqui teneis el video del tutorial

Y aqui el video correspondiente a la explicación sobre las fórmulas aplicadas para obtener la velocidad inicial vertical y horizontal a aplicar a la pelota ante un click de ratón por parte del usuario: La fisica del movimiento parabólico.

El bucle principal de un videojuego

En este video se describe la arquitectura básica de una aplicación consistente en un videojuego. El nucleo de este tipo de aplicaciones consiste en un bucle que repite en cada paso 2 acciones secuencialmente. La primera de ellas es la actualización de los valores que definen las caracteristicas de los elementos del juego (el modelo) teniendo en cuenta el tiempo que ha pasado desde la última actualización. La segunda consiste en la visualización en pantalla de los cambios, esta nueva «realidad», de forma gráfica tras la generación, a partir de la información aportada por los elementos del juego (el modelo) una vez actualizada, de lo que se conoce como un frame.

El proceso se repite una y otra vez, a un ritmo fijo determinado por el denominado framerate, por ello, antes de terminar cada pasada del bucle, este debe comprobar el tiempo que le ha sobrado despues de las tareas de actualización y renderizado del frame para proceder a dormir el hilo de ejecución el tiempo restante. Todo esta responsabilidad se asigna a una clase que se encargará de controlar que la acciones de actualización y renderizado se producen a un ritmo perfectamente sincronizado e independiente de la velocidad de la CPU.

Patrón Estrategia – Videotutorial

El patrón estrategia es uno de los patrones de comportamiento más importantes y utilizados dentro de del grupo de patrones Gof. Tanto es así, que muchos lenguajes ofrecen características integradas en el propio lenguaje para hacer incluso más facil de utilizar este elemento de diseño cuando se implementa en uno de estos lenguajes. Uno de estos lenguajes es java.

Aqui os dejo un videotutorial que trata a nivel teórico el patrón de diseño Estrategia explicando sus beneficios y situaciones en la que deberia aplicarse.

Expresiones lambda en java 1.8

Una expresión lambda equivale a escribir una especie de función matemática del tipo

f(x) = x * 2 + 3

En este caso leemos que dado un x, el resultado será el de multiplicar por 2 el valor de x y sumarle 3.

Para expresar esto mismo con una expresión lambda hariamos:
(x) -> x * 2 + 3

Expresar esto en programación requiría escribir un método o función, que a su vez debe encapsulsarse en una clase.

public class Contenedora implements Funcion {

	int funcion(int x) {
		return 2 * x + 3;
	}
}

Si la clase contenedora implementa una interfaz Funcion definida de esta manera

public interface Funcion {

	int funcion(int x); 
}

Entonces podemos asignar una referencia a un objeto de tipo Contenedora a una variable de tipo Funcion
y utilizar la variable para invocar al objeto que realiza la operación.

Funcion f1 = new Contenedora();
f1.funcion(25);     // 25 * 2 + 3

Todo este envoltorio en clases es necesario debido a que anteriormente java no permitia asignar funciones a variables, solamente se podían asignar valores de tipos de datos primitivos y referencias a objetos. Las funciones eran elementos de segunda categoría y se tenían que encapsular en clases contenedoras.

A partir de Java 8 si una interfaz como la que hemos definido, Funcion, contiene un único método, se podrá asignar directamente a una variable del tipo de la interfaz una función cuya firma sea compatible con la firma de este método.

class Funciones {
static int dobleMas3(int x) {
		return 2 * x + 3;
	}

//mas funciones dentro de la clase
}

Podemos usar la funciín de arriba dobleMas3 de este modo:

Funcion f1 = Funciones::dobleMas3;
f1.funcion(25);     // 25 * 2 + 3

A la variable f1, de tipo Funcion (una interfaz de un único método) le asignamos directamente el nombre de una función.

Pero existe un detalle que todavía se puede mejorar: las clases anónimas permiten asignar a una variable del tipo de la interfaz una referencia a un objeto nuevo anónimo creado así porque no existe una clase entre las que implementan la interfaz que reuna las características que buscamos y solamente vamos a crear una instancia objeto sola esa vez. ¿Para qué crear una clase implementadora de la interfaz a propósito para luego sólamente utilizar una instancia de esa clase? Es el caso de los «listeners» de Swing

Bien, pues esta misma situación también se puede presentar cuando estamos considerando funciones. ¿Y si no existe en el código una función que realice las instrucciones que yo quiero llevar a cabo?¿La defino en alguna parte, le doy un nombre y luego la asigno como hemos visto en el ejemplo de la función dobleMas3?

La respuesta es que no es necesario, igual que existen los objetos anónimos existen las funciones anónimas, y estas son las denominadas expresiones lambda.

Por tanto, supongamos que queremos asignar a esa variable f1 una función que no existe, en este caso haríamos algo así:

Funcion f1 = (x) -> x * 2 + 3; //expresion lambda
f1.funcion(25);     // 25 * 2 + 3

Una vez visto el ejemplo, vayamos a la sintaxis. Una expresión lambda se compone de 2 partes: parte izquierda y parte derecha, separadas por los caracteres ->. En la izquierda es donde se indican las variables de la expresión y en la derecha está el calculo que se realiza con esos valores de entrada proporcionados.

Se han de cumplir una serie de requisitos para que una expresión lambda se puede asignar a una variable (o pasarse como argumento en una llamada)

  1. La expresión lambda debe definir en su parte izquierda tantos identificadores como el número de parámetros que tiene la función única definida en la interfaz de cuyo tipo es la variable o parámetro de la llamada al que queremos asignarle la expresión.
  2. La expresión que realiza el cálculo en la parte derecha, cuando se evalue, debe proporcionar un resultado cuyo tipo debe coincidir con el tipo de valor de retorno que define la función en la interfaz

Como consecuencia de todo esto, podemos concluir que a partir de este momento, comportamiento o funcionalidad acaban convirtiéndose en información almacenable en variables y es posible pasarlo a otras funciones que la esperan como argumento de llamada. De modo que, igual que el resultado de una función depende de los valores con los que es invocada, el comportamiento de la función puede cambiar si al llamar a la función pasamos como argumento distintos comportamientos (funcionalidad o código). La implementación de un método de una clase puede parametrizar no sólo información para llevar a cabo su tarea sino que además puede parametrizar comportamientos (fragmentos de código) en los cuales delegar. En otras palabras, una parte del código pueda porporcionar no solo datos sino también instrucciones o código a otra parte cuando esta última es invocada.

Patrón Estrategia – Una clase para ordenar listas de elementos I

El patrón estrategia nos permite determinar el comportamiento en tiempo de ejecución de un objeto, seleccionando una estrategia diferente para el objeto antes de pedirle que realice de nuevo una operación.

Imaginemos que queremos definir un clase cuya finalidad sea ordenar una colección de objetos. Supongamos por ejemplo, que el tipo de los elementos de la colección sean Manzanas, y sus caracteristicas peso y color. El criterio para ordenar estos elementos de la colección en principio no es único. Podriamos ordenar por peso, por color, y a su vez de forma ascendente o descendente, etc. Si el tipo de elementos fuera otro distinto a Manzanas, por ejemplo Persona, se darían a su vez nuevos criterios.

Queda claro que existirán un sin fin de algoritmos o estrategias para indicar, dados dos elementos, cual debería preceder a cual en una hipotética ordenación.

La clase que ordena no tiene por qué conocer los detalles para discernir que elemento precede a otro, esta responsabilidad la puede delegar en otro objeto, su estrategia. Una vez pueda pedirle a su objeto estrategia que le indique, dados 2 elementos cualquiera, cual precede a cual, será capaz de ordenar una colección entera de elementos, a partir de los resultados que la estrategia le irá proporcionando cada vez que se le pida que compare 2 elementos de la colección para los cuales la clase que ordena necesite esta información durante el trascurso del algoritmo de ordenación que implementa internamente.

Comparer: La interfaz de la estrategia

Asi pues, la estrategia para comparar se define en los siguientes términos: una interfaz con un único método que recibe como argumentos dos referencias a los objetos a comparar y devuelve un valor menor que cero si el primero es menor, igual a cero sin son iguales y mayor que cero si el primero es mayor.

public interface Comparer<T> {

	int compare(T x, T y);

}

La interfaz es genérica en función del tipo T. Para comparar dos objetos tipo T se llama al método compare de la interfaz proporcionando como argumentos dos referencias a objetos tipo T y se obtiene un valor int. Cuando queramos comparar objetos Manzana, habrá que indicar como parámetro de tipo T el nombre de la clase: Apple.

La clase manzana: los items de la colección

Si utilizamos manzanas como elementos de la colección podemos definir la clase manzana mediante el siguiente código:

public class Apple {

	String color;
	double weight;

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public double getWeight() {
		return weight;
	}

	public void setWeight(double weight) {
		this.weight = weight;
	}

	public Apple(String color, double weight) {
		this.color = color;
		this.weight = weight;
	}

	@Override
	public String toString() {
		return "Apple [color=" + color + ", weight=" + weight + "]";
	}
}

Implementando la interfaz Comparer para definir una estrategia concreta

Podemos escribir una clase comparadora de manzanas, según su peso de menor a mayor, escribiendo una clase que implemente la interfaz IComparer y concretandola para T = Apple, es decir, implementando la inferfaz Comparer<Apple>.

public class ByWeightAppleComparer implements Comparer<Apple> {

	@Override
	public int compare(Apple x, Apple y) {		

if(x.getWeight() < y.getWeight()) return -1;
		if(x.getWeight() == y.getWeight()) return 0;
		if(x.getWeight() > y.getWeight()) return 1;

		return 0;
	}
}

La clase Sorter: delega en una estrategia de comparación

La clase que va a encargarse de realizar la ordenación de los elementos necesitará que se le proporcione una referencia a una intancia de un objeto de este tipo de estrategia (u otra cualquiera, son intercambiables objetos cuyo clase implemente la interfaz de la estrategia)
La clase Sorter utilizará como tipo del parámetro para recibir la referencia el tipo abstracto de la interfaz, no necesita conocer el tipo concreto para comunicarse con el objeto estrategia, utiliza la interfaz proporcionada por la interfaz abstracta común a todas las estrategias de comparación.

La clase Sorter será genérica para permitir ordenar colecciones de objetos de cualquier tipo especificado genericamente por T. Logicamente para ordenar colecciones de objetos de tipo T el sorter necesitará una estrategia comparadora de objetos de tipo T.

public class Sorter<T> {

	Comparer<T> comparer;

	// Interfaz para establecer la estrategia (un setter)
	public void setComparer(Comparer<T> comparer) {
		this.comparer = comparer;
	}

	public Sorter(Comparer<T> comparer) {
		this.comparer = comparer;
	}

	private int performComparison(T x, T y) {
		return comparer.compare(x, y);
	}

El método performComparison es el método en el cual la clase Sorter delega en la estrategia de comparación.
El campo comparer será de tipo Comparer de T, es decir, si se crea una instancia de Sorter concreta para elementos tipo manzana (T = Apple) Sorter, el comparador deberá ser a su vez un comparador de manzanas Comparer.

//Crear un sorter para ordenar manzanas por peso de menor a mayor
Sorter<Apple> appleSorter = new Sorter<Apple>(new ByWeightAppleComparer());

Y por último, el método de la clase Sorter que realiza la ordenación mediante el algoritmo de selección directa.

public void sort(T[] items) {

	for (int i = 0; i < items.length; i++) {
		// Asumir de partida que el valor menor de la parte aun no ordenada
		// del array esta en la primera posicion de la parte aun no ordenada
		int notOrderedLowerValueIndex = i;
		// Recorrer el resto del vector para encontrar otro menor
		for (int j = i + 1; j < items.length; j++)
			// Si se encuentra un valor menor en la posicion j
			if (performComparison(items[j],
					items[notOrderedLowerValueIndex]) < 0)
				notOrderedLowerValueIndex = j; // nueva posicion del menor j
			// Si el valor de la posicion i no estaba en su sitio
		// porque hemos encontrado en una posicion distinta otro valor aun
		// menor intercambiar las posiciones
		if (i != notOrderedLowerValueIndex) {
			T aux = items[i];
			items[i] = items[notOrderedLowerValueIndex];
			items[notOrderedLowerValueIndex] = aux;
		}
	}
}

En las lineas 20 y 30 se encuentra la llamada que necesita hacer el algoritmo de ordenación cuando necesita saber dados 2 elementos cual precede a cual. Para ello, llama al método performComparison de la clase, y este a su vez delega en el objeto estrategia elegido por el código cliente que instancia y utiliza un objeto de tipo Sorter.

El programa principal: hace de código cliente

El código cliente será, en el ejemplo, el método main de java.

public class Program {

	static Apple[] apples = new Apple[] { new Apple("verde", 125.0),
			new Apple("roja", 150.5), new Apple("amarilla", 200.0) };

	public static void main(String[] args) {

		//Crear un sorter para ordenar manzanas por peso de menor a mayor
		Sorter<Apple> appleSorter = new Sorter<Apple>(new ByWeightAppleComparer());
		appleSorter.sort(apples);
		printApples();

	private static void printApples() {
		for (Apple apple : apples) {
			System.out.println(apple.toString());
		}
	}

El resultado de ejecutar el código es el siguiente:

Apple [color=verde, weight=125.0]
Apple [color=roja, weight=150.5]
Apple [color=amarilla, weight=200.0]

El vector de manzanas ordenado por peso de menor a mayor.

La clase Sorter se puede reutilizar para ordenar manzanas por otros criterios (estrategias) o incluso otro tipo de objetos que no sean manzanas al ser génerica o parametrizada según el paramétro de clase T.