Programación Orientada a Objetos

Semana 5: Patrón Delegate y Aplicaciones Gráficas con Swing

El plan para hoy

Comprender el patrón Delegate Aplicar Comparator como ejemplo de Delegate Introducción a la programación gráfica con Swing Crear nuestra primera aplicación gráfica Empaquetar la aplicación en un JAR

Patrón Delegate

// Interfaz del delegado
public interface OrderDelegate {
    int compare(String a, String b);
}

// Implementación específica
public class AlphabeticalOrderDelegate implements OrderDelegate {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
}

// Clase que utiliza el delegado
public class StringSorter {
    private OrderDelegate delegate;
    
    public void setDelegate(OrderDelegate delegate) {
        this.delegate = delegate;
    }
    
    public void sortStrings(List<String> strings) {
        Collections.sort(strings, (a, b) -> delegate.compare(a, b));
    }
}

¿Por qué usar Delegate?

  • Separa responsabilidades
  • Flexibilidad en tiempo de ejecución
  • Evita herencia innecesaria
  • Facilita pruebas unitarias

Comparator como Delegate

public class ProductComparator implements Comparator<Product> {
    @Override
    public int compare(Product p1, Product p2) {
        return Double.compare(p1.getPrice(), p2.getPrice());
    }
}

List<Product> products = new ArrayList<>();
products.sort(new ProductComparator());
// O usando lambda
products.sort((p1, p2) -> Double.compare(p1.getPrice(), p2.getPrice()));

Jerarquía de Componentes Swing

Component (abstract)
├── Container
│   ├── JComponent
│   │   ├── JPanel
│   │   ├── JLabel
│   │   ├── JButton
│   │   ├── JTextField
│   │   └── JTextArea
│   ├── JFrame
│   └── JDialog
└── Window
    └── Frame

Introducción a Swing

import javax.swing.*;

public class FirstWindow extends JFrame {
    public FirstWindow() {
        setTitle("Mi Primera Ventana");
        setSize(400, 300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new FirstWindow().setVisible(true);
        });
    }
}

Componentes Principales

  • Contenedores
    • JFrame: Ventana principal
    • JPanel: Panel contenedor
    • JDialog: Ventana secundaria
  • Controles
    • JButton: Botón clickeable
    • JTextField: Campo de texto
    • JLabel: Etiqueta de texto
  • Menús
    • JMenuBar: Barra de menú
    • JMenu: Menú desplegable
    • JMenuItem: Elemento de menú

Layouts en Swing

// BorderLayout
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JButton("Norte"), BorderLayout.NORTH);
panel.add(new JButton("Sur"), BorderLayout.SOUTH);

// FlowLayout
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(new JButton("1"));
buttonPanel.add(new JButton("2"));

// GridLayout
JPanel grid = new JPanel(new GridLayout(2, 2));
grid.add(new JButton("1,1"));
grid.add(new JButton("1,2"));

Layouts Disponibles

  • BorderLayout
  • FlowLayout
  • GridLayout
  • GridBagLayout
  • BoxLayout
  • CardLayout

Eventos en Swing

JButton button = new JButton("Click Me");
button.addActionListener(e -> {
    System.out.println("¡Botón presionado!");
});

// Clase separada para el listener
class ButtonHandler implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("¡Botón presionado!");
    }
}

Tipos de Eventos

  • ActionEvent: Para acciones simples como clicks
  • MouseEvent: Para interacciones del mouse
  • KeyEvent: Para entrada de teclado
  • WindowEvent: Para eventos de la ventana
  • ItemEvent: Para cambios de estado

Primera Aplicación: Calculadora Simple

public class Calculadora extends JFrame {
    private JTextField display;
    private double result = 0;
    private String lastOperation = "=";
    private boolean start = true;
    
    public Calculadora() {
        // Configuración de la ventana
        configureWindow();
        
        // Crear componentes
        createDisplay();
        createButtons();
    }
    
    private void configureWindow() {
        setTitle("Calculadora");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());
    }
    
    private void createDisplay() {
        display = new JTextField("0");
        display.setEditable(false);
        display.setHorizontalAlignment(JTextField.RIGHT);
        add(display, BorderLayout.NORTH);
    }
    
    private void createButtons() {
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new GridLayout(4, 4, 5, 5));
        
        String[] buttonLabels = {
            "7", "8", "9", "/",
            "4", "5", "6", "*",
            "1", "2", "3", "-",
            "0", ".", "=", "+"
        };
        
        for (String label : buttonLabels) {
            JButton button = new JButton(label);
            buttonPanel.add(button);
            button.addActionListener(new ButtonClickListener());
        }
        
        add(buttonPanel, BorderLayout.CENTER);
    }
}

Manejando los Eventos

private class ButtonClickListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent event) {
        String command = event.getActionCommand();
        if (command.charAt(0) >= '0' && command.charAt(0) <= '9' || command.equals(".")) {
            if (start) {
                display.setText(command);
                start = false;
            } else {
                display.setText(display.getText() + command);
            }
        } else {
            if (!start) {
                calculate(Double.parseDouble(display.getText()));
                lastOperation = command;
                start = true;
            }
        }
    }
}

private void calculate(double x) {
    switch (lastOperation) {
        case "+": result += x; break;
        case "-": result -= x; break;
        case "*": result *= x; break;
        case "/": result /= x; break;
        case "=": result = x; break;
    }
    display.setText("" + result);
}

Creando un JAR Ejecutable

  1. Estructura del Proyecto
src/
  ├── com/
  │   └── miapp/
  │       └── Calculadora.java
  └── META-INF/
      └── MANIFEST.MF
  1. MANIFEST.MF
Manifest-Version: 1.0
Main-Class: com.miapp.Calculadora

Comandos para crear JAR

# Compilar
javac -d bin src/com/miapp/*.java

# Crear JAR
jar cvfm calculadora.jar src/META-INF/MANIFEST.MF -C bin .

# Ejecutar
java -jar calculadora.jar

Ejercicio: Conversor de Temperatura (30-45 min)

Crear una aplicación gráfica que:

  1. Tenga un campo de texto para ingresar temperatura
  2. Dos botones: “°C a °F” y “°F a °C”
  3. Una etiqueta para mostrar el resultado
  4. Un menú con opciones para:
    • Limpiar campos
    • Salir
    • Acerca de (mostrar diálogo)

Requerimientos:

  • Validar que el input sea numérico
  • Mostrar mensajes de error en un JOptionPane
  • Usar GridBagLayout para la disposición
  • Implementar el patrón Delegate para las conversiones

¿Preguntas?