Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 32

UNIVERSIDAD DE LAS FUERZAS ARMADAS ESPE

Foro

ING. Fausto Meneses Becerra Mgs. I. S.


Fundamentos de Programación
NRC:

Integrantes:
Pérez Ronquillo Kevin Alexander
Rogel Malla Steven Alexis
Sotalin Males Francisco Sebastian
Yanacallo Casagualpa Kevin Omar
Yela Cabezas Bryan Gustavo

GRUPO: 3
PATRONES DE DISEÑO
Introducción
Son soluciones simples y elegantes a problemas específicos y comunes del diseño orientado
a objetos. Son soluciones basadas en la experiencia y que se ha demostrado que funcionan.
Un patrón es una descripción del problema y la esencia de su solución, que se puede
reutilizar en casos distintos.
Está claro que un equipo de desarrollo no se limita a aplicar los principios del diseño
orientado a objetos una vez tras otra, lo más seguro es que se aprovechen de las soluciones
aplicadas en el pasado para resolver los problemas del presente.
La solución a algunos problemas del desarrollo viene en forma de bloques de código y de
esqueletos de una solución, a estos bloques de código se les conoce como patrones.
Conceptos Generales
“Un patrón describe un problema que ocurre de forma iterativa en nuestro entorno, también
describe la solución a dicho problema de tal forma que podemos usar la misma solución
todas las veces que sea necesario”.
Los patrones de diseño son un conjunto de prácticas de óptimo diseño que se utilizan para
abordar problemas recurrentes en la programación orientada a objetos.
Importancia de los patrones de diseño
Los patrones de diseño son importantes por lo siguiente:
Proporcionan catálogos de elementos reusables en el diseño de sistemas software.
Evitan la reiteración en la búsqueda de soluciones a problemas ya conocidos y solucionados
anteriormente.
Formalizan un vocabulario común entre diseñadores.
Estandarizan el modo en que se realiza el diseño.
Facilitan el aprendizaje de las nuevas generaciones de diseñadores condensando
conocimiento ya existente.
Asimismo, no pretenden:
Imponer ciertas alternativas de diseño frente a otras.
Eliminar la creatividad inherente al proceso de diseño.
Tipos de patrones
Estructurales
Describen como los objetos y las clases pueden ser combinados para formar estructuras
más grandes, es decir, proporcionan los mecanismos para este hecho. La diferencia entre el
modelo de clases y el modelo de objetos es que las clases desarrollan la abstracción con la
ayuda de la herencia y como estos pueden ser más utilizados para proporcionar más utilidad
al programa de interfaz. Por otro lado los patrones estructurales describen como los objetos
pueden ser asociados y combinados para formar estructuras más grandes y más complejas.

De creación
Proporcionan ayuda a la hora de crear objetos, principalmente cuando esta creación
requiere tomar decisiones. La toma de decisiones utilizando patrones puede ser dinámica.
Estos patrones ayudan a estructurar y encapsular estas decisiones. En algunas ocasiones
existe más de un patrón que se puede aplicar a la misma situación. En otras ocasiones se
pueden combinar múltiples patrones convenientemente. Algunos de los patrones de
creación más utilizados son los siguientes:

De comportamiento
Tratan específicamente la comunicación entre los objetos.
Patrón de Cadena de Responsabilidad, Patrón de Comando, Patrón Intérprete, Patrón
Iterador, Patrón Mediador, Patrón Memento, Patrón Observador, Patrón de Estado, Patrón
de Estrategia, Patrón del Método Plantilla, Patrón Visitante.

3.3.3 Tipos de patrones


Patrones de creación
Este tipo de patrones nos permite la creación de objectos. Son los que facilitan la tarea de
creación de nuevos objetos, de tal forma que el proceso de creación pueda ser desacoplado
de la implementación del resto del sistema.
Los patrones creacionales están basados en dos conceptos:
1. Encapsular el conocimiento acerca de los tipos concretos que nuestro sistema
utiliza. Estos patrones normalmente trabajarán con interfaces, por lo que la
implementación concreta que utilicemos queda aislada.
2. Ocultar cómo estas implementaciones concretas necesitan ser creadas y cómo se
combinan entre sí.
Patrones de estructura
Los patrones de estructura permiten facilitan la organización de la estructura de clases y sus
relaciones.
El objetivo de los patrones de estructuración es facilitar la independencia de la interfaz de
un objeto o de un conjunto de objetos respecto de su implementación. En el caso de un
conjunto de objetos, se trata también de hacer que esta interfaz sea independiente de la
jerarquía de clases y de la composición de los objetos.
Patrones de comportamiento
Estos proporcionan soluciones para organizar las interacciones y repartir procesamiento
entre objectos.
El diseñador de un sistema orientado a objetos se enfrenta a menudo al problema del
descubrimiento de objetos. Esto puede realizarse a partir de los dos aspectos siguientes:
 La estructuración de los datos.
 La distribución de los procesamientos 

3.4 Patrones de creación


