Domanda

Dopo aver letto molti blog, voci del forum e diversi documenti Apple, non so ancora se la sottoclasse estesa in Objective-C sia una cosa saggia da fare o meno .

Prendi ad esempio il seguente caso:

  

Di 'che sto sviluppando un puzzle game che   ha molti elementi. Tutti quelli   gli elementi condividono una certa quantità di   stesso comportamento. Quindi, nel mio   raccolta di elementi, diversi   gruppi di elementi condividono uguali   comportamento, distinguendo i gruppi da   gruppi, ecc ...

     

Quindi, dopo aver determinato ciò che eredita   da cosa, ho deciso di sottoclassare   dell'oblio. E perché non dovrei?   Considerando la facilità di messa a punto generale   il comportamento prende con questo modello, I   penso di aver realizzato qualcosa che OOP è   pensato per.

Ma, e questa è la fonte della mia domanda, Apple menziona l'uso di delegati, metodi di origine dei dati e protocolli informali a favore della sottoclasse . Mi sorprende davvero perché?

Sembra che ci siano due campi. Quelli a favore della sottoclasse, quelli a cui non piace. Apparentemente dipende dal gusto personale. Mi chiedo quali sono i pro e i contro della sottoclasse in modo massiccio e non della sottoclasse in modo massiccio ?

Per concludere, la mia domanda è semplice: Ho ragione? E perché o perché no?

È stato utile?

Soluzione

Personalmente, seguo questa regola: posso creare una sottoclasse se rispetta il Principio di sostituzione di Liskov .

Altri suggerimenti

La delega è un mezzo per utilizzare la tecnica di composizione per sostituire alcuni aspetti della codifica per la quale sottoclassi altrimenti. In quanto tale, si riduce alla secolare domanda del compito a portata di mano che necessita di una grande cosa che sa fare molto, o se si dispone di una rete libera di oggetti specializzati (una sorta di modello di responsabilità UNIX).

L'uso di una combinazione di delegati e protocolli (per definire ciò che i delegati dovrebbero essere in grado di fare) fornisce una grande flessibilità di comportamento e facilità di codifica - tornando a quel principio di sostituzione di Liskov, quando si esegue una sottoclasse per fare attenzione, non fare nulla che un utente dell'intera classe troverebbe inaspettato. Ma se stai semplicemente creando un oggetto delegato, allora hai molto meno di cui essere responsabile, solo che i metodi delegati che implementi fanno ciò che richiede un protocollo, oltre a ciò che non ti interessa.

Ci sono ancora molti buoni motivi per usare le sottoclassi, se si hanno veramente comportamenti e variabili condivise tra un numero di classi, può avere molto senso sottoclasse. Ma se puoi trarre vantaggio dal concetto di delegato, spesso renderai le tue lezioni più facili da estendere o utilizzare in modi che il progettista non si sarebbe aspettato.

Tendo ad essere più un fan dei protocolli formali che di quelli informali, perché non solo i protocolli formali assicurano di avere i metodi che una classe ti tratta come un delegato, ma anche perché la definizione del protocollo è un luogo naturale in cui documentare ciò che ci si aspetta da un delegato che implementa tali metodi.

La sottoclasse ha i suoi vantaggi, ma presenta anche alcuni svantaggi. Come regola generale, cerco di evitare l'ereditarietà dell'implementazione e invece di utilizzare l'ereditarietà e la delega dell'interfaccia.

Uno dei motivi per cui lo faccio è perché quando erediti l'implementazione, puoi finire con i problemi se ignori i metodi ma non aderisci al loro contratto (a volte non documentato). Inoltre, trovo che le gerarchie di classi ambulanti con ereditarietà dell'implementazione siano difficili perché i metodi possono essere sovrascritti o implementati a qualsiasi livello. Infine, quando si esegue la sottoclasse è possibile solo ampliare un'interfaccia, non è possibile restringerla. Questo porta a astrazioni che perdono. Un buon esempio di ciò è java.util.Stack che estende java.util.Vector. Non dovrei essere in grado di trattare una pila come un vettore. In questo modo il consumatore può solo correre attorno all'interfaccia.

