Skip to content
IRC-CodingIRC-Coding
OOPDispatchDynamic BindingSingle DispatchDouble DispatchVisitor PatternMúltiple DispatchvtablePolimorfismo

OOP Dispatch: Dynamic Binding, Single y Double Dispatch

OOP Dispatch con dynamic binding, Single/Double Dispatch y Visitor Pattern. Ejemplos Java para resolución de métodos y múltiple dispatch.

S

schutzgeist

17 min read
OOP Dispatch: Dynamic Binding, Single y Double Dispatch

OOP Dispatch: Enlace Dinámico, Single y Double Dispatch

Dispatch es el mecanismo que selecciona la implementación de método que realmente se ejecutará cuando se realiza una llamada. Esta selección puede ocurrir en tiempo de compilación o en tiempo de ejecución, y constituye el fundamento del polimorfismo.

¿Qué es Dispatch?

Dispatch es el proceso que resuelve una llamada a método hacia una implementación concreta. La manera en que se realiza esta resolución afecta directamente la flexibilidad y el rendimiento del software.

Tipos de Dispatch

  1. Dispatch Estático: Decisión en tiempo de compilación
  2. Dispatch Dinámico: Decisión en tiempo de ejecución
  3. Single Dispatch: Selección basada en el tipo del objeto receptor
  4. Double Dispatch: Selección basada en dos tipos de objeto
  5. Múltiple Dispatch: Selección basada en varios tipos de objeto

Dispatch Estático vs Dinámico

Dispatch Estático (Sobrecarga)

public class StaticDispatchDemo {
    
    // Métodos sobrecargados - dispatch estático
    public static class Printer {
        public void print(Object obj) {
            System.out.println("Printing Object: " + obj.toString());
        }
        
        public void print(String str) {
            System.out.println("Printing String: " + str);
        }
        
        public void print(Integer num) {
            System.out.println("Printing Integer: " + num);
        }
        
        public void print(String str, int count) {
            for (int i = 0; i < count; i++) {
                System.out.println("String " + (i + 1) + ": " + str);
            }
        }
    }
    
    public static void demonstrateStaticDispatch() {
        Printer printer = new Printer();
        
        // Llamadas directas - selección inequívoca
        printer.print("Hello");           // print(String)
        printer.print(42);               // print(Integer)
        printer.print("Test", 3);         // print(String, int)
        
        // Caso importante: los tipos estáticos deciden
        Object obj1 = "Hello";
        Object obj2 = 42;
        
        printer.print(obj1);  // print(Object) - ¡el compilador solo conoce el tipo Object!
        printer.print(obj2);  // print(Object) - ¡el compilador solo conoce el tipo Object!
        
        // Cast para seleccionar la sobrecarga correcta
        printer.print((String) obj1);   // print(String)
        // printer.print((Integer) obj2); // ClassCastException en tiempo de ejecución
    }
}

Dispatch Dinámico (Sobreescritura)

public class DynamicDispatchDemo {
    
    // Clase base con métodos virtuales
    public static abstract class Animal {
        protected String name;
        
        public Animal(String name) {
            this.name = name;
        }
        
        // Método virtual - se enlaza dinámicamente
        public String makeSound() {
            return name + " makes a sound";
        }
        
        // Otro método virtual
        public String move() {
            return name + " moves";
        }
        
        // Método final - no puede sobreescribirse
        public final String getName() {
            return name;
        }
        
        // Método abstracto - debe sobreescribirse
        public abstract String getType();
    }
    
    // Subclases concretas
    public static class Dog extends Animal {
        public Dog(String name) {
            super(name);
        }
        
        @Override
        public String makeSound() {
            return name + " barks: Woof!";
        }
        
        @Override
        public String move() {
            return name + " runs happily";
        }
        
        @Override
        public String getType() {
            return "Dog";
        }
        
        // Método adicional
        public String wagTail() {
            return name + " wags tail excitedly";
        }
    }
    
    public static class Cat extends Animal {
        public Cat(String name) {
            super(name);
        }
        