Son los que facilitan la tarea de creación de nuevos objetos, de tal forma que el proceso de
creación pueda ser desacoplado de la implementación del resto del sistema.
Los patrones creacionales están basados en dos conceptos:
1. Encapsular el conocimiento acerca de los tipos concretos que nuestro sistema
utiliza. Estos patrones normalmente trabajarán con interfaces, por lo que la
implementación concreta que utilicemos queda aislada.

2. Ocultar cómo estas implementaciones concretas necesitan ser creadas y cómo se


combinan entre sí.
Los patrones creacionales más conocidos son:
 Abstract Factory: Nos provee una interfaz que delega la creación de un conjunto de
objetos relacionados sin necesidad de especificar en ningún momento cuáles son las
implementaciones concretas.
 Factory Method: Expone un método de creación, delegando en las subclases la
implementación de este método.
 Builder: Separa la creación de un objeto complejo de su estructura, de tal forma que
el mismo proceso de construcción nos puede servir para crear representaciones
diferentes.
 Singleton: limita a uno el número de instancias posibles de una clase en nuestro
programa, y proporciona un acceso global al mismo.
 Prototype: Permite la creación de objetos basados en “plantillas”. Un nuevo objeto
se crea a partir de la clonación de otro objeto.
Singleton
Es un patrón de diseño creacional que garantiza que tan solo exista un objeto de su tipo y
proporciona un único punto de acceso a él para cualquier otro código.
El patrón tiene prácticamente los mismos pros y contras que las variables globales. Aunque
son muy útiles, rompen el modularidad de tu código.
No se puede utilizar una clase que dependa del Singleton en otro contexto. Tendrás que
llevar también la clase Singleton. La mayoría de las veces, esta limitación aparece durante
la creación de pruebas de unidad.

No se puede utilizar una clase que dependa del Singleton en otro contexto. Tendrás que
llevar también la clase Singleton. La mayoría de las veces, esta limitación aparece durante
la creación de pruebas de unidad:
1. Garantizar que una clase tenga una única instancia:
El motivo más habitual es controlar el acceso a algún recurso compartido, por
ejemplo, una base de datos o un archivo.
imagina que has creado un objeto y al cabo de un tiempo decides crear otro nuevo.
En lugar de recibir un objeto nuevo, obtendrás el que ya habías creado.
2. Proporcionar un punto de acceso global a dicha instancia:
Aunque son muy útiles, también son poco seguras, ya que cualquier código podría
sobrescribir el contenido de esas variables y descomponer la aplicación.
Al igual que una variable global, el patrón Singleton nos permite acceder a un
objeto desde cualquier parte del programa. No obstante, también evita que otro
código sobrescriba esa instancia.
Hoy en día el patrón Singleton se ha popularizado tanto que la gente suele llamar singleton
a cualquier patrón, incluso si solo resuelve uno de los problemas antes mencionados.
Todas las implementaciones del patrón Singleton tienen estos dos pasos en común:
1. Hacer privado el constructor por defecto para evitar que otros objetos utilicen el
operador new con la clase Singleton.
2. Crear un método de creación estático que actúe como constructor. Tras bambalinas,
este método invoca al constructor privado para crear un objeto y lo guarda en un
campo estático. Las siguientes llamadas a este método devuelven el objeto
almacenado en caché.
Patrones de Estructura
Los patrones de diseño más utilizados se clasifican en tres categorías principales, cada
patrón de diseño individual conforma un total de 23 patrones de diseño. Las cuatro
categorías principales son:
 Patrones creacionales
 Patrones estructurales
 Patrones de comportamiento
Patrones estructurales:
Facilitan soluciones y estándares eficientes con respecto a las composiciones de clase y las
estructuras de objetos. Aquí podemos encontrar:
 Adapter: Se utiliza para vincular dos interfaces que no son compatibles y
utilizan sus funcionalidades.
 Bridge: En este patrón hay una alteración estructural en las clases principales y
de implementador de interfaz sin tener ningún efecto entre ellas.
 Composite: Se usa para agrupar objetos como un solo objeto.
 Decorator: Este patrón restringe la alteración de la estructura del objeto mientras
se le agrega una nueva funcionalidad
 Facade: Proporciona una interfaz simplificada para una biblioteca, un marco o
cualquier otro conjunto complejo de clases.
 Flyweight: El patrón Flyweight se usa para reducir el uso de memoria y mejorar
el rendimiento al reducir la creación de objetos.
 Proxy: Se utiliza para crear objetos que pueden representar funciones de otras
clases u objetos y la interfaz se utiliza para acceder a estas funcionalidades.

PATRONES DE ESTRUCTURAS
Los patrones estructurales se enfocan en como las clases y objetos se componen para
formar estructuras mayores, los patrones estructurales describen como las estructuras
compuestas por clases crecen para crear nuevas funcionalidades de manera de agregar a la
estructura flexibilidad y que la misma pueda cambiar en tiempo de ejecución lo cual es
imposibles con una composición de clases estáticas.

Tipos de patrones estructurales


