Está en la página 1de 6

Proyecto 1

Análisis y diseño II

Temas

1. Diseño con interfaces.

2. Especificación y uso de contratos.

3. Separación de intereses y responsabilidades.

Instrucciones

 Investigar y crear un ejemplo para cada uno de los temas anteriores.

 Puede consultar al profesor durante la semana si los temas investigados son


correctos.

Diseño con interfaces

En la programación orientada a objetos, una interfaz es una descripción abstracta de un


conjunto de métodos que un objeto debe implementar. Define la forma en que los objetos
interactúan entre sí y proporciona un contrato para las clases que la implementan. Las
interfaces permiten establecer una separación clara entre la especificación y la
implementación de un objeto, lo que facilita el modularidad, el reusó de código y la
flexibilidad.

El diseño con interfaces implica diseñar las interacciones y comportamientos esperados de


los objetos a través de interfaces bien definidas. Esto promueve la encapsulación y la
abstracción, ya que los detalles de implementación se ocultan detrás de la interfaz, y los
objetos pueden interactuar entre sí sin conocer los detalles internos de los demás. Además,
el diseño con interfaces fomenta la programación basada en contratos y ayuda a garantizar
un acoplamiento débil entre los componentes del sistema.

Ejemplo

// Interfaz para los libros


public interface Libro {
void prestar();
void devolver();
boolean estaPrestado();
}

// Clase que implementa la interfaz Libro


public class LibroImpl implements Libro {
private boolean prestado;

public void prestar() {


if (!prestado) {
prestado = true;
System.out.println("El libro ha sido prestado.");
} else {
System.out.println("El libro ya está prestado.");
}
}

public void devolver() {


if (prestado) {
prestado = false;
System.out.println("El libro ha sido devuelto.");
} else {
System.out.println("El libro ya está disponible.");
}
}

public boolean estaPrestado() {


return prestado;
}
}

// Clase de prueba
public class Biblioteca {
public static void main(String[] args) {
Libro libro = new LibroImpl();

libro.prestar();
System.out.println("¿Está prestado? " + libro.estaPrestado());

libro.devolver();
System.out.println("¿Está prestado? " + libro.estaPrestado());
}
}
En este ejemplo, tenemos una interfaz llamada Libro que define los métodos prestar(),
devolver() y estaPrestado(). Luego, implementamos la interfaz en la clase LibroImpl,
donde se definen las funcionalidades específicas de un libro. La clase Biblioteca se encarga
de probar el funcionamiento de la implementación.

Al utilizar el diseño con interfaces, podemos trabajar con cualquier objeto que implemente
la interfaz Libro, sin necesidad de conocer los detalles de implementación de la clase
LibroImpl. Esto nos permite agregar nuevas implementaciones de libros en el futuro sin
afectar el código existente.

Especificación y uso de contratos

En la programación, un contrato es un acuerdo entre dos partes (por ejemplo, dos clases o
componentes) que define las responsabilidades y garantías mutuas. Los contratos se utilizan
para establecer reglas y restricciones claras sobre el comportamiento y las interacciones
entre los componentes de un sistema.

La especificación de contratos implica definir las precondiciones (lo que se espera antes de
la ejecución de un método), las pos condiciones (lo que se garantiza después de la ejecución
de un método) y las invariantes (condiciones que se mantienen durante la vida de un
objeto). Estas especificaciones pueden expresarse utilizando lenguajes formales o
anotaciones en el código.

El uso de contratos implica que los componentes deben cumplir con las especificaciones
establecidas. Esto implica que los métodos deben cumplir con las precondiciones antes de
ejecutarse y garantizar las pos condiciones después de ejecutarse. Los contratos
proporcionan una forma de validar y verificar el comportamiento de los componentes,
ayudando a detectar y prevenir errores en tiempo de desarrollo y ejecución.
Ejemplo

// Función para verificar la precondición


function verificarPrecondicion(condicion, mensajeError) {
if (!condicion) {
throw new Error(mensajeError);
}
}

// Función para verificar la postcondición


function verificarPostcondicion(condicion, mensajeError) {
if (!condicion) {
throw new Error(mensajeError);
}
}

// Clase que representa una cuenta bancaria


class CuentaBancaria {
constructor(saldoInicial) {
this.saldo = saldoInicial;
}

// Método para depositar dinero


depositar(cantidad) {
verificarPrecondicion(cantidad > 0, "La cantidad a depositar debe
ser mayor a 0.");

const saldoAnterior = this.saldo;


this.saldo += cantidad;

verificarPostcondicion(this.saldo === saldoAnterior + cantidad,


"El saldo no se actualizó correctamente.");
}

// Método para retirar dinero


retirar(cantidad) {
verificarPrecondicion(cantidad > 0, "La cantidad a retirar debe
ser mayor a 0.");
verificarPrecondicion(this.saldo >= cantidad, "No hay suficiente
saldo para realizar el retiro.");

const saldoAnterior = this.saldo;


this.saldo -= cantidad;

verificarPostcondicion(this.saldo === saldoAnterior - cantidad,


"El saldo no se actualizó correctamente.");
}
}

// Uso de la clase CuentaBancaria


const cuenta = new CuentaBancaria(1000);
console.log("Saldo inicial:", cuenta.saldo);

cuenta.depositar(500);
console.log("Saldo después del depósito:", cuenta.saldo);

cuenta.retirar(200);
console.log("Saldo después del retiro:", cuenta.saldo);

En este ejemplo, tenemos una clase CuentaBancaria que representa una cuenta bancaria y
tiene métodos para depositar y retirar dinero. Antes de ejecutar estas acciones, se realizan
verificaciones de precondición para asegurarse de que las cantidades proporcionadas sean
válidas (mayores a 0) y de que haya suficiente saldo para realizar el retiro. Después de cada
acción, se realizan verificaciones de postcondición para asegurarse de que el saldo se haya
actualizado correctamente.

Las funciones verificarPrecondicion y verificarPostcondicion se utilizan para lanzar


errores si las condiciones no se cumplen, lo que ayuda a detectar posibles errores en tiempo
de ejecución.

El uso de especificaciones y contratos en este ejemplo garantiza que las operaciones de


depósito y retiro se realicen correctamente y cumplan con las condiciones necesarias. Si
alguna de las condiciones no se cumple, se lanzará un error y se interrumpirá la ejecución
del programa.

Separación de intereses y responsabilidades

La separación de intereses y responsabilidades es un principio importante en el diseño de


software. Se refiere a la práctica de dividir un sistema en componentes o módulos que se
ocupen de tareas específicas y distintas. Esto permite lograr un diseño modular y bien
organizado, donde cada componente tenga una responsabilidad clara y esté enfocado en un
único interés.
La separación de intereses y responsabilidades es esencial para construir sistemas flexibles,
escalables y mantenerles. Al distribuir las tareas de manera adecuada, se obtiene un diseño
modular y organizado que facilita la evolución del software a largo plazo y reduce la
aparición de errores y problemas de mantenimiento.

También podría gustarte