Skip to content
IRC-CodingIRC-Coding
ООПDispatchДинамическая привязкаSingle DispatchDouble DispatchVisitor PatternМножественный dispatchvtableПолиморфизм

OOP Dispatch: Динамическая привязка и множественный dispatch

Динамическая привязка, Single/Double Dispatch и Visitor Pattern. Java примеры методов и множественного dispatch.

S

schutzgeist

16 min read
OOP Dispatch: Динамическая привязка и множественный dispatch

Dispatch в ООП: Динамическая привязка, Single и Double Dispatch

Dispatch — это механизм выбора конкретной реализации метода при его вызове. Выбор может происходить статически во время компиляции или динамически во время выполнения программы. Это основа полиморфизма.

Что такое Dispatch?

Dispatch разрешает вызов метода в конкретную реализацию. От способа этого разрешения зависит гибкость и производительность программы.

Виды Dispatch

  1. Статический Dispatch: решение принимается на этапе компиляции
  2. Динамический Dispatch: решение принимается во время выполнения
  3. Single Dispatch: выбор зависит от типа объекта-получателя
  4. Double Dispatch: выбор зависит от типов двух объектов
  5. Множественный Dispatch: выбор зависит от типов нескольких объектов

Статический и динамический Dispatch

Статический Dispatch (перегрузка)

public class StaticDispatchDemo {
    
    // Переиспользуемые методы - статический 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();
        
        // Прямые вызовы - однозначный выбор
        printer.print("Hello");           // print(String)
        printer.print(42);               // print(Integer)
        printer.print("Test", 3);         // print(String, int)
        
        // Важный момент: статические типы решают!
        Object obj1 = "Hello";
        Object obj2 = 42;
        
        printer.print(obj1);  // print(Object) - компилятор видит только тип Object!
        printer.print(obj2);  // print(Object) - компилятор видит только тип Object!
        
        // Приведение типа для выбора нужной перегрузки
        printer.print((String) obj1);   // print(String)
        // printer.print((Integer) obj2); // ClassCastException во время выполнения!
    }
}

Динамический Dispatch (переопределение)

public class DynamicDispatchDemo {
    
    // Базовый класс с виртуальными методами
    public static abstract class Animal {
        protected String name;
        
        public Animal(String name) {
            this.name = name;
        }
        
        // Виртуальный метод - привязывается динамически
        public String makeSound() {
            return name + " makes a sound";
        }
        
        // Еще один виртуальный метод
        public String move() {
            return name + " moves";
        }
        
        // Финальный метод - нельзя переопределить
        public final String getName() {
            return name;
        }
        
        // Абстрактный метод - нужно переопределить
        public abstract String getType();
    }
    
    // Конкретные подклассы
    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";
        }
        
        // Дополнительный метод
        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";
        }
    }
    
    // Демонстрация динамического 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("=== Динамический Dispatch ===");
        for (Animal animal : animals) {
            // Динамическая привязка - решение принимается во время выполнения!
            System.out.println(animal.getType() + ": " + animal.makeSound());
            System.out.println(animal.getType() + ": " + animal.move());
            System.out.println();
        }
        
        // Полиморфная обработка
        processAnimal(animals.get(0)); // Dog
        processAnimal(animals.get(1)); // Cat
        processAnimal(animals.get(2)); // Bird
    }
    
    // Полиморфный метод
    public static void processAnimal(Animal animal) {
        System.out.println("Processing " + animal.getType());
        
        // Динамический dispatch
        String sound = animal.makeSound();
        String movement = animal.move();
        
        System.out.println("  Sound: " + sound);
        System.out.println("  Movement: " + movement);
        
        // Операции, специфичные для типа (с 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

Как работает Single Dispatch

public class SingleDispatchDemo {
    
    // Single Dispatch - только тип получателя имеет значение
    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() {
            // Упрощено: равносторонний треугольник
            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; }
    }
    
    // Демонстрация
    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 с помощью Visitor Pattern

Visitor Pattern для двойной диспетчеризации

public class DoubleDispatchDemo {
    
    // Visitor Interface
    public interface ShapeVisitor {
        void visitCircle(Circle circle);
        void visitRectangle(Rectangle rectangle);
        void visitTriangle(Triangle triangle);
        double getResult();
    }
    
    // Конкретный Visitor для расчёта площади
    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 для расчёта периметра
    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(); // Упрощённо
            totalPerimeter += perimeter;
            System.out.println("Triangle perimeter: " + String.format("%.2f", perimeter));
        }
        
        @Override
        public double getResult() {
            return totalPerimeter;
        }
    }
    
    // Visitor для экспорта в 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; // Не используется для этого Visitor
        }
        
        public String getXML() {
            return "<shapes>\n" + xml.toString() + "</shapes>";
        }
    }
    
    // Демонстрация двойной диспетчеризации
    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 с Visitor Pattern ===");
        
        // Area Visitor
        System.out.println("Расчёт площади:");
        AreaVisitor areaVisitor = new AreaVisitor();
        for (Shape shape : shapes) {
            shape.accept(areaVisitor); // Double Dispatch!
        }
        System.out.println("Общая площадь: " + String.format("%.2f", areaVisitor.getResult()));
        System.out.println();
        
        // Perimeter Visitor
        System.out.println("Расчёт периметра:");
        PerimeterVisitor perimeterVisitor = new PerimeterVisitor();
        for (Shape shape : shapes) {
            shape.accept(perimeterVisitor); // Double Dispatch!
        }
        System.out.println("Общий периметр: " + String.format("%.2f", perimeterVisitor.getResult()));
        System.out.println();
        
        // XML Export Visitor
        System.out.println("Экспорт в XML:");
        XMLExportVisitor xmlVisitor = new XMLExportVisitor();
        for (Shape shape : shapes) {
            shape.accept(xmlVisitor); // Double Dispatch!
        }
        System.out.println(xmlVisitor.getXML());
    }
}