1. Adapter: Adapta una interfaz para que pueda ser utilizada por una clase que de otro
modo no podría utilizarla.
2. Bridge: Desacopla una abstracción de su implementación.
3. Decorator: Añade funcionalidad a una clase dinámica.
4. Facade: Provee de una interfaz unificada simple para acceder a una interfaz o grupo
de interfaces de una subsistema.
5. Flyweight: Reduce la redundancia cuando gran cantidad de objetos poseen idéntica
información.
6. Proxy: Porporciona un intermediario de un objeto para controlar su acceso.
7. Composite: Permite tratar objetos compuestos como su de uno simple se tratase.
8. Brige: Separa la abstracción de la implementación

Composición (Composite)
Es un patrón estructural que se utiliza para componer objetos en estructuras de árbol que
representan jerarquías todo-parte.
Este patrón permite tratar uniformemente a los objetos y a las composiciones y es un buen
ejemplo de utilización conjunta de herencia y composición.
Elementos que lo componen
1. Componente abstracto: Declara la interfaz para los objetos en la composición
implementando su comportamiento por defecto. Además, declara la interfaz para
acceder y manipular los componentes hijos.
2. Componente concreto: Define el comportamiento de los objetos elementales de la
composición (aquellos que no pueden tener hijos.)
3. Composición (Composite): Define el comportamiento de los objetos compuestos
de la composición.
4. Cliente: Maneja a los objetos a través del interfaz Componente.

Ventajas
El patrón composición permite tratar a los objetos básicos y a los objetos compuestos de
manera uniforme, facilitando la introducción de nuevos componentes sin afectar al cliente.
Desventajas
Para restringir qué componentes concretos pueden ir en una determinada composición es
necesario añadir comprobaciones en tiempo de ejecución.

Adaptador
Los interfaces se utilizan para desacoplar las clases clientes de las clases que ofrecían
ciertos servicios.
De esta forma se hace independiente de la clase cliente de la clase servidora, sin embargo,
se fuerza a que la clase servidora implementara un determinado interfaz.
En diversas ocasiones puede no interesar modificar la clase servidora, bien porque no se
puede (porque no se dispone del código fuente) o porque no se quiere modificar la clase
para determinado uso concreto y especializado.
Para convertir el interfaz de una clase en otro interfaz que es el esperado por el cliente que
usa dicha clase se utiliza el patrón adaptador.
Elementos que lo componen
1. Objeto (Target): Interfaz que utiliza el cliente.
2. Cliente (Client): Colabora con los objetos usando la interfaz objetivo.
3. Adaptado (Adapted): Define una interfaz existente que necesita ser adaptada.
4. Adaptador (Adapter): adaptar la interfaz del adaptador a la interfaz Objeto

Esquema herencia múltiple


El objeto adaptador herencia el interfaz de objeto al mismo tiempo que hereda de la clase
Adaptado el método PeticiónEspecífica.

Esquema composición
El objeto adaptador hereda el interfaz de Objetivo y está compuesto de un objeto del tipo
Adaptado a cuyo método PeticiónEspecífica delega las peticiones que le llegan del cliente.
Objetivo puede ser un interface o una clase abstracta.
Ventajas e inconvenientes
Herencia múltiple:
Solo permite adaptar una clase, no sus subclases.
Permite que el adaptador sobrescriba parte de la conducta del adaptado (ya que es una
subclase).
Además, con la herencia nos evitamos la dirección que se produce en la composición.
Composición:
Permite que un único adaptador trabaje sobre muchos adaptados (una clase y sus
subclases).
Introduce un nivel más de indirección, lo cual implica una penalización en rendimiento.
Complica la sobreescritura de la conducta del adaptado (se necesita crear una subclase del
adaptado y que el adaptador se refiera a esta subclase)
Patrones de comportamiento
 Estos proporcionan soluciones para organizar las interacciones y repartir
procesamiento entre objectos.
 Caracterizan las formas en las que interactúan y reparten responsabilidades las
distintas clases u objetos.

El diseñador de un sistema orientado a objetos se enfrenta a menudo al problema del


descubrimiento de objetos. Esto puede realizarse a partir de los dos aspectos siguientes:

 La estructuración de los datos.


 La distribución de los procesamientos

Chain of Responsibility
Permite pasar solicitudes a lo largo de una cadena de manejadores. Al recibir una solicitud,
cada manejador decide si la procesa o si la pasa al siguiente manejador de la cadena.

Command 

Convierte una solicitud en un objeto independiente que contiene toda la información sobre
la solicitud. Esta transformación te permite parametrizar los métodos con diferentes
solicitudes, retrasar o poner en cola la ejecución de una solicitud y soportar operaciones que
no se pueden realizar.

Iterator

Permite recorrer elementos de una colección sin exponer su representación subyacente


(lista, pila, árbol, etc.).

Mediator 

Permite reducir las dependencias caóticas entre objetos. El patrón restringe las
comunicaciones directas entre los objetos, forzándolos a colaborar únicamente a través de
un objeto mediador.

Memento

Permite guardar y restaurar el estado previo de un objeto sin revelar los detalles de su
implementación.

Observer

