Dinamička dispečerska obrada

U računarstvu, **dinamička dispečerska obrada** je proces odabira koju implementaciju polimorfne operacije (metoda ili funkcije) pozvati u vrijeme izvođenja. Obično se koristi u jezicima i sistemima objektno orijentisanog programiranja (OOP) i smatra se glavnom karakteristikom.).
Ovo odvaja podržane interfejse od osnovnih struktura podataka. Svaka kompajlirana biblioteka ne mora znati cijeli raspon podržanih interfejsa da bi ispravno koristila tip, već samo specifičan raspored vtable-a koji im je potreban. Kod može prosljeđivati različite interfejse istom dijelu podataka različitim funkcijama. Ova svestranost dolazi na štetu dodatnih podataka sa svakom referencom objekta, što je problematično ako se mnogo takvih referenci trajno pohranjuje.

Termin *debeli pokazivač* jednostavno se odnosi na pokazivač s dodatnim povezanim informacijama. Dodatne informacije mogu biti pokazivač vtable-a za dinamičko slanje opisano gore, ali su češće veličina povezanog objekta za opisivanje npr. *kriške*.

#### Implementacija Smalltalka
Smalltalk koristi dispečer poruka zasnovan na tipu. Svaka instanca ima jedan tip čija definicija sadrži metode. Kada instanca primi poruku, dispečer pretražuje odgovarajuću metodu u mapi poruka-metoda za taj tip, a zatim poziva metodu.

Budući da tip može imati lanac osnovnih tipova, ova pretraga može biti skupa. Naivna implementacija Smalltalk mehanizma izgleda ima znatno veće opterećenje od C++ mehanizma, a to opterećenje bi se javljalo za svaku poruku koju objekat primi.

Prave Smalltalk implementacije često koriste tehniku poznatu kao ugrađeno keširanje što čini otpremu metoda vrlo brzom. Inline keširanje u osnovi pohranjuje prethodnu adresu odredišne metode i klasu objekta mjesta poziva (ili više parova za višesmjerno keširanje). Keširana metoda se inicijalizira s najčešćom ciljnom metodom (ili samo rukovateljem promašaja keša), na temelju selektora metode. Kada se mjesto poziva metode dosegne tijekom izvršavanja, ona samo poziva adresu u kešu. (U generatoru dinamičkog koda, ovaj poziv je direktan poziv jer se direktna adresa vraća pomoću logike promašaja keša.) Prolog kod u pozvanoj metodi zatim uspoređuje keširanu klasu sa stvarnom klasom objekta i ako se ne podudaraju, izvršavanje se grana na rukovatelja promašaja keša kako bi pronašao ispravnu metodu u klasi. Brza implementacija može imati više unosa u kešu i često je potrebno samo nekoliko instrukcija da se izvršenje dovede do ispravne metode pri početnom promašaju keša. Uobičajeni slučaj bit će podudaranje keširane klase, a izvršavanje će se jednostavno nastaviti u metodi.

Izvanlinijsko keširanje se također može koristiti u logici pozivanja metode, korištenjem klase objekta i selektora metode. U jednom dizajnu, selektor klase i metode su heširani i koriste se kao indeks u tabeli keša otpreme metoda.

Budući da je Smalltalk reflektivni jezik, mnoge implementacije omogućavaju mutaciju pojedinačnih objekata u objekte sa dinamički generisanim tabelama pretraživanja metoda. Ovo omogućava promjenu ponašanja objekta na nivou pojedinačnog objekta. Iz ovoga je nastala čitava kategorija jezika poznata kao jezik zasnovan na prototipovima, od kojih su najpoznatiji Self i JavaScript. Pažljiv dizajn keširanja otpreme metoda omogućava čak i jezicima zasnovanim na prototipovima da imaju visokoperformansnu otpremu metoda.

Mnogi drugi dinamički tipizirani jezici, uključujući Python, Ruby, Objective-C i Groovy koriste slične pristupe.

## Primjeri
Slika:UML class pet.svg

