Lisp – oggetti – classi – 2

peano-gosperProcedo, continuando da qui a copiare qui.

Funzioni d’accesso

Credo si debba tradurre così “Accessor Functions”, se no cambierò.
Con make-instance e slot-value abbiamo tutti i tools necessari per creare e manipolare le istanze a una classe. Tutto quello che può servire d’altro si può implementare in termini di queste funzioni. Tuttavia si sa che nella programmazione orientata agli oggetti accedere direttamente agli slots (o field o variabili membro) di un oggetto genera codice fragile. Per esempio supponiamo di voler cambiare la definizione di bank-account in modo che invece di memorizzare balance come numero come una lista temporale di prelievi e depositi. Il codice che accede direttamente allo slot balance va in errore se si tenta di cambiare la definizione della classe per rimuovere lo slot o per per aggiungere la nuova lista nel vecchio slot. D’altra parte se si definisce una funzione, balance, che accede allo slot si può ridefinirlo dopo per preservarne il comportamento anche se la sua rappresentazione interna cambia e tutto il codice che usa questa funzione continua a funzionare senza bisogno di modifiche.

Un altro vantaggio di usare funzioni d’accesso invece dell’accesso diretto a slot-value è che queste permettono di limitare i modi con cui il codice può modificare lo slot. Può essere bello per gli utenti della classe bank-account di ottenere balance ma si può volere che tutte le sue modifiche passino attraverso depositi e prelievi. Se i clienti sanno che possono manipolare questi oggetti solo attraverso le API funzionali pubblicate si può fornire una funzione balance ma non renderla setf-abile se si vuole tenere read-only.

Infine usando funzioni d’accesso si rende il codice più chiaro perché questo evita usi di funzioni di slot-value verbose.

È immediato definire una funzione che legge il valore dello slot balance:

c17-12

Tuttavia se si vuole definire una subclasse di bank-account può essere un’idea di definire balance come funzione generica. In questo modo i possono fornire i diversi metodi in balance per quelle subclassi o estenderne le loro definizioni con metodi ausiliari. Quindi si può riscrivere così:

c17-13

Come detto non si vuole che l’utente possa accedere direttamente a cambiare balance ma per altri slot, come customer-name si può prevedere una unzione per settarlo. In questo caso il modo più chiaro è definire una funzione come una funzione setf.

Una funzione setf è un modo di estendere setf, definendo un nuovo tipo di posto settabile. Il nome di una funzione setf è una lista di due elementi in cui il primo è setf e il secondo è un simbolo, tipicamente il nome di una funzione usata per accedere al posto che la funzione setf setterà. Una funzione setf può prendere qualunque numero di argomenti ma il primo è sempre il valore da assegnare al posto. Si può per esempio definire una funzione setf per settare lo slot customer-name in bank-account così:

c17-14

Dopo la valutazione della definizione un’espressione come:

(setf (customer-name *account*) "Sally Sue") ==> "Sally Sue"

Nota: qui mi becco un errore; vedo di ricaricare tutto quello di ieri. OK.

c17-15

Niente di difficile per scrivere queste funzioni d’accesso ma non è The Lisp Way scriverle a mano. Pertanto defclass supporta tre opzioni di slot che permettono di crearle automaticamente le funzioni di reader e writer per uno specifico slot.

L’opzione :reader specifica il nome da usarsi per la funzione generica che accetta un oggetto come suo singolo argomento. Quando la defclass è valutata la funzione generica è creata se già non esiste. Viene poi aggiunto un metodo specializzante il suo argomento della nuova classe e restituito il valore dello slot alla funzione generica. Il nome può essere qualunque ma è di solito il nome dello slot stesso. Quindi invece di scrivere la funzione generica balance e i metodi visualizzati sopra si può cambiare lo specificatore slot nella definizione di bank-account così:

(balance
 :initarg :balance
 :initform 0
 :reader balance)

L’opzione :writer è usata per creare una funzione generica e un metodo per settare il valore di uno slot. Funzione e metodo seguono le specifiche di una funzione setf, prendendo il nuovo valore come primo argomento e ritornandolo come risultato cosicché si può definire una funzione come (setf customer-name). Per esempio si possono provvedere i metodi reader e writer simili a quelli appena scritti cambiando solo lo specificatore dello slot così:

(customer-name
 :initarg :customer-name
 :initform (error "Must supply a customer name.")
 :reader customer-name
 :writer (setf customer-name))

Siccome è comunissimo voler sia le funzioni reader e writer defclass fornisce l’opzione :accessor che crea le funzioni dette e la corrispondente setf. Quindi riscriviamo così:

(customer-name
 :initarg :customer-name
 :initform (error "Must supply a customer name.")
 :accessor customer-name)

Infine un’ultima opzione per lo slot è :documentation –devo dire a cosa serve?
Mettendo tutto assieme e aggiungendo un metodo reader per gli slots account-number e account-type la form defclass per la classe bank-account diventa:

c17-16

Pausaaaa 👿
Ma poi si continua :mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: