Che cos'è l'associazione anticipata e tardiva?


77

Continuo a sentir parlare del legame anticipato e tardivo, ma non capisco cosa siano. Ho trovato la seguente spiegazione che non capisco:

L'associazione anticipata si riferisce all'assegnazione di valori alle variabili durante la fase di progettazione, mentre l'associazione tardiva si riferisce all'assegnazione di valori alle variabili durante la fase di esecuzione.

Qualcuno potrebbe definire i due tipi di rilegatura e confrontarli?


1
tempo di compilazione vs runtime.
barlop

Risposte:


84

Ci sono due concetti principali nella confusione: rilegatura e caricamento. È integrato dal concetto di DataBinding, che è spesso da qualche parte nel mezzo e fa entrambe le cose. Dopo averlo considerato, aggiungerò un altro concetto, per completare il trifecta, la spedizione.

tipi

Rilegatura in ritardo : il tipo è sconosciuto fino a quando la variabile non viene esercitata durante il runtime; di solito attraverso l'incarico ma ci sono altri mezzi per forzare un tipo; le lingue tipizzate dinamicamente chiamano questa una caratteristica sottostante, ma molte lingue tipizzate staticamente hanno un metodo per ottenere l'associazione tardiva

Implementato spesso utilizzando [dinamici] tipi dinamici, opzioni di introspezione / riflessione, flag e compilatore o tramite metodi virtuali prendendo in prestito ed estendendo l'invio dinamico

Early Binding : il tipo è noto prima che la variabile venga esercitata durante il runtime, di solito attraverso un mezzo statico e dichiarativo

Implementato spesso utilizzando tipi primitivi standard

funzioni

Invio statico : noto, funzione specifica o subroutine al momento della compilazione; è inequivocabile e abbinato alla firma

Implementato come funzioni statiche; nessun metodo può avere la stessa firma

Invio dinamico : non una funzione o subroutine specifica al momento della compilazione; determinato dal contesto durante l'esecuzione. Esistono due diversi approcci al "dispacciamento dinamico", distinti da quali informazioni contestuali vengono utilizzate per selezionare l'implementazione della funzione appropriata.

Nel singolo invio [ dinamico ] , solo il tipo di istanza viene utilizzato per determinare l'implementazione della funzione appropriata. Nei linguaggi tipicamente statici, ciò che ciò significa in pratica è che il tipo di istanza decide quale implementazione del metodo viene utilizzata indipendentemente dal tipo di riferimento indicato quando la variabile viene dichiarata / assegnata. Poiché viene utilizzato un solo tipo, il tipo dell'istanza dell'oggetto, per dedurre l'implementazione appropriata, questo approccio è chiamato "invio singolo".

Esiste anche un invio multiplo [ dinamico ] , in cui i tipi di parametri di input aiutano anche a determinare quale implementazione di funzione chiamare. Poiché più tipi - sia il tipo dell'istanza sia i tipi dei parametri - influenzano quale implementazione del metodo è selezionata, questo approccio è chiamato "invio multiplo".

Implementato come funzioni virtuali o astratte; altri indizi includono metodi sostituiti, nascosti o ombreggiati.

NB: se il sovraccarico del metodo comporta un invio dinamico è specifico della lingua. Ad esempio, in Java, i metodi sovraccarichi vengono inviati staticamente.

Valori

Lazy Loading : strategia di inizializzazione dell'oggetto che difende l'assegnazione del valore fino a quando necessario ; consente a un oggetto di trovarsi in uno stato sostanzialmente valido ma consapevolmente incompleto e di attendere che i dati siano necessari prima di caricarlo; spesso trovato particolarmente utile per caricare set di dati di grandi dimensioni o attendere risorse esterne

Implementato spesso non caricando intenzionalmente una raccolta o un elenco in un oggetto composito durante il costruttore o le chiamate di inizializzazione fino a quando alcuni chiamanti a valle non chiedono di vedere il contenuto di quella raccolta (es. Get_value_at, get_all_as, ecc.). Le variazioni includono il caricamento di meta informazioni sulla raccolta (come dimensioni o chiavi), ma omettendo i dati effettivi; fornisce inoltre un meccanismo ad alcuni runtime per fornire agli sviluppatori uno schema di implementazione singleton abbastanza sicuro ed efficiente

Caricamento desideroso : strategia di inizializzazione dell'oggetto che esegue immediatamente tutte le assegnazioni di valore per avere tutti i dati necessari per essere completi prima di considerarsi in uno stato valido.

Implementato spesso fornendo agli oggetti compositi tutti i loro dati noti il ​​più presto possibile, come durante una chiamata del costruttore o l'inizializzazione

Data Binding : spesso comporta la creazione di un collegamento o una mappa attivi tra due flussi di informazioni compatibili in modo che le modifiche a uno si riflettano nell'altro e viceversa; per essere compatibili spesso devono avere un tipo di base o un'interfaccia comune

Implementato spesso come tentativo di fornire una sincronizzazione più chiara e coerente tra diversi aspetti dell'applicazione (ad esempio view-model to view, model to controller, ecc.) E parla di concetti come source e target, endpoint, bind / unbind, update ed eventi come on_bind, on_property_change, on_explicit, on_out_of_scope


NOTA EDIT: ultima modifica importante per fornire una descrizione degli esempi di come questi si verificano spesso. Particolari esempi di codice dipendono interamente dall'implementazione / runtime / piattaforma


2
Questa risposta sembra troppo specifica per i linguaggi orientati agli oggetti.
Jack,

27

Tutto ciò che viene deciso dal compilatore durante la compilazione può essere riferito al binding EARLY / COMPILE TIME e tutto ciò che deve essere deciso in RUNTIME è chiamato binding LATE / RUNTIME .

Per esempio,

Sovraccarico del metodo e sostituzione del metodo .

1) Nel metodo Il sovraccarico delle chiamate del metodo ai metodi viene deciso dal compilatore, nel senso che la funzione che verrà chiamata viene decisa dal compilatore al momento della compilazione. Quindi essere in anticipo .

2) Nel metodo Override, viene deciso in RUNTIME quale metodo verrà chiamato. Quindi viene chiamato LATE BINDING .

Ho cercato di renderlo semplice e facile da ottenere. Spero che sia di aiuto.


9

L'associazione tardiva è quando il comportamento viene valutato in fase di esecuzione. È necessario quando vuoi davvero determinare come agire in base alle informazioni che hai solo quando il programma è in esecuzione. L'esempio più chiaro secondo me è il meccanismo di funzione virtuale, in particolare in C ++.

class A
{
public:
    void f() {}
    virtual void g() {}
};

class B : public A
{
    void f() {}
    virtual void g() {}
};

int main()
{
    A* a = new B;
    a->f();
    a->g();
}

In questo esempio, a->f()chiamerà effettivamente void A::f(), perché è associato in anticipo (o staticamente) e quindi il programma in fase di esecuzione pensa che sia solo un puntatore a una Avariabile di tipo, mentre a->g()in realtà chiamerà void B::g(), perché il compilatore, vedendo g()è virtuale, inietta codice per cercare l'indirizzo della funzione corretta da chiamare in fase di esecuzione.


1
"Il runtime"? Stai parlando di C ++. Il C ++ viene compilato direttamente in codice macchina, non è necessario un runtime per risolvere i metodi virtuali.
tdammers,

3
@tdammers C ++ in realtà ha bisogno di una libreria di runtime, anche se non per chiamate virtuali. Se leggi attentamente, noterai che questa risposta dice che il compilatore "inietta codice per cercare l'indirizzo della funzione corretta [...] in fase di esecuzione".

Bene, ma quel "codice per cercare l'indirizzo della funzione corretta" è fondamentalmente solo una dereferenza del puntatore a due stadi agnostica seguita da una chiamata di funzione. Non c'è "pensiero" coinvolto; l'unica ragione per cui funziona in modo affidabile è perché il compilatore esegue il controllo del tipo in fase di compilazione ; in fase di esecuzione, il codice generato si fida del compilatore per aver svolto i compiti di controllo del tipo. Se usi cast non sicuri (ad esempio cast puntatore in stile C), puoi legalmente trattare gli oggetti C ++ come oggetti della classe sbagliata, ma i loro vtable saranno completamente incasinati e il codice si romperà.
tdammers,

@tdammers Ho provato a stare lontano da quel tipo di risposta, perché è un dettaglio di implementazione dei compilatori, che potrebbe o non potrebbe essere vero per alcuni compilatori esoterici. Ciò che conta è il concetto.
Yam Marcovic,

1
@tdammers E per "runtime" intendo "il programma in fase di runtime". Ovviamente il C ++ non è gestito. Ma dal momento che mi hai mostrato che può causare confusione, la sto cambiando in tutti i termini.
Yam Marcovic,

5

se hai familiarità con i puntatori a funzioni questo sarebbe un esempio. Le funzioni definite possono essere definite come associazione anticipata. mentre se usi i puntatori a funzione è il suo Binding in ritardo.

  int add(int x,int y)
  {
    return x+y;
  }
  int sub(int x,int y)
  {
      return x-y;
  }


    int main()
    {
     //get user choice
     int(*fp)(int,int);
     //if add
      fp=add;
     //else if sub
     fp=sub;
     cout<<fp(2,2);
    }

qui le funzioni add e sub sono funzioni (i suoi indirizzi sono associati al time-link di compilazione)

ma il puntatore alla funzione è in ritardo nel binding, il fp può chiamare add o sub a seconda della scelta dell'utente [in fase di esecuzione].


