OOP Dispatch: Dynamische Bindung, Single & Double Dispatch
Dispatch bezeichnet die Auswahl der tatsächlich auszuführenden Methode bei einem Methodenaufruf. Diese Auswahl kann statisch zur Kompilierzeit oder dynamisch zur Laufzeit erfolgen und ist die Grundlage für Polymorphie.
Was ist Dispatch?
Dispatch ist der Mechanismus, der einen Methodenaufruf auf eine konkrete Implementierung auflöst. Die Art der Dispatch-Auflösung beeinflusst die Flexibilität und Performance der Software.
Arten des Dispatch
- Statischer Dispatch: Entscheidung zur Kompilierzeit
- Dynamischer Dispatch: Entscheidung zur Laufzeit
- Single Dispatch: Auswahl basierend auf Empfängerobjekt
- Double Dispatch: Auswahl basierend auf zwei Objekttypen
- Mehrfachdispatch: Auswahl basierend auf mehreren Objekttypen
Statischer vs Dynamischer Dispatch
Statischer Dispatch (Überladen)
public class StaticDispatchDemo {
// Überladene Methoden - statischer Dispatch
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();
// Direkte Aufrufe - eindeutige Auswahl
printer.print("Hello"); // print(String)
printer.print(42); // print(Integer)
printer.print("Test", 3); // print(String, int)
// Wichtiger Fall: Statische Typen entscheiden!
Object obj1 = "Hello";
Object obj2 = 42;
printer.print(obj1); // print(Object) - Compiler kennt nur Object-Typ!
printer.print(obj2); // print(Object) - Compiler kennt nur Object-Typ!
// Cast zur Auswahl der richtigen Überladung
printer.print((String) obj1); // print(String)
// printer.print((Integer) obj2); // ClassCastException zur Laufzeit!
}
}
Dynamischer Dispatch (Überschreiben)
public class DynamicDispatchDemo {
// Basisklasse mit virtuellen Methoden
public static abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// Virtuelle Methode - wird dynamisch gebunden
public String makeSound() {
return name + " makes a sound";
}
// Weitere virtuelle Methode
public String move() {
return name + " moves";
}
// Finale Methode - kann nicht überschrieben werden
public final String getName() {
return name;
}
// Abstrakte Methode - muss überschrieben werden
public abstract String getType();
}
// Konkrete Unterklassen
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";
}
// Zusätzliche Methode
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";
}
}
// Demonstration von dynamischem Dispatch
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("=== Dynamischer Dispatch ===");
for (Animal animal : animals) {
// Dynamische Bindung - Laufzeit entscheidet!
System.out.println(animal.getType() + ": " + animal.makeSound());
System.out.println(animal.getType() + ": " + animal.move());
System.out.println();
}
// Polymorphe Verarbeitung
processAnimal(animals.get(0)); // Dog
processAnimal(animals.get(1)); // Cat
processAnimal(animals.get(2)); // Bird
}
// Polymorphe Methode
public static void processAnimal(Animal animal) {
System.out.println("Processing " + animal.getType());
// Dynamischer Dispatch
String sound = animal.makeSound();
String movement = animal.move();
System.out.println(" Sound: " + sound);
System.out.println(" Movement: " + movement);
// Type-spezifische Operationen (mit 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
Wie Single Dispatch funktioniert
public class SingleDispatchDemo {
// Single Dispatch - nur der Empfängertyp entscheidet
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() {
// Vereinfacht: gleichseitiges Dreieck
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; }
}
// Demonstration
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 mit Visitor Pattern
Visitor Pattern für Double Dispatch
public class DoubleDispatchDemo {
// Visitor Interface
public interface ShapeVisitor {
void visitCircle(Circle circle);
void visitRectangle(Rectangle rectangle);
void visitTriangle(Triangle triangle);
double getResult();
}
// Konkreter Visitor für Flächenberechnung
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 für Umfangberechnung
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(); // Vereinfacht
totalPerimeter += perimeter;
System.out.println("Triangle perimeter: " + String.format("%.2f", perimeter));
}
@Override
public double getResult() {
return totalPerimeter;
}
}
// Visitor für XML-Export
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; // Nicht relevant für diesen Visitor
}
public String getXML() {
return "<shapes>\n" + xml.toString() + "</shapes>";
}
}
// Demonstration von 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 mit Visitor Pattern ===");
// Area Visitor
System.out.println("Flächenberechnung:");
AreaVisitor areaVisitor = new AreaVisitor();
for (Shape shape : shapes) {
shape.accept(areaVisitor); // Double Dispatch!
}
System.out.println("Gesamtfläche: " + String.format("%.2f", areaVisitor.getResult()));
System.out.println();
// Perimeter Visitor
System.out.println("Umfangberechnung:");
PerimeterVisitor perimeterVisitor = new PerimeterVisitor();
for (Shape shape : shapes) {
shape.accept(perimeterVisitor); // Double Dispatch!
}
System.out.println("Gesamtumfang: " + String.format("%.2f", perimeterVisitor.getResult()));
System.out.println();
// XML Export Visitor
System.out.println("XML Export:");
XMLExportVisitor xmlVisitor = new XMLExportVisitor();
for (Shape shape : shapes) {
shape.accept(xmlVisitor); // Double Dispatch!
}
System.out.println(xmlVisitor.getXML());
}
}
Mehrfachdispatch (Multiple Dispatch)
Simulation von Mehrfachdispatch
public class MultipleDispatchDemo {
// Problem: Operationen, die von zwei Typen abhängen
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 - nur anderer Typ entscheidet
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 - Problem: wir wissen nicht welchen Typ wir haben
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; }
}
// Lösung mit Double Dispatch Pattern
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());
}
}
// Verbesserte Collider mit 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); // Umkehrung für 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);
}
}
// Demonstration
public static void demonstrateMultipleDispatch() {
System.out.println("=== Multiple Dispatch Simulation ===");
// Einfacher Single Dispatch (Problem)
System.out.println("Single Dispatch (Problem):");
Collider asteroid1 = new Asteroid("Rocky", 100.0);
Collider spaceship1 = new Spaceship("Explorer", 50.0);
asteroid1.collideWith(spaceship1); // Nur Typ von spaceship1 bekannt
System.out.println();
// Double Dispatch Lösung
System.out.println("Double Dispatch Solution:");
ImprovedAsteroid asteroid2 = new ImprovedAsteroid("Rocky", 100.0);
ImprovedSpaceship spaceship2 = new ImprovedSpaceship("Explorer", 50.0);
asteroid2.collideWith(spaceship2); // Beide Typen bekannt!
System.out.println();
// Komplexere Kollisionen
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("Complex collisions:");
for (int i = 0; i < objects.size(); i++) {
for (int j = i + 1; j < objects.size(); j++) {
objects.get(i).collideWith(objects.get(j));
}
}
}
}
Virtuelle Methodentabellen (vtable)
Wie vtables funktionieren
public class VTableDemo {
// Vereinfachte Darstellung einer vtable
public static class VTableVisualization {
// Basisklasse
public static class Base {
// Virtuelle Methoden
public virtual void method1() { System.out.println("Base.method1"); }
public virtual void method2() { System.out.println("Base.method2"); }
// Konkrete Methode
public void concreteMethod() { System.out.println("Base.concreteMethod"); }
}
// Abgeleitete Klasse 1
public static class Derived1 extends Base {
@Override
public void method1() { System.out.println("Derived1.method1"); }
// method2 geerbt
@Override
public void method2() { System.out.println("Derived1.method2"); }
}
// Abgeleitete Klasse 2
public static class Derived2 extends Base {
// method1 geerbt
@Override
public void method2() { System.out.println("Derived2.method2"); }
public void method3() { System.out.println("Derived2.method3"); }
}
// Demonstration der vtable
public static void demonstrateVTable() {
System.out.println("=== VTable Demonstration ===");
Base base = new Base();
Base derived1 = new Derived1();
Base derived2 = new Derived2();
// Vtable für Base:
// [Base.method1, Base.method2]
System.out.println("Base object:");
base.method1(); // Base.method1
base.method2(); // Base.method2
// Vtable für Derived1:
// [Derived1.method1, Derived1.method2]
System.out.println("\nDerived1 object:");
derived1.method1(); // Derived1.method1
derived1.method2(); // Derived1.method2
// Vtable für Derived2:
// [Base.method1, Derived2.method2]
System.out.println("\nDerived2 object:");
derived2.method1(); // Base.method1 (geerbt)
derived2.method2(); // Derived2.method2
// Konkrete Methode - nicht in vtable
System.out.println("\nConcrete method:");
base.concreteMethod();
derived1.concreteMethod();
derived2.concreteMethod();
}
}
// Performance-Vergleich
public static class PerformanceComparison {
// Finale Methode - kein virtueller Aufruf
public static class FinalClass {
public final void finalMethod() {
// Kein vtable Lookup
}
}
// Virtuelle Methode - vtable Lookup
public static class VirtualClass {
public void virtualMethod() {
// vtable Lookup erforderlich
}
}
public static void performanceTest() {
FinalClass finalObj = new FinalClass();
VirtualClass virtualObj = new VirtualClass();
int iterations = 100_000_000;
// Test finale Methode
long start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
finalObj.finalMethod();
}
long finalTime = System.nanoTime() - start;
// Test virtuelle Methode
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 in der Praxis
Framework-Beispiel: Plugin-System
public class PluginDispatchDemo {
// Plugin Interface
public interface Plugin {
void initialize();
void execute();
void shutdown();
String getName();
String getVersion();
}
// Abstrakte Basisklasse für 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());
}
}
// Abstrakte Methode für konkrete Implementierung
protected abstract void doExecute();
// Standardimplementierungen
@Override
public void initialize() {
System.out.println("Initializing plugin: " + name);
}
@Override
public void shutdown() {
System.out.println("Shutting down plugin: " + name);
}
}
// Konkrete Plugins
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...");
}
}
// Plugin Manager mit 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(); // Dynamischer Dispatch
}
}
public void shutdownAllPlugins() {
System.out.println("\n=== Shutting down all plugins ===");
for (Plugin plugin : plugins) {
plugin.shutdown(); // Dynamischer Dispatch
}
}
public void executePluginByName(String name) {
for (Plugin plugin : plugins) {
if (plugin.getName().equals(name)) {
plugin.execute(); // Dynamischer Dispatch
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());
}
}
}
// Demonstration
public static void demonstratePluginSystem() {
PluginManager manager = new PluginManager();
// Plugins registrieren
manager.registerPlugin(new DatabasePlugin());
manager.registerPlugin(new LoggingPlugin());
manager.registerPlugin(new SecurityPlugin());
// Alle Plugins ausführen
manager.listPlugins();
manager.executeAllPlugins();
// Spezifisches Plugin ausführen
System.out.println("\n=== Executing specific plugin ===");
manager.executePluginByName("LoggingPlugin");
// Aufräumen
manager.shutdownAllPlugins();
}
}
Best Practices für Dispatch
1. Final für Performance
public class DispatchBestPractices {
// Finale Methoden für Performance
public static class PerformanceOptimized {
// Finale Methode - kein virtueller Aufruf
public final void criticalMethod() {
// Hot Path Code
}
// Finale Klasse - keine Vererbung möglich
public static final class ImmutableData {
private final int value;
public ImmutableData(int value) {
this.value = value;
}
public final int getValue() {
return value;
}
}
}
// Interface für Flexibilität
public interface Processor {
void process();
}
// Konkrete Implementierung
public static class FastProcessor implements Processor {
@Override
public void process() {
// Implementierung
}
}
}
2. Vermeidung von Dispatch-Problemen
public class DispatchProblems {
// Problem: Überladung mit Referenztypen
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); // Ruft process(Object) auf!
}
}
// Lösung: Klare Methodennamen
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);
}
}
}
}
Prüfungsrelevante Konzepte
Wichtige Unterscheidungen
-
Statisch vs Dynamisch
- Statisch: Compiler-zeit (Überladen)
- Dynamisch: Laufzeit (Überschreiben)
-
Single vs Double Dispatch
- Single: Ein Objekttyp entscheidet
- Double: Zwei Objekttypen entscheiden
-
vtable vs Early Binding
- vtable: Virtuelle Methoden
- Early Binding: Finale Methoden
Typische Prüfungsaufgaben
- Erklären Sie Single vs Double Dispatch
- Implementieren Sie Visitor Pattern
- Vergleichen Sie statische und dynamische Bindung
- Beschreiben Sie vtable Funktionsweise
- Lösen Sie Dispatch-Probleme
Zusammenfassung
Dispatch ist fundamental für objektorientierte Programmierung:
- Statischer Dispatch: Vorhersagbar, performant (Überladen)
- Dynamischer Dispatch: Flexibel, erweiterbar (Überschreiben)
- Single Dispatch: Standard in den meisten OOP-Sprachen
- Double Dispatch: Über Visitor Pattern realisierbar
- vtables: Effiziente Implementierung dynamischen Dispatch
Die richtige Anwendung von Dispatch ermöglicht flexible, wartbare und performante Softwarearchitekturen.