strutture+function_handler+self=object orientation
Vediamo come questa cosa possa essere vera in VimScript: per prima cosa vi esorto a scaricare questo archivio con all'interno del codice VimScript, fra cui quello che ho scritto per l'OOP. In secundis aprite ed osservate il file "OO.vim" presente nella cartella "VimPresentation\Automation\Scripting". In esso:
- BaseClass: salva in una variabile globale la classe corrente, costruita come dizionario con l'entry speciale "__name" contenente i nomi delle classi da cui l'oggetto eredita (per ora solo la corrente) e l'array (ora vuoto) delle interfacce implementate. Successivamente lo restituisce.
- Interface: nefinisce una struttura simile a quella di una classe ma senza array delle interfacce.
- Hinerit: l'ereditatrietà è gestita all'interno del costruttore chiamando questa funzione, qui viene creata l'istanza della superclasse, viene poi aggiunto ai nomi delle classi da cui si eredita il nome della sottoclasse, sarà poi compito del costruttore terminare la propria opera aggingendo nuovi attributi e svolgendo altri compiti di inizializzazione.
- Virtual: i metodi virtuali non sono altro che metodi che vengono (nella sottoclasse, non nella superclasse come in C++) salvati all'interno della strututra d'istanza sotto nuovo nome tipo "superclasse_metodo" e che quindi vengono sostituiti nella sottoclasse.
- PureVirtual: un metodo virtuale puro invece è un metodo che non è ancora stato implementato, se ne genera quindi una versione dummy ( che genera solo un'eccezione) e si utilizza la stessa tecnica dei metodi virtuali.
- Implements: l'implementazione delle interfacce significa due cose:
- l'interfaccia (il suo nome) deve figurare tra le interfacce implementate,
- tutti i metodi virtuali puri presenti nell'interfaccia devono essere in quelche modo "importati" all'interno della classe corrente (starà poi al programmatore implementarli).
- Isa: usando i nomi da dizionario si può sapere se una istanza è (da qualche parte nell'albero d'ereditarietà) istanza di una determinata classe.
- IsImplemented: in modo analogo si può controllare se un istanza implementa una determinata interfaccia.
- Import: un po' di comodità non guasta! ;)
let s:_default = 42All'interno di uno script le variabili nello scope s: sono locali allo script stesso, per una classe questo significa avere a disposizione uno strumento per gli attributi statici.
function! Counter(...)Al solito definiamo una classe come funzione con funzioni innestate, la funzione di suo farà da costruttore. In fondo si può trovare il codice:
...
endfunction
function! Counter_compare(a,b)che definisce una funzione friend. Il costruttore svolge le seguenti azioni:
" Contronto actual:
return a:a._actual>a:b._actual
endfunction
if a:0<1che rappresentano:
let initial = s:_default
else
let initial = a:1
end
let counter = BaseClass('Counter') | Implements ICounter
call extend(counter,{'_actual': initial})
- inizializzazione dell'attributo "initial" come variabile (per ora) locale,
- creazione dell'istanza con implementazione dell'interfaccia ICounter,
- agginta dell'attributo privato "initial".
A questo punto vengono aggiunti i metodi (eventualmente dichiarati virtuali se devono sovrascrivere i metodi di una superclasse o di un'interfaccia):
Virtual actualE la classe è pronta! ;) Si osservi che i metodi vengono dichiarati con la speciale sintassi:
function! counter.actual() dict
return self._actual
endfunction
funciton! struttura.nome(...) dictdove la parola chiave "dict" assicura che all'interno del metodo si possa fare riferimento al dizionario "counter" con la parola chiave self. Si noti anche che il nome della funzione inizia con il nome dell'istanza che si sta costruento e che verrà restituita al termine del costruttore, infatti si sta semplicemente dichiarando un funciotn_handler di nome (in questo esempio) "actual" all'interno del dizionario "counter" richiedendo di generare il riferimento locale "self" con la parola chiave "dict".
...
endfunction
Nel file "StepCounter.vim" si può osservare che l'ereditarietà è ottenuta semplicemente sostituendo la chiamata a "BaseClass" con la chiamata a "Hinerit" passando anche il nome della superclasse e la lista degli argomenti.
Con la definizione dei comandi si è resa semplicissima la dichiarazione delle interfacce:
fun! ICounter()
let counter = Interface('ICounter')
Abstract actual next prev set
return counter
endfun
Infatti:
- Si crea l'interfaccia in una fuzione "costruttore",
- si crea "l'istanza" con "Interface",
- si creano i metodi virtuali puri col comando "Abstract",
- si restituisce l'interfaccia.