        @Override
        public String makeSound() {
            return name + " meows: Meow!";
        }
        
        @Override
        public String move() {
            return name + " sneaks silently";
        }
        
        @Override
        public String getType() {
            return "Cat";
        }
        
        public String purr() {
            return name + " purrs contentedly";
        }
    }
    
    public static class Bird extends Animal {
        public Bird(String name) {
            super(name);
        }
        
        @Override
        public String makeSound() {
            return name + " chirps: Tweet!";
        }
        
        @Override
        public String move() {
            return name + " flies gracefully";
        }
        
        @Override
        public String getType() {
            return "Bird";
        }
        
        public String sing() {
            return name + " sings a beautiful song";
        }
    }
    
    // Demostración de dispatch dinámico
    public static void demonstrateDynamicDispatch() {
        List<Animal> animals = new ArrayList<>();
        animals.add(new Dog("Buddy"));
        animals.add(new Cat("Whiskers"));
        animals.add(new Bird("Tweety"));
        
        System.out.println("=== Dispatch Dinámico ===");
        for (Animal animal : animals) {
            // Enlace dinámico - ¡el tiempo de ejecución decide!
            System.out.println(animal.getType() + ": " + animal.makeSound());
            System.out.println(animal.getType() + ": " + animal.move());
            System.out.println();
        }
        
        // Procesamiento polimórfico
        processAnimal(animals.get(0)); // Dog
        processAnimal(animals.get(1)); // Cat
        processAnimal(animals.get(2)); // Bird
    }
    
    // Método polimórfico
    public static void processAnimal(Animal animal) {
        System.out.println("Processing " + animal.getType());
        
        // Dispatch dinámico
        String sound = animal.makeSound();
        String movement = animal.move();
        
        System.out.println("  Sound: " + sound);
        System.out.println("  Movement: " + movement);
        
        // Operaciones específicas de tipo (con instanceof)
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            System.out.println("  Special: " + dog.wagTail());
        } else if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            System.out.println("  Special: " + cat.purr());
        } else if (animal instanceof Bird) {
            Bird bird = (Bird) animal;
            System.out.println("  Special: " + bird.sing());
        }
    }
}

Single Dispatch

Cómo funciona el Single Dispatch

public class SingleDispatchDemo {
    
    // Single Dispatch - solo el tipo del receptor decide
    public interface Shape {
        double area();
        double perimeter();
        String getType();
        void accept(ShapeVisitor visitor);
    }
    
    public static class Circle implements Shape {
        private double radius;
        
        public Circle(double radius) {
            this.radius = radius;
        }
        
        @Override
        public double area() {
            return Math.PI * radius * radius;
        }
        
        @Override
        public double perimeter() {
            return 2 * Math.PI * radius;
        }
        
        @Override
        public String getType() {
            return "Circle";
        }
        
        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visitCircle(this); // Single Dispatch
        }
        
        public double getRadius() { return radius; }
    }
    
    public static class Rectangle implements Shape {
        private double width, height;
        
        public Rectangle(double width, double height) {
            this.width = width;
            this.height = height;
        }
        
        @Override
        public double area() {
            return width * height;
        }
        
        @Override
        public double perimeter() {
            return 2 * (width + height);
        }
        
        @Override
        public String getType() {
            return "Rectangle";
        }
        
        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visitRectangle(this); // Single Dispatch
        }
        
        public double getWidth() { return width; }
        public double getHeight() { return height; }
    }
    
    public static class Triangle implements Shape {
        private double base, height;
        
        public Triangle(double base, double height) {
            this.base = base;
            this.height = height;
        }
        
        @Override
        public double area() {
            return 0.5 * base * height;
        }
        
        @Override
        public double perimeter() {
            // Simplificado: triángulo equilátero
            double side = base;
            return 3 * side;
        }
        
        @Override
        public String getType() {
            return "Triangle";
        }
        
        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visitTriangle(this); // Single Dispatch
        }
        
        public double getBase() { return base; }
        public double getHeight() { return height; }
    }
    
    // Demostración
    public static void demonstrateSingleDispatch() {
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle(2.0));
        shapes.add(new Rectangle(3.0, 4.0));
        shapes.add(new Triangle(5.0, 3.0));
        
        System.out.println("=== Single Dispatch ===");
        for (Shape shape : shapes) {
            System.out.println(shape.getType());
            System.out.println("  Area: " + String.format("%.2f", shape.area()));
            System.out.println("  Perimeter: " + String.format("%.2f", shape.perimeter()));
            System.out.println();
        }
    }
}