Permite definir un mecanismo de suscripción para notificar a varios objetos sobre cualquier
evento que le suceda al objeto que están observando.

State

Permite a un objeto alterar su comportamiento cuando su estado interno cambia. Parece


como si el objeto cambiara su clase.

Strategy

Permite definir una familia de algoritmos, colocar cada uno de ellos en una clase separada y
hacer sus objetos intercambiables.

Template Method 

Define el esqueleto de un algoritmo en la super clase pero permite que las subclases
sobrescriban pasos del algoritmo sin cambiar su estructura. El patrón Template Method
sugiere que dividas un algoritmo en una serie de pasos, conviertas estos pasos en métodos y
coloques una serie de llamadas a esos métodos dentro de un único método plantilla. Los
pasos pueden ser abstractos, o contar con una implementación por defecto. Para utilizar el
algoritmo, el cliente debe aportar su propia subclase, implementar todos los pasos
abstractos y sobrescribir algunos de los opcionales si es necesario (pero no el propio
método plantilla).

Visitor 

Permite separar algoritmos de los objetos sobre los que operan. El patrón Visitor sugiere
que coloques el nuevo comportamiento en una clase separada llamada visitante, en lugar de
intentar integrarlo dentro de clases existentes. El objeto que originalmente tenía que realizar
el comportamiento se pasa ahora a uno de los métodos del visitante como argumento, de
modo que el método accede a toda la información necesaria contenida dentro del objeto.

Ejemplo de Chain of Responsibility:

middleware/Middleware.java: Interfaz de validación básica

package refactoring_guru.chain_of_responsibility.example.middleware;

/**
* Base middleware class.
*/
public abstract class Middleware {
private Middleware next;

/**
* Builds chains of middleware objects.
*/
public Middleware linkWith(Middleware next) {
this.next = next;
return next;
}

/**
* Subclasses will implement this method with concrete checks.
*/
public abstract boolean check(String email, String password);

/**
* Runs check on the next object in chain or ends traversing if we're in
* last object in chain.
*/
protected boolean checkNext(String email, String password) {
if (next == null) {
return true;
}
return next.check(email, password);
}
}

middleware/ThrottlingMiddleware.java: Comprueba el límite de cantidad de


solicitudes

package refactoring_guru.chain_of_responsibility.example.middleware;

/**
* ConcreteHandler. Checks whether there are too many failed login requests.
*/
public class ThrottlingMiddleware extends Middleware {
private int requestPerMinute;
private int request;
private long currentTime;

public ThrottlingMiddleware(int requestPerMinute) {


this.requestPerMinute = requestPerMinute;
this.currentTime = System.currentTimeMillis();
}

/**
* Please, not that checkNext() call can be inserted both in the beginning
* of this method and in the end.
*
* This gives much more flexibility than a simple loop over all middleware
* objects. For instance, an element of a chain can change the order of
* checks by running its check after all other checks.
*/
public boolean check(String email, String password) {
if (System.currentTimeMillis() > currentTime + 60_000) {
request = 0;
currentTime = System.currentTimeMillis();
}

request++;

if (request > requestPerMinute) {


System.out.println("Request limit exceeded!");
Thread.currentThread().stop();
}
return checkNext(email, password);
}
}
 middleware/UserExistsMiddleware.java: Comprueba las credenciales del usuario
package refactoring_guru.chain_of_responsibility.example.middleware;
import refactoring_guru.chain_of_responsibility.example.server.Server;

/**
* ConcreteHandler. Checks whether a user with the given credentials exists.
*/
public class UserExistsMiddleware extends Middleware {
private Server server;

public UserExistsMiddleware(Server server) {


this.server = server;
}

public boolean check(String email, String password) {


if (!server.hasEmail(email)) {
System.out.println("This email is not registered!");
return false;
}
if (!server.isValidPassword(email, password)) {
System.out.println("Wrong password!");
return false;
}
return checkNext(email, password);
}
}
 middleware/RoleCheckMiddleware.java: Comprueba el papel del usuario
package refactoring_guru.chain_of_responsibility.example.middleware;

/**
* ConcreteHandler. Checks a user's role.
*/
public class RoleCheckMiddleware extends Middleware {
public boolean check(String email, String password) {
if (email.equals("[email protected]")) {
System.out.println("Hello, admin!");
return true;
}
System.out.println("Hello, user!");
return checkNext(email, password);
}
}
 server
 server/Server.java: Objetivo de la autorización
package refactoring_guru.chain_of_responsibility.example.server;

import refactoring_guru.chain_of_responsibility.example.middleware.Middleware;
import java.util.HashMap;
import java.util.Map;

/**
* Server class.
*/
public class Server {
private Map<String, String> users = new HashMap<>();
private Middleware middleware;

/**
* Client passes a chain of object to server. This improves flexibility and
* makes testing the server class easier.
*/
public void setMiddleware(Middleware middleware) {
this.middleware = middleware;
}

/**
* Server gets email and password from client and sends the authorization
* request to the chain.
*/
public boolean logIn(String email, String password) {
if (middleware.check(email, password)) {
System.out.println("Authorization have been successful!");

// Do something useful here for authorized users.

return true;
}
return false;
}

public void register(String email, String password) {


users.put(email, password);
}

public boolean hasEmail(String email) {


return users.containsKey(email);
}

public boolean isValidPassword(String email, String password) {


return users.get(email).equals(password);
}
}
 Demo.java: Código cliente
