Cocoa e pattern di programmazione
Cocoa è un framework complesso. Nella sua progettazione ed implementazione sono utilizzate molteplici tecniche di progettazione ad oggetti.
Tra queste ricordiamo l’adozione dei seguenti pattern di programmazione:
- Modello-Vista-Controller (MVC)
- Delega
- Delega di data source
- Observer – KVC, KVO & binding
- Notifica
L’adozione di tali modelli di programmazione è praticamente obbligatoria: nella adozione più semplice (senza coinvolgere il progetto di nuove classi) si esplicita nell’individuare specifici oggetti (anche realizzati da utente) capaci di essere attori per uno di questi ruoli.
Cerchiamo di specificare brevemente.
Delega
Il processo di delega è un modo che una istanza di oggetto ha per “traslare” l’implementazione di un suo comportamento (metodo) con il metodo omonimo appartenente ad un altro oggetto (anche di tipo diverso).
Il processo di delega è una forma di composizione (composizione dei comportamenti) tra oggetti.
Una classe che prevede processo di delega per i suoi oggetti avrà un attributo indicante l’oggetto delegato (delegate). Per classi gestite con Interface Builder è sufficiente collegare l’attributo delegato di un oggetto ad un altro oggetto per stabilire la possibilità che il processo di delega abbia luogo.
Nel processo di delega, uno stesso oggetto delegato può essere delegato ad implementare più di un compito (metodo).
Vediamo come esempio quali metodi vengono delegati dalla classe NSTableView
ad un suo oggetto delegato:
tableView:willDisplayCell:forTableColumn:row:
tableView:shouldSelectRow:
tableView:shouldSelectTableColumn:
selectionShouldChangeInTableView:
tableView:shouldEditTableColumn:row:
Modello-Vista-Controllo (MVC – Model/View/Controller)
Pattern di programmazione storico introdotto da SmallTalk e comune a tutti gli approcci ad oggetti alle interfacce grafiche (Java compreso).
Si fonda sulla separazione logica ed implementativa della rappresentazione dei dati dell’applicazione (modello), della componente visuale di interfaccia utente (vista) e della componente che coordina i due e costituisca tutto quanto è eventualmente procedurale nella applicazione (controller).
Una applicazione Cocoa è fortemente conforme al pattern MVC.
Non a caso il framework introduce classi e derivazioni da queste per rappresentare una implementazione delle componenti conforme al modello, con forme e comportamenti prestabiliti a governare le più disparate esigenze.
Per Vista e Controllo abbiamo:
NSView
NSController
Per il modello si possono usare tutte le classi conforme al modello “container” o “collection” quali:
NSArray
NSSet
oppure la classe NSManagedObject
con l’adozione del framework Core Data (>= 10.4).
Composizione
La composizione è una tecnica di ADT (Abstract Type Definition) per la composizione delle informazioni degli oggetti. In progettazione object-oriented viene indicata come avente maggiori vantaggi dell’ereditarietà (si prefersce in sostanza descrivere i problemi con relazione ha-un invece che è-un, per ridurre la complessità della gerarchia di derivazione o eventuali effetti collaterali).
Questa filosofia è adottata anche da Cocoa.
Alcune istanze di classe dell’ApplicationKit esistono solo come elementi parte di classi più grandi come “aiuto all’implementazione”.
Un esempio classico è la NSTextFieldCell
, che incapsulata in unica istanza in una NSTableView
costituisce l’elemento che “disegna” tutte le celle della tabella, riutilizzata più volte dall’algoritmo di disegno della classe NSTableView
.
In questo caso si ha anche una scelta architetturale particolare per cui non si incapsulano n istanze di celle per quante sono gli elementi della tabella (aumento di complessità), ma una sola istanza che non è un vero oggetto grafico (non deriva da NSView
), ma è uno strumento che implementa il comportamento grafico del “disegnatore”.
Protocolli
Un protocollo è un modo alternativo alla derivazione per rendere possibile l’intoperabilità di classi molto differenti che devono rispondere ai medesimi messaggi.
L’insieme dei messaggi in comune, invece di essere una interfaccia, diviene un protocollo.
Un protocollo diviene così una interfaccia che qualsiasi classe può adottare in maniera indipendente dal progetto.
Esistono due tipi di protocolli in objc: formali ed informali.
Un protocollo informale è sostanzialmente una categoria di NSObject
. Contrariamente ad una interfaccia, un protocollo informale non è da implementarsi obbligatoriamente. Prima che sia invocato un metodo, l’oggetto chiamante controlla se l’oggetto destinatario del messaggio implementa il metodo. Dato che i metodi opzionali di un protocollo sono stati introdotti solo nel Objective-C 2.0, i protocolli informali sono stati essenziali nel modo in cui le classi di Foundation e AppKit hanno implementato la delega..
NSObject
protocols:
autorelease
class
conformsToProtocol
:description
hash
isEqual:
isKindOfClass:
isMemberOfClass:
isProxy
performSelector:
performSelector:withObject:
performSelector:withObject:withObject:
release
respondsToSelector:
retain
retainCount
self
superclass
zone
Un protocollo formale dichiara una lista di metodi che ci si aspetta utilizzata da una classe utente. Hanno una propria sintassi per dichiarazione, adozione e controllo di tipo.
Esempio particolare di protocollo: NSTableViewDataSource
Il metodo setDataSource:
di una NSTableView
vuole un oggetto conforme ad protocollo informale NSTableVewDataSource:
- (void)setDataSource:(id<NSTableViewDataSource>)anObject
L’oggetto che viene impostato come “sorgente dati” sarà in realtà un oggetto sottoposto a processo di delega che risponde al protocollo informale richiesto.
In particolare delega i seguenti compiti (che sono dettati dal protocollo):
Delegate Message | Significato |
numberOfRowsInTableView: | Lettura valori |
tableView:objectValueForTableColumn:row: | Lettura valori |
tableView:setObjectValue:forTableColumn:row: | Impostazione valori |
tableView:acceptDrop:row:dropOperation: | Dragging |
tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes: | Dragging |
tableView:validateDrop:proposedRow:proposedDropOperation: | Dragging |
tableView:writeRowsWithIndexes:toPasteboard: | Dragging |
tableView:sortDescriptorsDidChange: | Ordinamento |
Osservatori e Notifiche
L’Observer pattern è un design pattern utilizzato per tenere sotto controllo lo stato di diversi oggetti.
Observer
Un observer definisce una dipendenza uno-a-molti tra oggetti al fine di notificare e aggiornare automaticamente tutti gli oggetti dipendenti al cambiamento dello stato dell’oggetto osservato. Il pattern Observer è essenzialmente un modello “pubblica e sottoscrivi” in cui un soggetto e i suoi osservatori sono non strettamente accoppiati. La comunicazione può avvenire tra gli osservatori e gli osservati senza che l’uno sappia molto dell’altro.
Notifications
Il meccanismo di notifica in Cocoa implementa un sistema diffusione messaggi uno-a-molti in accoro con il Observer pattern. Gli oggetti di un programma aggiungono se stessi o altri oggetti ad una lista di osservatori per una o più notifiche, ciascuna delle quali viene identificata da una stringa (nome della notifica).
Gli oggetti delegati vengono automaticamente registrati per ricevere messaggi corrispondenti alle notifiche.
Esempio: delegato di una NSTableView
. Questi messaggi informano il delegato quando la selezione è stata modificata o quando una colonna viene mossa o ridimensinoata:
Delegate Message | Notification |
tableViewColumnDidMove: |
NSTableViewColumnDidMoveNotification |
tableViewColumnDidResize: |
NSTableViewColumnDidResizeNotification |
tableViewSelectionDidChange: |
NSTableViewSelectionDidChangeNotification |
tableViewSelectionIsChanging: |
NSTableViewSelectionIsChangingNotification |
Continua