Double Dispatch con Visitor Pattern

Visitor Pattern para Double Dispatch

public class DoubleDispatchDemo {
    
    // Visitor Interface
    public interface ShapeVisitor {
        void visitCircle(Circle circle);
        void visitRectangle(Rectangle rectangle);
        void visitTriangle(Triangle triangle);
        double getResult();
    }
    
    // Concreto Visitor para cálculo de áreas
    public static class AreaVisitor implements ShapeVisitor {
        private double totalArea = 0.0;
        
        @Override
        public void visitCircle(Circle circle) {
            double area = Math.PI * circle.getRadius() * circle.getRadius();
            totalArea += area;
            System.out.println("Circle area: " + String.format("%.2f", area));
        }
        
        @Override
        public void visitRectangle(Rectangle rectangle) {
            double area = rectangle.getWidth() * rectangle.getHeight();
            totalArea += area;
            System.out.println("Rectangle area: " + String.format("%.2f", area));
        }
        
        @Override
        public void visitTriangle(Triangle triangle) {
            double area = 0.5 * triangle.getBase() * triangle.getHeight();
            totalArea += area;
            System.out.println("Triangle area: " + String.format("%.2f", area));
        }
        
        @Override
        public double getResult() {
            return totalArea;
        }
    }
    
    // Visitor para cálculo de perímetros
    public static class PerimeterVisitor implements ShapeVisitor {
        private double totalPerimeter = 0.0;
        
        @Override
        public void visitCircle(Circle circle) {
            double perimeter = 2 * Math.PI * circle.getRadius();
            totalPerimeter += perimeter;
            System.out.println("Circle perimeter: " + String.format("%.2f", perimeter));
        }
        
        @Override
        public void visitRectangle(Rectangle rectangle) {
            double perimeter = 2 * (rectangle.getWidth() + rectangle.getHeight());
            totalPerimeter += perimeter;
            System.out.println("Rectangle perimeter: " + String.format("%.2f", perimeter));
        }
        
        @Override
        public void visitTriangle(Triangle triangle) {
            double perimeter = 3 * triangle.getBase(); // Simplificado
            totalPerimeter += perimeter;
            System.out.println("Triangle perimeter: " + String.format("%.2f", perimeter));
        }
        
        @Override
        public double getResult() {
            return totalPerimeter;
        }
    }
    
    // Visitor para exportación XML
    public static class XMLExportVisitor implements ShapeVisitor {
        private StringBuilder xml = new StringBuilder();
        
        @Override
        public void visitCircle(Circle circle) {
            xml.append("<shape type=\"circle\">\n");
            xml.append("  <radius>").append(circle.getRadius()).append("</radius>\n");
            xml.append("  <area>").append(String.format("%.2f", Math.PI * circle.getRadius() * circle.getRadius())).append("</area>\n");
            xml.append("</shape>\n");
        }
        
        @Override
        public void visitRectangle(Rectangle rectangle) {
            xml.append("<shape type=\"rectangle\">\n");
            xml.append("  <width>").append(rectangle.getWidth()).append("</width>\n");
            xml.append("  <height>").append(rectangle.getHeight()).append("</height>\n");
            xml.append("  <area>").append(String.format("%.2f", rectangle.getWidth() * rectangle.getHeight())).append("</area>\n");
            xml.append("</shape>\n");
        }
        