Множественная диспетчеризация (Multiple Dispatch)

Имитация множественной диспетчеризации

public class MultipleDispatchDemo {
    
    // Задача: операции, зависящие от двух типов
    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 - определяется только тип другого объекта
            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 - проблема: мы не знаем точный тип объекта
            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; }
    }
    
    // Решение с помощью 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());
        }
    }
    
    // Улучшенные Collider с двойной диспетчеризацией
    public static class ImprovedAsteroid extends Asteroid {
        public ImprovedAsteroid(String name, double mass) {
            super(name, mass);
        }
        
        @Override
        public void collideWith(Collider other) {
            other.collideWithAsteroid(this); // Обратный вызов для 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);
        }
    }
    
    // Демонстрация
    public static void demonstrateMultipleDispatch() {
        System.out.println("=== Имитация Multiple Dispatch ===");
        
        // Простая Single Dispatch (проблема)
        System.out.println("Single Dispatch (проблема):");
        Collider asteroid1 = new Asteroid("Rocky", 100.0);
        Collider spaceship1 = new Spaceship("Explorer", 50.0);
        asteroid1.collideWith(spaceship1); // Известен только тип spaceship1
        
        System.out.println();
        
        // Решение с Double Dispatch
        System.out.println("Решение с Double Dispatch:");
        ImprovedAsteroid asteroid2 = new ImprovedAsteroid("Rocky", 100.0);
        ImprovedSpaceship spaceship2 = new ImprovedSpaceship("Explorer", 50.0);
        asteroid2.collideWith(spaceship2); // Известны оба типа!
        
        System.out.println();
        
        // Более сложные столкновения
        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));
            }
        }
    }
}

Виртуальные таблицы методов (vtable)

Как работают vtable

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 в реальных приложениях

Пример из фреймворка: система плагинов

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 для Dispatch

1. Final для производительности

public class DispatchBestPractices {
    
    // Финальные методы для производительности
    public static class PerformanceOptimized {
        // Финальный метод - без виртуального вызова
        public final void criticalMethod() {
            // Hot Path Code
        }
        
        // Финальный класс - наследование невозможно
        public static final class ImmutableData {
            private final int value;
            
            public ImmutableData(int value) {
                this.value = value;
            }
            
            public final int getValue() {
                return value;
            }
        }
    }
    
    // Интерфейс для гибкости
    public interface Processor {
        void process();
    }
    
    // Конкретная реализация
    public static class FastProcessor implements Processor {
        @Override
        public void process() {
            // Реализация
        }
    }
}

2. Избежание проблем с Dispatch

public class DispatchProblems {
    
    // Проблема: перегрузка с типами ссылок
    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); // Вызывает process(Object)!
        }
    }
    
    // Решение: понятные имена методов
    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);
            }
        }
    }
}

Концепции для экзамена

Важные различия

  1. Статический против динамического

    • Статический: время компиляции (перегрузка)
    • Динамический: время выполнения (переопределение)
  2. Single против Double Dispatch

    • Single: один тип объекта определяет решение
    • Double: два типа объектов определяют решение
  3. vtable против Early Binding

    • vtable: виртуальные методы
    • Early Binding: финальные методы

Типовые экзаменационные задачи

  1. Объясните Single vs Double Dispatch
  2. Реализуйте паттерн Visitor
  3. Сравните статическую и динамическую привязку
  4. Опишите механизм работы vtable
  5. Разрешите проблемы с Dispatch

Итого

Dispatch является фундаментом объектно-ориентированного программирования:

  • Статический Dispatch: предсказуемый, быстрый (перегрузка)
  • Динамический Dispatch: гибкий, расширяемый (переопределение)
  • Single Dispatch: стандарт в большинстве OOP-языков
  • Double Dispatch: реализуется через паттерн Visitor
  • vtables: эффективная реализация динамического Dispatch

Правильное применение Dispatch обеспечивает гибкие, поддерживаемые и производительные архитектуры программного обеспечения.

Продолжение в курсе OOP

Все статьи про OOP теперь завершены. Вернитесь к первой статье: Основы объектно-ориентированного программирования OOP.

Назад к блогу
Share:

Похожие статьи