Altri hanno menzionato il principio di sostituzione di Liskov. Penso che l'utilizzo di questo avrebbe sicuramente chiarito il problema java.util.Stack ma può anche portare a gerarchie di classi molto profonde al fine di garantire che le classi ottengano solo i metodi che dovrebbero avere.

Invece, con l'ereditarietà dell'interfaccia non esiste essenzialmente una gerarchia di classi perché le interfacce raramente devono estendersi a vicenda. Le classi implementano semplicemente le interfacce di cui hanno bisogno e possono quindi essere trattate nel modo corretto dal consumatore. Inoltre, poiché non esiste alcuna eredità di implementazione, i consumatori di queste classi non dedurranno il loro comportamento a causa della precedente esperienza con una classe genitore.

Alla fine, però, non importa in che modo vai. Entrambi sono perfettamente accettabili. È davvero più una questione di cosa ti senti più a tuo agio e di cosa incoraggiano i framework con cui stai lavorando. Come dice il vecchio proverbio: & Quot; Quando a Roma fai come fanno i romani. & Quot;

Non c'è niente di sbagliato nell'usare l'ereditarietà in Objective-C. Apple lo usa abbastanza. Ad esempio, in Cocoa-touch, l'albero delle eredità di UIButton è UIControl: UIView: UIResponder: NSObject.

Penso che Martin abbia colpito un punto importante menzionando il principio di sostituzione di Liskov. Inoltre, l'uso corretto dell'ereditarietà richiede che l'implementatore della sottoclasse abbia una profonda conoscenza della superclasse. Se hai mai lottato per estendere una classe non banale in un quadro complesso, sai che c'è sempre una curva di apprendimento. Inoltre, i dettagli di implementazione della super classe spesso & Quot; sfogliare & Quot; alla sottoclasse, che è un grosso problema nell'amplificatore @ $ &; per i costruttori di framework.

Apple ha scelto di utilizzare la delega in molti casi per risolvere questi problemi; classi non banali come UIApplication espongono punti di estensione comuni attraverso un oggetto delegato, quindi la maggior parte degli sviluppatori ha sia una curva di apprendimento più semplice sia un modo più liberamente accoppiato per aggiungere un comportamento specifico dell'applicazione - raramente è necessario estendere direttamente UIApplication.

Nel tuo caso, per il codice specifico dell'applicazione, utilizza le tecniche che preferisci e lavora meglio per il tuo design. L'ereditarietà è un ottimo strumento se utilizzata in modo appropriato.

Vedo spesso che i programmatori di applicazioni traggono lezioni dai progetti di framework e provano ad applicarli al loro codice di applicazione (questo è comune nei mondi Java, C ++ e Python e Objective-C). Sebbene sia utile riflettere e comprendere le scelte fatte dai progettisti del framework, queste lezioni non si applicano sempre al codice dell'applicazione.

In generale dovresti evitare di sottoclassare le classi API se esistono delegati, ecc. che realizzano ciò che vuoi fare. Nel tuo codice la sottoclasse è spesso più gradevole, ma dipende davvero dai tuoi obiettivi, ad es. se stai fornendo un'API, devi fornire un'API basata su delegati anziché assumere la sottoclasse.

Quando si ha a che fare con la sottoclasse di API presenta più potenziali bug, ad es. se una classe nella gerarchia di classi ottiene un nuovo metodo che ha lo stesso nome della tua aggiunta, fai delle interruzioni. Inoltre, se stai fornendo una funzione di tipo utile / di supporto, c'è la possibilità che in futuro qualcosa di simile verrà aggiunto alla classe reale che stavi eseguendo la sottoclasse e che potrebbe essere più efficiente, ecc. Ma il tuo override lo nasconderà.