        @Override
        public void visitTriangle(Triangle triangle) {
            xml.append("<shape type=\"triangle\">\n");
            xml.append("  <base>").append(triangle.getBase()).append("</base>\n");
            xml.append("  <height>").append(triangle.getHeight()).append("</height>\n");
            xml.append("  <area>").append(String.format("%.2f", 0.5 * triangle.getBase() * triangle.getHeight())).append("</area>\n");
            xml.append("</shape>\n");
        }
        
        @Override
        public double getResult() {
            return 0; // No relevante para este Visitor
        }
        
        public String getXML() {
            return "<shapes>\n" + xml.toString() + "</shapes>";
        }
    }
    
    // Demostración de Double Dispatch
    public static void demonstrateDoubleDispatch() {
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle(2.0));
        shapes.add(new Rectangle(3.0, 4.0));
        shapes.add(new Triangle(5.0, 3.0));
        
        System.out.println("=== Double Dispatch con Visitor Pattern ===");
        
        // Area Visitor
        System.out.println("Cálculo de áreas:");
        AreaVisitor areaVisitor = new AreaVisitor();
        for (Shape shape : shapes) {
            shape.accept(areaVisitor); // Double Dispatch!
        }
        System.out.println("Área total: " + String.format("%.2f", areaVisitor.getResult()));
        System.out.println();
        
        // Perimeter Visitor
        System.out.println("Cálculo de perímetros:");
        PerimeterVisitor perimeterVisitor = new PerimeterVisitor();
        for (Shape shape : shapes) {
            shape.accept(perimeterVisitor); // Double Dispatch!
        }
        System.out.println("Perímetro total: " + String.format("%.2f", perimeterVisitor.getResult()));
        System.out.println();
        
        // XML Export Visitor
        System.out.println("Exportación XML:");
        XMLExportVisitor xmlVisitor = new XMLExportVisitor();
        for (Shape shape : shapes) {
            shape.accept(xmlVisitor); // Double Dispatch!
        }
        System.out.println(xmlVisitor.getXML());
    }
}

Multiple Dispatch

Simulación de Multiple Dispatch

public class MultipleDispatchDemo {
    
    // Problema: operaciones que dependen de dos tipos
    public interface Collider {
        void collideWith(Collider other);
        String getType();
    }
    
    public static class Asteroid implements Collider {
        private String name;
        private double mass;
        
        public Asteroid(String name, double mass) {
            this.name = name;
            this.mass = mass;
        }
        
        @Override
        public void collideWith(Collider other) {
            // Single Dispatch - solo el tipo del otro decide
            System.out.println(name + " collides with " + other.getType());
        }
        
        @Override
        public String getType() {
            return "Asteroid";
        }
        
        public double getMass() { return mass; }
        public String getName() { return name; }
    }
    
    public static class Spaceship implements Collider {
        private String name;
        private double shield;
        
        public Spaceship(String name, double shield) {
            this.name = name;
            this.shield = shield;
        }
        
        @Override
        public void collideWith(Collider other) {
            // Single Dispatch - problema: no sabemos qué tipo tenemos
            System.out.println(name + " collides with " + other.getType());
        }
        
        @Override
        public String getType() {
            return "Spaceship";
        }
        
        public double getShield() { return shield; }
        public String getName() { return name; }
    }
    
    public static class SpaceStation implements Collider {
        private String name;
        private double hull;
        
        public SpaceStation(String name, double hull) {
            this.name = name;
            this.hull = hull;
        }
        
        @Override
        public void collideWith(Collider other) {
            System.out.println(name + " collides with " + other.getType());
        }
        
        @Override
        public String getType() {
            return "SpaceStation";
        }
        
        public double getHull() { return hull; }
        public String getName() { return name; }
    }
    