### C++
```cpp
import std;

// napravi Pet apstraktnu virtuelnu bazu klase
class Pet {
protected:
std::string name;
public:
Pet(const std::string& name):
name{name} {}

virtual void speak() = 0;
};

class Dog : public Pet {
public:
Dog(const std::string& name):
Pet(name) {}!'", name);
    }
};

class Cat : public Pet {
public:
    Cat(const std::string& name):
        Pet(name) {}

    void speak() override {
        std::println("{} says 'Meow!'", name);
    }
};

// speak() will be able to accept anything deriving from Pet
void speak(Pet& pet) {
    pet.speak();
}

int main() {
    Dog fido("Fido");
    Cat simba("Simba");
    speak(fido);
    speak(simba);
    return 0;
}
```

### Java
```java
abstract class Pet {
    protected String name;

    public Pet(String name) {
        this.name = name;
    }

    public abstract void speak();
}

class Dog extends Pet {
    public Dog(String name) {
        super(name);
    }

    @Override
    public void speak() {
        System.out.printf("%s says 'Woof!'%n", name);
    }
}

class Cat extends Pet {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void speak() {
        System.out.printf("%s says 'Meow!'%n", name);
    }
};

public class Main {
    public static void speak(Pet pet) {
        pet.speak();
    }

    public static void main(String[] args) {
        Dog fido = new Dog("Fido");
        Cat simba = new Cat("Simba");
        speak(fido);
        speak(simba);
    }
}
```

### Python
```python
from abc import ABC, abstractmethod
from typing import Never

# ABC is a class used to denote that classes directly extending it are abstract
class Pet(ABC):
    def __init__(self, name: str) -> None:
        self.name = name

    @abstractmethod
    def speak(self) -> Never:
        raise NotImplementedError("Abstract method must be implemented by derived classes")

class Dog(Pet):
    def __init__(self, name: str) -> None:
        super.__init__(name)

    def speak(self) -> None:
        print(f"{self.name} says 'Woof!'")

class Cat(Pet):
    def __init__(self, name: str) -> None:
        super.__init__(name)

    def speak(self) -> None:
        print(f"{self.name} says 'Meow!'")

def speak(pet: Pet) -> None:
    # Dynamically dispatches the speak method
    # pet can either be an instance of Dog or Cat
    pet.speak()

if __name__ == "__main__":
    fido: Dog = Dog("Fido")
    speak(dog)
    simba: Cat = Cat("Simba")
    speak(cat)
```

### Rust
```rust
trait Pet {
    fn speak(&self);
}

struct Dog {
    name: &'a str
}

struct Cat {
    name: &'a str
}

impl Dog {
    fn new(name: &'a str) -> Self {
        Dog { name }
    }
}

impl Cat {
    fn new(name: &'a str) -> Self {
        Cat { name }
    }
}

impl Pet for Dog {
    fn speak(&self) {
        println!("{} says 'Woof!'", self.name);
    }
}

impl Pet for Cat {
    fn speak(&self) {
        println!("{} says 'Meow!'", self.name);
    }
}

// speak() uses dynamic dispatch and resolves the type at runtime,
// for any type that implements the trait Pet
fn speak(pet: &dyn Pet) {
    pet.speak();
}

fn main() {
    let fido: Dog = Dog::new("Fido");
    let simba: Cat = Cat::new("Simba");
    speak(&fido);
    speak(&simba);
}
```

## Također pogledajte
* Kucanje tipa "patka"
* Dvostruko slanje
* Preopterećenje funkcija
* Nadjačavanje metode
* Povezivanje imena

## Reference
## Dodatna literatura
* 
* 
*  (NB. Something similar to "fat pointers" specifically for Intel's real-mode  on x86 processors, containing both a deliberately denormalized pointer to a shared code entry point and some info to still distinguish the different callers in the shared code. While, in an open system, pointer-normalizing 3rd-party instances (in other drivers or applications) cannot be ruled out completely on public interfaces, the scheme can be used safely on internal interfaces to avoid redundant entry code sequences.)
*  https://web.archive.org/web/20220711181007/https://www.drdobbs.com/architecture-and-design/cs-biggest-mistake/228701625
* 

Kategorija:Računarstvo
Kategorija:Programiranje