package refactoring_guru.chain_of_responsibility.example;
import refactoring_guru.chain_of_responsibility.example.middleware.Middleware;
import
refactoring_guru.chain_of_responsibility.example.middleware.RoleCheckMiddleware;
import
refactoring_guru.chain_of_responsibility.example.middleware.ThrottlingMiddleware;
import
refactoring_guru.chain_of_responsibility.example.middleware.UserExistsMiddleware;
import refactoring_guru.chain_of_responsibility.example.server.Server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* Demo class. Everything comes together here.
*/
public class Demo {
private static BufferedReader reader = new BufferedReader(new
InputStreamReader(System.in));
private static Server server;

private static void init() {


server = new Server();
server.register("[email protected]", "admin_pass");
server.register("[email protected]", "user_pass");

// All checks are linked. Client can build various chains using the same
// components.
Middleware middleware = new ThrottlingMiddleware(2);
middleware.linkWith(new UserExistsMiddleware(server))
.linkWith(new RoleCheckMiddleware());

// Server gets a chain from client code.


server.setMiddleware(middleware);
}

public static void main(String[] args) throws IOException {


init();

boolean success;
do {
System.out.print("Enter email: ");
String email = reader.readLine();
System.out.print("Input password: ");
String password = reader.readLine();
success = server.logIn(email, password);
} while (!success);
}
}

OutputDemo.txt: Resultado de la ejecución

Enter email: [email protected]


Input password: admin_pass
Hello, admin!
Authorization have been successful!

Enter email: [email protected]


Input password: user_pass
Hello, user!
Authorization have been successful!
Patrones de comportamiento
Finalmente, los patrones de comportamiento son quienes proporcionan las soluciones para
organizar los datos y los objetos, adicionalmente, establecen el orden entre las interacciones
que se producen en estos últimos. Dentro de este grupo de patrones podemos encontrar los
siguientes:
Chain of resaponbility:Como su nombre lo indica, este patrón se refiere a la creación de una
cadena de objetos, de forma que cuando uno de ellos no pueda responder a una instrucción,
el siguiente eslabón logre dar la correspondiente respuesta.
Command:El principal objetivo de este patrón es convertir una consulta en un objeto, de tal
manera que las operaciones básicas relacionadas sean más fáciles de realizar. Es decir, su
función es transformar una solicitud en un objeto.
Interpreter:Se refiere a la posibilidad de ofrecer la representación de un lenguaje a través de
objetos gramaticales. Esto con la finalidad de que las expresiones escritas de ese lenguaje
puedan ser evaluadas e interpretadas fácilmente.
Iterator:Su principal función es permitir el acceso secuencial a un grupo de objetos, sin la
necesidad de que los usuarios del sistema conozcan los aspectos relacionados con la
implementación de los mismos.
Mediator:Tal y como su nombre lo indica, este patrón se encarga de construir un objeto que
actúe como mediador entre las interacciones de los objetos sin que estos interactúen
directamente.
Memento:En términos generales, es uno de los patrones de mayor importancia, pues es él
quien se encarga de restaurar y resguardar el estado de los objetos. Al respecto, debemos
advertir que una condición básica dentro de este patrón es el respeto a la encapsulación de
las clases.
Observer:La principal función de este patrón es hacer que las modificaciones ocurridas en
el sujeto sean notificadas a los observadores, a fines de que estos puedan actualizar sus
estados.
State:El objetivo de este patrón es bastante fácil de entender, pues él se encarga de ofrecer
la oportunidad de que un objeto pueda modificar su comportamiento en base a su estado
interno.
Strategy:Facilita la adaptación entre el comportamiento y los algoritmos de un objeto,
dependiendo de la existencia o no de una necesidad concreta por parte del sistema. Al
respecto, las interacciones entre el objeto y los usuarios del sistema no deben alterarse
durante la aplicación de este patrón.
Template method:Este patrón se encarga de realizar el correspondiente reporte acerca de las
etapas que conforman las operaciones de un objeto, las cuales se encuentran descritas en las
subclases. En otras palabras, se trata de delegar en las subclases algunas etapas que
conforman las operaciones de un objeto.
Visitor:Se encarga de la construcción de una operación requerida relacionada con los
elementos de una serie de objetos, sin que ocurra alguna modificación en las clases de los
mismos. En resumen, este patrón es el encargado de añadir nuevas operaciones a una serie
de objetos.

Strategy

La mayor parte de los problemas que nos podemos encontrar al usar patrones de diseño
vienen de no ser capaces de reconocer en qué contextos hay que aplicarlos. Caemos en la
trampa de, al intentar escribir código mejor, generar deuda técnica, ya que el patrón
aplicado no resolvía el problema que teníamos en realidad. Es por eso que la pregunta más
importante que tenemos que responder cuando empezamos a estudiar un patrón de diseño
es: ¿para qué sirve?.