    // Solución con patrón Double Dispatch
    public interface CollisionHandler {
        void handleAsteroidAsteroid(Asteroid a1, Asteroid a2);
        void handleAsteroidSpaceship(Asteroid asteroid, Spaceship spaceship);
        void handleAsteroidStation(Asteroid asteroid, SpaceStation station);
        void handleSpaceshipSpaceship(Spaceship s1, Spaceship s2);
        void handleSpaceshipStation(Spaceship spaceship, SpaceStation station);
        void handleStationStation(SpaceStation s1, SpaceStation s2);
    }
    
    public static class DefaultCollisionHandler implements CollisionHandler {
        @Override
        public void handleAsteroidAsteroid(Asteroid a1, Asteroid a2) {
            double totalMass = a1.getMass() + a2.getMass();
            System.out.println("Asteroid collision: " + a1.getName() + " + " + a2.getName() + 
                             " (total mass: " + String.format("%.1f", totalMass) + ")");
        }
        
        @Override
        public void handleAsteroidSpaceship(Asteroid asteroid, Spaceship spaceship) {
            double damage = asteroid.getMass() * 0.1;
            spaceship.setShield(spaceship.getShield() - damage);
            System.out.println("Asteroid-Spaceship collision: " + asteroid.getName() + 
                             " hits " + spaceship.getName() + " (damage: " + 
                             String.format("%.1f", damage) + ")");
        }
        
        @Override
        public void handleAsteroidStation(Asteroid asteroid, SpaceStation station) {
            double damage = asteroid.getMass() * 0.05;
            station.setHull(station.getHull() - damage);
            System.out.println("Asteroid-Station collision: " + asteroid.getName() + 
                             " hits " + station.getName() + " (damage: " + 
                             String.format("%.1f", damage) + ")");
        }
        
        @Override
        public void handleSpaceshipSpaceship(Spaceship s1, Spaceship s2) {
            System.out.println("Spaceship collision: " + s1.getName() + " + " + s2.getName());
        }
        
        @Override
        public void handleSpaceshipStation(Spaceship spaceship, SpaceStation station) {
            System.out.println("Spaceship-Station collision: " + spaceship.getName() + 
                             " docks at " + station.getName());
        }
        
        @Override
        public void handleStationStation(SpaceStation s1, SpaceStation s2) {
            System.out.println("Station collision: " + s1.getName() + " + " + s2.getName());
        }
    }
    
    // Colisores mejorados con Double Dispatch
    public static class ImprovedAsteroid extends Asteroid {
        public ImprovedAsteroid(String name, double mass) {
            super(name, mass);
        }
        
        @Override
        public void collideWith(Collider other) {
            other.collideWithAsteroid(this); // Inversión para Double Dispatch
        }
        
        public void collideWithAsteroid(Asteroid other) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleAsteroidAsteroid(this, other);
        }
        
        public void collideWithSpaceship(Spaceship spaceship) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleAsteroidSpaceship(this, spaceship);
        }
        
        public void collideWithStation(SpaceStation station) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleAsteroidStation(this, station);
        }
    }
    
    public static class ImprovedSpaceship extends Spaceship {
        public ImprovedSpaceship(String name, double shield) {
            super(name, shield);
        }
        
        @Override
        public void collideWith(Collider other) {
            other.collideWithSpaceship(this);
        }
        
        public void collideWithAsteroid(Asteroid asteroid) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleAsteroidSpaceship(asteroid, this);
        }
        
        public void collideWithSpaceship(Spaceship other) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleSpaceshipSpaceship(this, other);
        }
        
        public void collideWithStation(SpaceStation station) {
            CollisionHandler handler = new DefaultCollisionHandler();
            handler.handleSpaceshipStation(this, station);
        }
    }
    
    // Demostración
    public static void demonstrateMultipleDispatch() {
        System.out.println("=== Simulación de Multiple Dispatch ===");
        
        // Single Dispatch simple (problema)
        System.out.println("Single Dispatch (Problema):");
        Collider asteroid1 = new Asteroid("Rocky", 100.0);
        Collider spaceship1 = new Spaceship("Explorer", 50.0);
        asteroid1.collideWith(spaceship1); // Solo se conoce el tipo de spaceship1
        
        System.out.println();
        
        // Solución con Double Dispatch
        System.out.println("Solución con Double Dispatch:");
        ImprovedAsteroid asteroid2 = new ImprovedAsteroid("Rocky", 100.0);
        ImprovedSpaceship spaceship2 = new ImprovedSpaceship("Explorer", 50.0);
        asteroid2.collideWith(spaceship2); // Ambos tipos son conocidos!
        
        System.out.println();
        
        // Colisiones más complejas
        List<Collider> objects = Arrays.asList(
            new ImprovedAsteroid("Rocky", 100.0),
            new ImprovedSpaceship("Explorer", 50.0),
            new ImprovedSpaceship("Voyager", 60.0),
            new SpaceStation("Alpha", 200.0)
        );
        
        System.out.println("Colisiones complejas:");
        for (int i = 0; i < objects.size(); i++) {
            for (int j = i + 1; j < objects.size(); j++) {
                objects.get(i).collideWith(objects.get(j));
            }
        }
    }
}

