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
- Dispatch Estático: Decisión en tiempo de compilación
- Dispatch Dinámico: Decisión en tiempo de ejecución
- Single Dispatch: Selección basada en el tipo del objeto receptor
- Double Dispatch: Selección basada en dos tipos de objeto
- 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
-
Estático vs Dinámico
- Estático: Tiempo de compilación (Sobrecarga)
- Dinámico: Tiempo de ejecución (Sobrescritura)
-
Single vs Double Dispatch
- Single: Un tipo de objeto decide
- Double: Dos tipos de objeto deciden
-
vtable vs Early Binding
- vtable: Métodos virtuales
- Early Binding: Métodos finales
Tareas típicas de examen
- Explicar Single vs Double Dispatch
- Implementar Visitor Pattern
- Comparar vinculación estática y dinámica
- Describir el funcionamiento de vtable
- 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.