Si prega di leggere la documentazione Apple quot; Aggiunta di comportamento a un programma Cocoa " !. Nella sezione &; Ereditando da una classe Cocoa & Quot; , vedi il secondo paragrafo. Apple afferma chiaramente che la sottoclasse è il modo principale per aggiungere un comportamento specifico dell'applicazione al framework (si prega di notare QUADRO ).
Il modello MVC non impedisce completamente l'uso di sottoclassi o sottotipi. Almeno non ho visto questa raccomandazione né da Apple né da altri (se ho perso, non esitate a indicarmi la giusta fonte di informazioni su questo). Se stai eseguendo la sottoclasse delle classi api solo all'interno della tua applicazione, vai avanti, nessuno ti blocca ma fai attenzione a non interrompere il comportamento della classe / api nel suo insieme. La sottoclasse è un ottimo modo per estendere la funzionalità di api del framework. Vediamo molte sottoclassi anche nelle API del framework Apple IOS. Come sviluppatore bisogna fare attenzione che l'implementazione sia ben documentata e non duplicata accidentalmente da un altro sviluppatore. È un altro gioco con la palla se l'applicazione è un insieme di classi API che prevedi di distribuire come componente riutilizzabile.

IMHO, piuttosto che chiedere quale sia la migliore pratica, prima leggi attentamente la relativa documentazione, implementala e testala. Prendi il tuo giudizio. Sai meglio di cosa stai facendo. È facile per gli altri (come me e tanti altri) leggere cose da diverse fonti sulla Rete e gettare parole. Sii il tuo giudice, ha funzionato per me finora.

Penso davvero che dipenda da cosa stai cercando di fare. Se il gioco puzzle che descrivi nell'esempio ha davvero un insieme di elementi unici che condividono attributi comuni e non ci sono classi fornite, ad esempio & Quot; NSPuzzlePiece & Quot; - che si adattano alle tue esigenze, quindi non vedo alcun problema con la sottoclasse ampiamente.

Nella mia esperienza, i delegati, i metodi di origine dei dati e i protocolli informali sono molto più utili quando Apple ha fornito una classe che già fa qualcosa di simile a ciò che vuoi che faccia.

Ad esempio, supponi di creare un'app che utilizza una tabella. Esiste (e parlo qui dell'SDK di iPhone, poiché è lì che ho esperienza) un UITableView di classe che fa tutte le piccole cose della creazione di una tabella per l'interazione con l'utente ed è molto più efficiente definire un'origine dati per un istanza di UITableView rispetto alla sottoclasse completa di UITableView e ridefinire o estendere i suoi metodi per personalizzare il suo comportamento.

Concetti simili valgono per delegati e protocolli. Se riesci ad adattare le tue idee alle classi di Apple, di solito è più facile (e funzionerà in modo più fluido) farlo e utilizzare l'origine dati, i delegati e i protocolli piuttosto che creare le tue sottoclassi. Ti aiuta a evitare ulteriore lavoro e perdita di tempo ed è generalmente meno soggetto a errori. Le lezioni di Apple si sono occupate del business di rendere efficienti le funzioni e il debug; più puoi lavorare con loro, meno errori avrà il tuo programma a lungo termine.

la mia impressione dell'enfasi 'contro' dell'ADC sulla sottoclasse ha più a che fare con l'eredità di come si è evoluto il sistema operativo ... indietro nel tempo (Mac Classic aka os9) quando c ++ era l'interfaccia principale per la maggior parte dei mac toolbox, la sottoclasse era lo standard di fatto per consentire a un programmatore di modificare il comportamento delle normali funzionalità del sistema operativo (e questo a volte era davvero un dolore al collo e significava che bisognava fare molta attenzione che qualsiasi modifica si comportasse in modo prevedibile e non ha infranto alcun comportamento standard).

detto questo, LA MIA IMPRESSIONE dell'enfasi di ADC sulla sottoclasse è non sollevando un caso per la progettazione della gerarchia di classi di un'applicazione senza eredità, MA INSTEAD per sottolineare che in il nuovo modo di fare le cose (ad es. OSX) ci sono nella maggior parte dei casi mezzi più appropriati per personalizzare il comportamento standard senza la sottoclasse.

Quindi, sicuramente, progetta l'architettura del tuo programma di puzzle nel modo più robusto possibile, sfruttando l'eredità come ritieni opportuno!

non vedo l'ora di vedere la tua nuova fantastica applicazione puzzle!

| K lt &;

Apple sembra effettivamente scoraggiare passivamente la sottoclasse con Objective-C.

È un assioma del design OOP per favorire la composizione rispetto all'implementazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top