Tablas de métodos virtuales (vtable)

Cómo funcionan las vtables

public class VTableDemo {
    
    // Representación simplificada de una vtable
    public static class VTableVisualization {
        
        // Clase base
        public static class Base {
            // Métodos virtuales
            public virtual void method1() { System.out.println("Base.method1"); }
            public virtual void method2() { System.out.println("Base.method2"); }
            
            // Método concreto
            public void concreteMethod() { System.out.println("Base.concreteMethod"); }
        }
        
        // Clase derivada 1
        public static class Derived1 extends Base {
            @Override
            public void method1() { System.out.println("Derived1.method1"); }
            // method2 heredado
            
            @Override
            public void method2() { System.out.println("Derived1.method2"); }
        }
        
        // Clase derivada 2
        public static class Derived2 extends Base {
            // method1 heredado
            @Override
            public void method2() { System.out.println("Derived2.method2"); }
            
            public void method3() { System.out.println("Derived2.method3"); }
        }
        
        // Demostración de la vtable
        public static void demonstrateVTable() {
            System.out.println("=== VTable Demonstration ===");
            
            Base base = new Base();
            Base derived1 = new Derived1();
            Base derived2 = new Derived2();
            
            // Vtable para Base:
            // [Base.method1, Base.method2]
            System.out.println("Base object:");
            base.method1(); // Base.method1
            base.method2(); // Base.method2
            
            // Vtable para Derived1:
            // [Derived1.method1, Derived1.method2]
            System.out.println("\nDerived1 object:");
            derived1.method1(); // Derived1.method1
            derived1.method2(); // Derived1.method2
            
            // Vtable para Derived2:
            // [Base.method1, Derived2.method2]
            System.out.println("\nDerived2 object:");
            derived2.method1(); // Base.method1 (heredado)
            derived2.method2(); // Derived2.method2
            
            // Método concreto - no en vtable
            System.out.println("\nConcrete method:");
            base.concreteMethod();
            derived1.concreteMethod();
            derived2.concreteMethod();
        }
    }
    
    // Comparación de rendimiento
    public static class PerformanceComparison {
        
        // Método final - sin llamada virtual
        public static class FinalClass {
            public final void finalMethod() {
                // Sin búsqueda en vtable
            }
        }
        
        // Método virtual - búsqueda en vtable
        public static class VirtualClass {
            public void virtualMethod() {
                // Búsqueda en vtable requerida
            }
        }
        
        public static void performanceTest() {
            FinalClass finalObj = new FinalClass();
            VirtualClass virtualObj = new VirtualClass();
            
            int iterations = 100_000_000;
            
            // Prueba método final
            long start = System.nanoTime();
            for (int i = 0; i < iterations; i++) {
                finalObj.finalMethod();
            }
            long finalTime = System.nanoTime() - start;
            
            // Prueba método virtual
            start = System.nanoTime();
            for (int i = 0; i < iterations; i++) {
                virtualObj.virtualMethod();
            }
            long virtualTime = System.nanoTime() - start;
            
            System.out.println("Performance Comparison:");
            System.out.println("Final method: " + (finalTime / 1_000_000) + " ms");
            System.out.println("Virtual method: " + (virtualTime / 1_000_000) + " ms");
            System.out.println("Difference: " + ((virtualTime - finalTime) / 1_000_000) + " ms");
        }
    }
}

