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.

Objektno orijentirani sistemi modeliraju problem kao skup interaktivnih objekata koji izvršavaju operacije na koje se poziva po imenu. Polimorfizam je fenomen u kojem donekle zamjenjivi objekti svaki izlažu operaciju istog imena, ali se moguće razlikuju u ponašanju. Na primjer, objekt i objekt imaju metod koja se može koristiti za zapisivanje osobnog zapisa u pohranu. Njihove implementacije se razlikuju. Program sadrži referencu na objekt koji može biti ili objekt ili objekt. Koji je to objekt, možda je određeno postavkom za vrijeme izvođenja, i u ovoj fazi, program možda ne zna ili ne mari koji. Kada program pozove na objektu, nešto mora odabrati koje će se ponašanje primijeniti. Ako se OOP zamisli kao poruka objektima, onda u ovom primjeru program šalje poruku objektu nepoznatog tipa, ostavljajući sistemu za podršku za vrijeme izvođenja da pošalje poruku pravom objektu. Objekt izvršava ono ponašanje koje implementira.

Dinamička otprema je u suprotnosti sa statičkom otpremom, u kojoj se implementacija polimorfne operacije odabire u vrijeme kompajliranja. Svrha dinamičke otpreme je odgoditi odabir odgovarajuće implementacije dok se ne sazna tip parametra (ili više parametara) u vrijeme izvođenja.

Dinamička otprema se razlikuje od kasnog vezivanja (također poznatog kao dinamičko vezivanje). Vezivanje imena povezuje ime s operacijom. Polimorfna operacija ima nekoliko implementacija, a sve su povezane s istim imenom. Veze se mogu napraviti u vrijeme kompajliranja ili (s kasnim vezivanjem) u vrijeme izvođenja. S dinamičkom otpremom, jedna određena implementacija operacije se bira u vrijeme izvođenja. Dok dinamička otprema ne podrazumijeva kasno vezivanje, kasno vezivanje podrazumijeva dinamičku otpremu, budući da implementacija operacije s kasnim vezivanjem nije poznata do vremena izvođenja.

Mehanizmi Jednostruka i višestruka otprema

Dinamička otprema je u suprotnosti sa tatičkom otpremom, u kojoj se implementacija polimorfne operacije odabire u vrijeme kompajliranja. Svrha dinamičke otpreme je odgoditi odabir odgovarajuće implementacije dok se ne sazna tip parametra (ili više parametara) u vrijeme izvođenja.

Dinamička otprema se razlikuje od kasnog vezivanja (kasnog vezivanja) (također poznatog kao dinamičko vezivanje). Vezivanje imena povezuje ime s operacijom. Polimorfna operacija ima nekoliko implementacija, a sve su povezane s istim imenom. Veze se mogu napraviti u vrijeme kompajliranja ili (kod kasnog vezivanja) u vrijeme izvođenja. Kod dinamičke otpreme, jedna određena implementacija operacije se bira u vrijeme izvođenja. Iako dinamičko slanje ne podrazumijeva kasno vezivanje, kasno vezivanje podrazumijeva dinamičko slanje, budući da implementacija operacije kasnog vezivanja nije poznata do vremena izvršavanja.

Mehanizmi Jednostruko i višestruko slanje

Izbor verzije metoda za pozivanje može se zasnivati na jednom objektu ili na kombinaciji objekata. Prvo se naziva jednostruko slanje i direktno ga podržavaju uobičajeni objektno orijentisani jezici kao što su Smalltalk, C++, Java, C#, Objective-C, Swift, JavaScript i Python. U ovim i sličnim jezicima, može se nazvati metod za dijeljenje sa sintaksom koja liči nadividend.divide(djelitelj)# dividenda / djelitelj gdje su parametri opcionalni. Ovo se smatra slanjem poruke pod nazivom s parametrom na . Implementacija će biti odabrana samo na osnovu tipa (možda racionalni, floating point, matrix), zanemarujući tip ili vrijednost .

Nasuprot tome, neki jezici otpremaju metode ili funkcije na osnovu kombinacije operanada; u slučaju dijeljenja, tipovi i zajedno određuju koja će se operacija izvršiti. Ovo je poznato kao višestruko otpremanje. Primjeri jezika koji podržavaju višestruku otpremu su Common Lisp, Dylan i Julia.

Mehanizmi dinamičke otpreme Jezik se može implementirati s različitim mehanizmima dinamičke otpreme. Izbori mehanizma dinamičke otpreme koje nudi jezik u velikoj mjeri mijenjaju programske paradigme koje su dostupne ili su najprirodnije za korištenje unutar datog jezika.

Normalno, u tipiziranom jeziku, mehanizam otpreme će se izvršavati na osnovu tipa argumenata (najčešće na osnovu tipa primaoca poruke). Jezici sa slabim ili nikakvim sistemima tipizacije često nose tabelu otpreme kao dio podataka objekta za svaki objekat. Ovo omogućava ponašanje instance jer svaka instanca može mapirati datu poruku na zaseban način.

Neki jezici nude hibridni pristup.

Dinamička otprema će uvijek imati dodatni trošak, tako da neki jezici nude statičku otpremu za određene metode.

C++ implementacija C++ koristi rano povezivanje i nudi i dinamičko i statičko slanje. Podrazumijevani oblik slanja je statički. Da bi dobio dinamičko slanje, programer mora deklarirati metod kao .

C++ kompajleri obično implementiraju dinamičko slanje sa strukturom podataka koja se naziva virtualna tabela funkcija (vtable) koja definira mapiranje imena na implementaciju za datu klasu kao skup pokazivača funkcija članica. Ovo je isključivo detalj implementacije, jer C++ specifikacija ne spominje vtables. Instance tog tipa će zatim pohraniti pokazivač na ovu tabelu kao dio svojih podataka instance, što komplicira scenarije kada se koristi višestruko nasljeđivanje. Budući da C++ ne podržava kasno povezivanje, virtualna tabela u C++ objektu ne može se mijenjati za vrijeme izvođenja, što ograničava potencijalni skup ciljeva slanja na konačan skup odabran za vrijeme kompajliranja.

Preopterećenje tipova ne proizvodi dinamičko slanje u C++ jer jezik smatra tipove parametara poruke dijelom formalnog imena poruke. To znači da naziv poruke koji programer vidi nije formalni naziv koji se koristi za povezivanje.

Implementacija u Go, Rustu i Nimu U Go, Rust i Nim, koristi se svestranija varijacija ranog povezivanja. Vtable pokazivači se prenose s referencama objekata kao 'fat pointeri' ('interfejsi' u Gou ili 'trait objekti' u Rustu)). 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++ 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 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 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 trait Pet { fn speak(&self); }

struct Dog<'a> { name: &'a str }

struct Cat<'a> { name: &'a str }

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

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

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

impl Pet<'a> for Cat<'a> { 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