3

Il legame precoce e tardivo ha senso solo nel contesto dei tipi e non nel modo in cui lo descrivi. Praticamente tutti i linguaggi moderni sono digitati nel senso che tutti i valori hanno tipi fissi. La differenza si presenta quando guardiamo a linguaggi tipizzati dinamicamente vs staticamente. Nelle lingue tipizzate dinamicamente le variabili non hanno tipi, quindi possono fare riferimento a valori di qualsiasi tipo e ciò significa che quando si chiama un metodo su un oggetto a cui fa riferimento una variabile, l'unico modo per determinare se quella chiamata è valida o meno è cercare la classe per l'oggetto e vedere se quel metodo esiste realmente. Ciò consente alcune cose interessanti come l'aggiunta di nuovi metodi alle classi in fase di esecuzione perché la ricerca del metodo effettivo viene rinviata fino all'ultimo momento. Molte persone definiscono questo stato di cose tardivo vincolante.

In una lingua tipizzata staticamente le variabili hanno tipi e una volta dichiarate non possono fare riferimento a nessun valore che non sia dello stesso tipo. Questo non è strettamente vero, ma supponiamo che per ora. Ora, se sai che la variabile farà sempre riferimento a valori di un tipo specifico, non c'è motivo di capire se una chiamata al metodo è valida o meno in fase di esecuzione perché puoi determinare la validità prima che il codice venga mai eseguito. Questo è indicato come associazione anticipata.

Un esempio per dimostrare l'associazione tardiva nel rubino:

a = 1 # a is an integer at this point
a.succ # asking for its successor is valid

class A
  def method_a
    # some code
  end
end

a = A.new
a.method_a # this is also valid
a.succ # this is not valid


class A # we can re-open the class and add a method
  def succ
    # some more code
  end
end
a.succ # now this is valid

La sequenza di azioni sopra riportata non è possibile in un linguaggio come Java in cui tutti i tipi sono corretti in fase di esecuzione.


1

Invece di darti una definizione accademica, proverò a mostrarti alcune delle differenze usando un esempio del mondo reale usando VBA:

Legatura anticipata:

Dim x As FileSystemObject
Set x = New FileSystemObject
Debug.Print x.GetSpecialFolder(0)

Ciò richiede che un riferimento sia impostato sul componente "Microsoft Scripting Runtime" in fase di progettazione . Ha il vantaggio di ricevere un messaggio di errore già al momento della compilazione quando si ha un errore di battitura FileSystemObjecto nomi di metodi come GetSpecialFolder.

Rilegatura tardiva

Dim x As Object
Set x = CreateObject("Scripting.FileSystemObject")
Debug.Print x.GetSpecialFolder(0)

Ciò non richiede un riferimento da impostare in anticipo, la creazione dell'istanza e la determinazione del tipo avverranno solo in fase di esecuzione. Il compilatore non si lamenterà al momento della compilazione quando si tenta di chiamare un metodo inesistente di x, ciò comporterà un errore di runtime solo quando viene eseguita la riga specifica.

Quindi, lo svantaggio dell'associazione tardiva è che non hai nessun tipo di controllo forte qui. Ma questo è anche il vantaggio: supponiamo che tu abbia un componente in cui esistono diverse versioni e ogni versione più recente fornisce alcune funzioni aggiuntive. (Un esempio reale sono i componenti di MS Office, come l'interfaccia COM di Excel) L'associazione tardiva ti consente di scrivere codice che funziona insieme a tutte quelle versioni: puoi prima determinare la versione del componente specifico e se scopri di avere disponibile solo una versione precedente, evita di eseguire chiamate di funzioni che non funzionano con quella versione.


-2

Forse l'esempio più comune di associazione tardiva è la risoluzione degli URL di Internet. Supporta sistemi dinamici e sistemi di grandi dimensioni senza cercare di collegare e collegare tutti i siti del mondo prima che tu possa raggiungerne uno, ma d'altra parte comporta un certo sovraccarico (ricerca DNS, molto meno routing IP) in fase di esecuzione.

Alla luce di ciò, la maggior parte delle varietà di rilegatura negli ambienti linguistici è più o meno precoce, in fase di compilazione o di collegamento.

Ogni tipo ha costi e benefici.


Puoi inserire un riferimento per questa definizione di rilegatura? Non ho mai sentito parlare di risoluzione degli indirizzi Internet come "vincolante", sebbene poiché l'associazione sia l'atto di risolvere i nomi, suppongo che qualcuno abbia sostenuto che il concetto di associazione anticipata / tardiva possa essere applicato alla risoluzione dell'URI agli indirizzi Internet. Ma questa non è un'interpretazione comune e il concetto di associazione anticipata / tardiva precede il momento in cui i computer erano comunemente collegati a Internet.
Jay Elston,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.