Dispatch en la práctica

Ejemplo de framework: Sistema de plugins

public class PluginDispatchDemo {
    
    // Interfaz de plugin
    public interface Plugin {
        void initialize();
        void execute();
        void shutdown();
        String getName();
        String getVersion();
    }
    
    // Clase base abstracta para plugins
    public abstract class AbstractPlugin implements Plugin {
        protected String name;
        protected String version;
        
        public AbstractPlugin(String name, String version) {
            this.name = name;
            this.version = version;
        }
        
        @Override
        public final String getName() {
            return name;
        }
        
        @Override
        public final String getVersion() {
            return version;
        }
        
        // Template Method Pattern
        @Override
        public final void execute() {
            System.out.println("Executing plugin: " + name);
            try {
                doExecute();
                System.out.println("Plugin execution completed: " + name);
            } catch (Exception e) {
                System.err.println("Plugin execution failed: " + name + " - " + e.getMessage());
            }
        }
        
        // Método abstracto para la implementación concreta
        protected abstract void doExecute();
        
        // Implementaciones predeterminadas
        @Override
        public void initialize() {
            System.out.println("Initializing plugin: " + name);
        }
        
        @Override
        public void shutdown() {
            System.out.println("Shutting down plugin: " + name);
        }
    }
    
    // Plugins concretos
    public static class DatabasePlugin extends AbstractPlugin {
        public DatabasePlugin() {
            super("DatabasePlugin", "1.0.0");
        }
        
        @Override
        protected void doExecute() {
            System.out.println("  Connecting to database...");
            System.out.println("  Executing database operations...");
            System.out.println("  Closing database connection...");
        }
        
        @Override
        public void initialize() {
            super.initialize();
            System.out.println("  Loading database driver...");
        }
    }
    
    public static class LoggingPlugin extends AbstractPlugin {
        public LoggingPlugin() {
            super("LoggingPlugin", "2.1.0");
        }
        
        @Override
        protected void doExecute() {
            System.out.println("  Setting up loggers...");
            System.out.println("  Configuring log levels...");
            System.out.println("  Starting log rotation...");
        }
        
        @Override
        public void shutdown() {
            System.out.println("  Flushing log buffers...");
            super.shutdown();
        }
    }
    
    public static class SecurityPlugin extends AbstractPlugin {
        public SecurityPlugin() {
            super("SecurityPlugin", "1.5.2");
        }
        
        @Override
        protected void doExecute() {
            System.out.println("  Initializing security manager...");
            System.out.println("  Setting up authentication...");
            System.out.println("  Configuring authorization rules...");
        }
    }
    
    // Gestor de plugins con dispatch
    public static class PluginManager {
        private List<Plugin> plugins = new ArrayList<>();
        
        public void registerPlugin(Plugin plugin) {
            plugins.add(plugin);
            plugin.initialize();
        }
        
        public void executeAllPlugins() {
            System.out.println("=== Executing all plugins ===");
            for (Plugin plugin : plugins) {
                plugin.execute(); // Dispatch dinámico
            }
        }
        
        public void shutdownAllPlugins() {
            System.out.println("\n=== Shutting down all plugins ===");
            for (Plugin plugin : plugins) {
                plugin.shutdown(); // Dispatch dinámico
            }
        }
        
        public void executePluginByName(String name) {
            for (Plugin plugin : plugins) {
                if (plugin.getName().equals(name)) {
                    plugin.execute(); // Dispatch dinámico
                    return;
                }
            }
            System.out.println("Plugin not found: " + name);
        }
        