El patrón Strategy tiene sentido cuando nos encontramos en un escenario en el que para
conseguir un objetivo, tenemos diferentes formas de afrontarlo. Por poner un ejemplo,
imaginemos que estamos desarrollando un programa que añade estilos en formato HTML a
un texto. Los estilos que soporta serán: poner en negrita, letra cursiva y subrayar.

Una solución que podríamos dar sería:


public enum Styles
{
Bold,
Italic,
Underline
}
public class Styler
{
public string SetStyle(string input, Styles style)
{
switch (style)
{
case Styles.Bold:
return "<b>" + input + "</b>";
case Styles.Italic:
return "<i>" + input + "</i>";
case Styles.Underline:
return "<u>" + input + "</u>";
default:
throw new NotImplementedException("This style is not supported");
}
}
}

Como podemos ver, hemos usado un bloque de tipo “switch” y en dependencia del estilo
que queramos añadirle al texto de entrada (“input”), realizamos una acción u otra. El
resultado final es que si dentro de dos días tenemos el requerimiento de añadir dos o tres
estilos nuevos a nuestro sistema, tendremos que añadir más bloques “case” a nuestro
código. Esto en definitiva, lo que provocaría es que no estuviéramos cumpliendo con el
principio SOLID de Open Closed.

Este principio señala que nuestro código debería estar abierto a la extensión, pero cerrado a
la modificación. Es decir, que para añadir una nueva funcionalidad, no tengamos la
necesidad de modificar los algoritmos que ya están programados. Y es aquí es donde el
patrón strategy cobra su importancia.

Con el fin de no tener que modificar nuestro bloque “switch” con cada nuevo estilo que
queramos aplicar al texto, vamos a dividir cada uno de los estilos existentes, en clases más
pequeñas, que solo resuelvan un estilo cada una:

public class BoldStyler


{
public string SetStyle(string input)
{
return "<b>" + input + "</b>";
}
}
public class ItalicStyler
{
public string SetStyle(string input)
{
return "<i>" + input + "</i>";
}
}
public class UnderlineStyler
{
public string SetStyle(string input)
{
return "<u>" + input + "</u>";
}
}

Como podemos ver, todas las clases son muy parecidas y podríamos sacar una interface
común que defina su comportamiento:

public interface IStyler


{
string SetStyle(string input);
}
public class BoldStyler : IStyler
//...
public class ItalicStyler : IStyler
//...
public class UnderlineStyler : IStyler
//...

Ahora tendríamos que modificar el código inicial para que use estas nuevas clases y seguir
resolviendo el problema:

public class Styler


{
public string SetStyle(string input, IStyler styler)
{
return styler.SetStyle(input);
}
}
Y si por un casual, ahora necesitáramos añadir un nuevo formato a nuestro programa, solo
tendríamos que desarrollar un nuevo objeto que implementara “IStyler”. Y así evitaríamos
tener que modificar el código que ya tenemos escrito.

Dando un repaso a los objetos que hemos ido desarrollando hasta este momento, podríamos
extraer un diagrama de clases como este:

Donde vemos que nuestro objeto “Styler” consume objetos que implementan “IStyler”
como son “BoldStyler”, “ItalicStyler”, … . Un diagrama semejante al que define el patrón
Strategy:

Donde los artefactos son:

Context: es el objeto que orquesta el funcionamiento de las estrategias. Puede recibir una de
estas por parámetro o gestionarlas internamente.
IStrategy: es la definición común que tiene que implementar todo algoritmo que soporte el
sistema.
ConcreteStrategyX: los objetos que, implementando la interface común, desarrollan un
algoritmo concreto.
En este primer ejemplo hemos creado un contexto que acepta recibir una estrategia como
parámetro, pero quizá, el problema podría ser que obligatoriamente hay que aplicar los tres
formatos al texto que le pasemos. En este caso podríamos generar una clase de contexto
como esta:

public class Styler


{
private readonly List<IStyler> strategies = new List<IStyler>
{
new BoldStyler(),
new ItalicStyler(),
new UnderlineStyler()
};
public string SetStyle(string input)
{
var result = input;
foreach(var strategy in this.strategies)
{
result += strategy.SetStyle(result);
}
return result;
}
}
Y si usáramos alguna Framework de inyección de dependencias como Autofac,
StructureMaps o NInject, podríamos aprovecharnos de los sistemas de escaneo de
ensamblados para poder obtener en el constructor todas las estrategias de un tipo
correspondiente:

public class Styler


{
private readonly IEnumerable<IStyler> strategies;
public Styler(IEnumerable<IStyler> strategies)
{
this.strategies = strategies;
}
public string SetStyle(string input)
{
var result = input;
foreach(var strategy in this.strategies)
{
result += strategy.SetStyle(result);
}
return result;
}
}
Una de las grandes ventajas del Strategy Pattern es que no es exclusivo de c# o la
plataforma .Net, puede ser usado con diferentes lenguajes de programación, como por
ejemplo Javascript. Una implementación de este mismo código podría ser esta:

function HtmlStyler() {
this.setStyle = function (input) {
var result = input;
for (var key in this.strategies) {
var strategy = this.strategies[key];
if (strategy.setStyle)
result = strategy.setStyle(result);
else
throw "Invalid strategy";
}
return result;
};
}
HtmlStyler.prototype.strategies = { };
HtmlStyler.prototype.strategies.boldStyler = {
setStyle: function(input) {
return '<b>' + input + '</b>';
},
};
HtmlStyler.prototype.strategies.italicStyler = {
setStyle: function (input) {
return '<i>' + input + '</i>';
},
};
HtmlStyler.prototype.strategies.underlineStyler = {
setStyle: function (input) {
return '<u>' + input + '</u>';
},
};
La peculiaridad de esta implentación en Javascript es el uso de “prototype” para facilitar las
futuras características nuevas que se pueden desarrollar. No hará falta modificar el
programa original, si no añadir una nueva propiedad a las estrategias del prototipo de
nuestro objeto.

Y cómo no, también podríamos realizar el mismo código en el lenguaje de programación de


moda, Typescript:
interface IStyler {
setStyle: (input: string) => string;
}
class HTMLStyler {
strategies: IStyler[];
constructor (strategies: IStyler[]) {
this.strategies = strategies;
}
setStyle(input: string):string {
var result: string = input;
for (var i = 0; i < this.strategies.length; i++) {
var strategy : IStyler = this.strategies[i];
result = strategy.setStyle(result);
}
return result;
}
}
class BoldStyler implements IStyler {
setStyle(input: string) {
return '<b>' + input + '</b>';
}
}

class ItalicStyler implements IStyler {


setStyle(input: string) {
return '<i>' + input + '</i>';
}
}
class UnderlineStyler implements IStyler {
setStyle(input: string) {
return '<u>' + input + '</u>';
}
}
var styler = new HTMLStyler([new BoldStyler(), new ItalicStyler(), new
UnderlineStyler()]);
alert(styler.setStyle('hola!'));
Y podremos ver claramente que el resultado final se asemeja más al que desarrollamos con
c#, que a la solución propuesta en javascript.

Mientras describíamos el patrón Strategy hemos dejado caer alguno de sus beneficios,
como por ejemplo que es más fácil de leer el código. También será más fácil por tanto de
mantener y por supuesto de ampliar con nuevas funcionalidades. Gracias a este patrón
vamos a cumplir con dos de los principios de SOLID: el principio de responsabilidad única,
al crear pequeñas clases que contienen un algoritmo muy concreto; y el de abierto/cerrado,
abriendo nuestra solución a la extensión pero no a la modificación.

También queda claro el problema que podría suponer a largo plazo: una gran cantidad
nueva de objetos para que sean gestionados por el hilo principal de nuestro programa. Por
lo que para un sistema de tiempo real o donde la velocidad de respuesta y el poco consumo
de recursos, fueran lo más importante, no sería la implementación ideal.
EJEMPLO STRATEGY:

Medio de pago en una aplicación de comercio electrónico


En este ejemplo, el patrón Strategy se utiliza para implementar los distintos medios de pago
de una aplicación de comercio electrónico. Una vez seleccionado el producto a comprar, un
cliente elige un medio de pago: Paypal o tarjeta de crédito.

Las estrategias concretas no solo realizan el propio pago, sino que además alteran el
comportamiento del formulario de pago, proporcionando campos adecuados para el registro
de los datos del pago.