        public void listPlugins() {
            System.out.println("=== Registered Plugins ===");
            for (Plugin plugin : plugins) {
                System.out.println(plugin.getName() + " v" + plugin.getVersion());
            }
        }
    }
    
    // Demostración
    public static void demonstratePluginSystem() {
        PluginManager manager = new PluginManager();
        
        // Registrar plugins
        manager.registerPlugin(new DatabasePlugin());
        manager.registerPlugin(new LoggingPlugin());
        manager.registerPlugin(new SecurityPlugin());
        
        // Ejecutar todos los plugins
        manager.listPlugins();
        manager.executeAllPlugins();
        
        // Ejecutar plugin específico
        System.out.println("\n=== Executing specific plugin ===");
        manager.executePluginByName("LoggingPlugin");
        
        // Limpiar
        manager.shutdownAllPlugins();
    }
}

Prácticas recomendadas para Dispatch

1. Final para rendimiento

public class DispatchBestPractices {
    
    // Métodos finales para rendimiento
    public static class PerformanceOptimized {
        // Método final - sin llamada virtual
        public final void criticalMethod() {
            // Hot Path Code
        }
        
        // Clase final - herencia imposible
        public static final class ImmutableData {
            private final int value;
            
            public ImmutableData(int value) {
                this.value = value;
            }
            
            public final int getValue() {
                return value;
            }
        }
    }
    
    // Interfaz para flexibilidad
    public interface Processor {
        void process();
    }
    
    // Implementación concreta
    public static class FastProcessor implements Processor {
        @Override
        public void process() {
            // Implementación
        }
    }
}

2. Evitar problemas de Dispatch

public class DispatchProblems {
    
    // Problema: Sobrecarga con tipos de referencia
    public static class ProblematicOverloading {
        public void process(Object obj) {
            System.out.println("Processing Object");
        }
        
        public void process(String str) {
            System.out.println("Processing String");
        }
        
        public void demonstrateProblem() {
            Object obj = "Hello";
            process(obj); // ¡Llama a process(Object)!
        }
    }
    
    // Solución: Nombres de métodos claros
    public static class Solution {
        public void processObject(Object obj) {
            System.out.println("Processing Object");
        }
        
        public void processString(String str) {
            System.out.println("Processing String");
        }
        
        public void demonstrateSolution() {
            Object obj = "Hello";
            if (obj instanceof String) {
                processString((String) obj);
            } else {
                processObject(obj);
            }
        }
    }
}

Conceptos relevantes para examen

Distinciones importantes

  1. Estático vs Dinámico

    • Estático: Tiempo de compilación (Sobrecarga)
    • Dinámico: Tiempo de ejecución (Sobrescritura)
  2. Single vs Double Dispatch

    • Single: Un tipo de objeto decide
    • Double: Dos tipos de objeto deciden
  3. vtable vs Early Binding

    • vtable: Métodos virtuales
    • Early Binding: Métodos finales

Tareas típicas de examen

  1. Explicar Single vs Double Dispatch
  2. Implementar Visitor Pattern
  3. Comparar vinculación estática y dinámica
  4. Describir el funcionamiento de vtable
  5. Resolver problemas de Dispatch

Resumen

Dispatch es fundamental para la programación orientada a objetos:

  • Dispatch estático: Predecible, eficiente (Sobrecarga)
  • Dispatch dinámico: Flexible, extensible (Sobrescritura)
  • Single Dispatch: Estándar en la mayoría de lenguajes OOP
  • Double Dispatch: Realizable mediante Visitor Pattern
  • vtables: Implementación eficiente de Dispatch dinámico

La aplicación correcta de Dispatch permite arquitecturas de software flexibles, mantenibles y eficientes.

Continúa en la ruta de aprendizaje OOP

Todos los artículos OOP están completos. Vuelve al primer artículo: Objektorientierte Programmierung OOP Grundlagen.

Volver al blog
Share:

Nächster Artikel in Programación Orientada a Objetos

Weiterlesen
Fundamentos OOP: Clases, Objetos, Instancias y Métodos

Entradas relacionadas