package refactoring_guru.strategy.example.strategies;
/**
* Common interface for all strategies.
*/
public interface PayStrategy {
boolean pay(int paymentAmount);
void collectPaymentDetails();
}
strategies/PayByPayPal.java: Pago con PayPal
package refactoring_guru.strategy.example.strategies;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
/**
* Concrete strategy. Implements PayPal payment method.
*/
public class PayByPayPal implements PayStrategy {
private static final Map<String, String> DATA_BASE = new HashMap<>();
private final BufferedReader READER = new BufferedReader(new
InputStreamReader(System.in));
private String email;
private String password;
private boolean signedIn;
static {
DATA_BASE.put("amanda1985", "[email protected]");
DATA_BASE.put("qwerty", "[email protected]");
}
/**
* Collect customer's data.
*/
@Override
public void collectPaymentDetails() {
try {
while (!signedIn) {
System.out.print("Enter the user's email: ");
email = READER.readLine();
System.out.print("Enter the password: ");
password = READER.readLine();
if (verify()) {
System.out.println("Data verification has been successful.");
} else {
System.out.println("Wrong email or password!");
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
private boolean verify() {
setSignedIn(email.equals(DATA_BASE.get(password)));
return signedIn;
}

/**
* Save customer data for future shopping attempts.
*/
@Override
public boolean pay(int paymentAmount) {
if (signedIn) {
System.out.println("Paying " + paymentAmount + " using PayPal.");
return true;
} else {
return false;
}
}
private void setSignedIn(boolean signedIn) {
this.signedIn = signedIn;
}
}
EJEMPLO ADAPTER:

El patrón Adapter finge ser una pieza redonda con un radio igual a la mitad del diámetro
del cuadrado (en otras palabras, el radio del círculo más pequeño en el que quepa la pieza
cuadrada).
// Digamos que tienes dos clases con interfaces compatibles:
// RoundHole (HoyoRedondo) y RoundPeg (PiezaRedonda).
class RoundHole is
constructor RoundHole(radius) { ... }
method getRadius() is
// Devuelve el radio del agujero.
method fits(peg: RoundPeg) is
return this.getRadius() >= peg.getRadius()
class RoundPeg is
constructor RoundPeg(radius) { ... }
method getRadius() is
// Devuelve el radio de la pieza.

// Pero hay una clase incompatible: SquarePeg (PiezaCuadrada).


class SquarePeg is
constructor SquarePeg(width) { ... }
method getWidth() is
// Devuelve la anchura de la pieza cuadrada.
// Una clase adaptadora te permite encajar piezas cuadradas en
// hoyos redondos. Extiende la clase RoundPeg para permitir a
// los objetos adaptadores actuar como piezas redondas.
class SquarePegAdapter extends RoundPeg is
// En realidad, el adaptador contiene una instancia de la
// clase SquarePeg.
private field peg: SquarePeg
constructor SquarePegAdapter(peg: SquarePeg) is
this.peg = peg
method getRadius() is
// El adaptador simula que es una pieza redonda con un
// radio que pueda albergar la pieza cuadrada que el
// adaptador envuelve.
return peg.getWidth() * Math.sqrt(2) / 2
// En algún punto del código cliente.
hole = new RoundHole(5)
rpeg = new RoundPeg(5)
hole.fits(rpeg) // verdadero
small_sqpeg = new SquarePeg(5)
large_sqpeg = new SquarePeg(10)
hole.fits(small_sqpeg) // esto no compila (tipos incompatibles)
small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg)
large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg)
hole.fits(small_sqpeg_adapter) // verdadero
hole.fits(large_sqpeg_adapter) // falso

EJEMPLO SINGLETON:

Una vez que tenemos claro cual es el concepto de Configurador vamos a crearlo en
código .En este caso nuestro configurador almacenará dos valores url, y base de datos que
serán compartidos por el resto de Clases de la aplicación.
package com.arquitecturajava;
public class Configurador {
private String url;
private String baseDatos;
private static final Configurador miconfigurador;
public static Configurador getConfigurador(String url,String baseDatos) {
if (miconfigurador==null) {
miconfigurador=new Configurador(url,baseDatos);
}
return miconfigurador;
}
private Configurador(String url,String baseDatos){
this.url=url;
this.baseDatos=baseDatos;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getBaseDatos() {
return baseDatos;
}
public void setBaseDatos(String baseDatos) {
this.baseDatos = baseDatos;
}
}
Para conseguir que una clase sea de tipo Singleton necesitamos en primer lugar que su
constructor sea privado. De esa forma ningún programa será capaz de construir objetos de
esta tipo . En segundo lugar necesitaremos disponer de una variable estatica privada que
almacene una referencia al objeto que vamos a crear a traves del constructor . Por ultimo un
método estático publico que se encarga de instanciar el objeto la primera vez y almacenarlo
en la variable estática.
Una vez aclarado como funciona un Singleton es muy sencillo utilizarle desde un programa
ya que basta con invocar al método estático.
package com.arquitecturajava;
public class Principal {
public static void main(String[] args) {
Configurador c= Configurador.getConfigurador("miurl", "mibaseDatos");
System.out.println(c.getUrl());
System.out.println(c.getBaseDatos());
}
}
Conclusiones:
1. Los patrones de estructuras son muy utilizados para ayudar al programador a tener
un código más flexible.
2. Al seguir los patrones de estructura garantizamos que los programas que
desarrollamos sean mantenibles y más completos, por lo que esto de desarrollo de
software resulta ser un tema importante y que ocuparemos en la vida profesional.

Recomendaciones:
1. Es recomendable seguir al pie de la letra los patrones de estructuras ya que son de
gran ayuda para evitar problemas, pero es importante aclarar que no son necesarios.
2. Tener claro los temas de desarrollo de software resulta ser beneficioso para quienes
estudiamos carreras relacionadas con la tecnología, pues que por ejemplo saber
sobre patrones de diseño hará que nuestros trabajos universitarios o incluso
laborales sean más profesionales y funcionales.

Fuentes de consulta:
1. https://highscalability.wordpress.com/2010/04/12/patrones%C2%A0estructurales/
2. http://quegrande.org/apuntes/EI/OPT/POO/teoria/07-08/tema_6_-_patrones.pdf
3. https://refactoring.guru/es/design-patterns/adapter
4. https://www.arquitecturajava.com/ejemplo-de-java-singleton-patrones-classloaders/
5. https://refactoring.guru/es/design-patterns/singleton

También podría gustarte