it-swarm.it

Vantaggi della programmazione orientata agli oggetti

Nota : questa domanda è un estratto modificato di un post sul blog che ho scritto qualche mese fa. Dopo aver inserito un link al blog in un commento su Programmers.SE qualcuno mi ha chiesto di pubblicare una domanda qui in modo da poter rispondere. Questo post è il mio più popolare, in quanto le persone sembrano digitare "Non ottengo una programmazione orientata agli oggetti" in Google molto. Sentiti libero di rispondere qui o in un commento su Wordpress.

Cos'è la programmazione orientata agli oggetti? Nessuno mi ha dato una risposta soddisfacente. Sento che non otterrai una buona definizione da qualcuno che va in giro dicendo "oggetto" e "orientato agli oggetti" con il naso in aria. Né otterrai una buona definizione da qualcuno che non ha fatto altro che programmare orientato agli oggetti. Nessuno che capisca sia la programmazione procedurale che quella orientata agli oggetti mi ha mai dato un'idea coerente di ciò che fa effettivamente un programma orientato agli oggetti.

Qualcuno può darmi le proprie idee sui vantaggi della programmazione orientata agli oggetti?

35
Joel J. Adamson

Pensa al software come a una macchina o una catena di montaggio esistente all'interno del computer. Alcune materie prime e componenti vengono immessi nella macchina e segue una serie di procedure per trasformarli in alcuni prodotti finali. Le procedure sono impostate per eseguire un'operazione specifica su alcune materie prime o componenti su una serie specifica di parametri (ad es. Tempo, temperatura, distanza, ecc.) In un ordine particolare. Se i dettagli dell'operazione da eseguire non fossero corretti, o i sensori della macchina non fossero calibrati correttamente, o se alcune materie prime o componenti non fossero conformi agli standard di qualità previsti, ciò potrebbe modificare il risultato dell'operazione e il prodotto non si rivelerebbe come previsto.

Tale macchina è molto rigida nel suo funzionamento e input accettabili. Le macchine non mettono in discussione l'intelligenza dei progettisti né il suo attuale ambiente operativo. Continuerà a seguire le procedure finché sarà diretto. Anche se un cambiamento nelle materie prime o nei componenti potrebbe avere un effetto drammatico su ciò che è accaduto nelle operazioni successive, la macchina eseguirà comunque le sue procedure. Il processo dovrebbe essere rivisto per vedere quali modifiche alle procedure erano necessarie per compensare e produrre il risultato desiderato. Una modifica al design o alla configurazione del prodotto potrebbe anche richiedere una modifica significativa delle operazioni eseguite o del loro ordine. Sebbene i responsabili della produzione abbiano rapidamente imparato l'importanza di isolare il più possibile le operazioni per ridurre gli effetti indesiderati tra loro, vengono fatte molte ipotesi sulle condizioni in cui si trovano i componenti mentre vengono sottoposti alla lavorazione; ipotesi che potrebbero non essere rilevate fino a quando il prodotto finale non sarà nelle mani dell'utente in un ambiente operativo diverso.

Ecco com'è la programmazione procedurale.

Ciò che l'orientamento agli oggetti fornisce è un modo per rimuovere i presupposti della condizione dei componenti; quindi, le operazioni da eseguire su quel componente e come integrarlo nel prodotto finale. In altre parole, OOP è come prendere i dettagli del processo per gestire un particolare componente e consegnarlo a una macchina più piccola da fare. La macchina più grande responsabile del processo dice alla macchina specifica del componente quale operazione che prevede di essere eseguita, ma lascia i dettagli che i passaggi devono essere gestiti dalla macchina specifica del componente.

Per quanto riguarda i vantaggi dell'orientamento agli oggetti rispetto al software non orientato agli oggetti:

  • comportamento specifico del componente - fare dettagli su come gestire un particolare componente la responsabilità della macchina specifica del componente più piccolo assicura ogni volta che quel componente viene gestito, la sua macchina lo farà in modo appropriato;
  • espressioni polimorfiche - poiché macchine specifiche per componente eseguono operazioni su misura per il suo particolare componente, lo stesso messaggio inviato a macchine diverse può agire diversamente;
  • tipo di astrazione - spesso ha senso che diversi tipi di componenti utilizzino lo stesso vocabolario per le operazioni eseguite dalle loro macchine;
  • separazione delle preoccupazioni - lasciare dettagli specifici dei componenti alle loro macchine significa che la macchina di processo deve solo gestire le preoccupazioni più generali e più grandi del suo processo e i dati necessari per gestirlo; inoltre, è meno probabile che siano influenzati da cambiamenti in altri componenti;
  • adattabilità - i componenti che si concentrano sulla propria area di specialità possono essere adattati a usi imprevisti semplicemente cambiando i componenti che utilizza o rendendoli disponibili per un'altra macchina di processo;
  • riutilizzo del codice - i componenti con un focus ristretto e una maggiore adattabilità possono sfruttare i loro costi di sviluppo essendo utilizzati più spesso.
7
Huperniketes

Dal tuo blog, sembra che tu abbia familiarità con la programmazione sia imperativa che funzionale e che tu abbia familiarità con i concetti di base coinvolti nella programmazione orientata agli oggetti, ma non hai mai avuto davvero "clic" su ciò che lo rende utile. Cercherò di spiegare in termini di tale conoscenza e spero che ti sia utile.

Alla base, OOP è un modo per utilizzare il paradigma imperativo per gestire meglio alti livelli di complessità creando strutture di dati "intelligenti" che modellano il dominio problematico. In un (oggetto non procedurale standard orientato), hai due cose fondamentali: variabili e codice che sa cosa farne. Il codice prende l'input dall'utente e varie altre fonti, lo memorizza in variabili, lo opera e produce dati di output che va all'utente o in varie altre posizioni.

La programmazione orientata agli oggetti è un modo per semplificare il programma prendendo quel modello di base e ripetendolo su scala ridotta. Proprio come un programma è una grande raccolta di dati con codice che sa cosa farne, ogni oggetto è un piccolo pezzo di dati associato a codice che sa cosa farne.

Suddividendo il dominio problematico in parti più piccole e assicurando che quanti più dati possibili siano associati direttamente al codice che sa cosa farne, si rende molto più facile ragionare sul processo nel suo insieme e anche sul sottotitolo problemi che compongono il processo.

Raggruppando i dati in classi di oggetti, è possibile centralizzare il codice relativo a tali dati, semplificando la ricerca e il debug del codice rilevante. E incapsulando i dati dietro gli identificatori di accesso e accedendoli solo attraverso metodi (o proprietà, se la tua lingua li supporta), riduci notevolmente il potenziale di corruzione dei dati o la violazione degli invarianti.

E utilizzando l'ereditarietà e il polimorfismo, è possibile riutilizzare le classi preesistenti, personalizzandole in base alle proprie esigenze specifiche, senza dover modificare gli originali o riscrivere tutto da zero. (Che è un cosa che non dovresti mai fare , se puoi evitarlo. Fai solo attenzione a capire il tuo oggetto base, o potresti finire con canguri assassini .

Per me, questi sono i principi fondamentali della programmazione orientata agli oggetti: gestione della complessità, centralizzazione del codice e migliore modellizzazione del dominio dei problemi attraverso la creazione di classi di oggetti, eredità e polimorfismo e maggiore sicurezza senza sacrificare il potere o il controllo attraverso l'uso dell'incapsulamento e proprietà. Spero che questo ti aiuti a capire perché così tanti programmatori lo trovano utile.

EDIT: in risposta alla domanda di Joel nei commenti,

Puoi spiegare cosa contiene un "programma orientato agli oggetti" (oltre a queste fantasiose definizioni che hai delineato) che è fondamentalmente diverso da un programma imperativo? Come "fai rotolare la palla?"

Un piccolo disclaimer qui. Il mio modello di "un programma orientato agli oggetti" è fondamentalmente il modello Delphi, che è molto simile al modello C # /. NET da quando sono stati creati da ex membri del team Delphi. Quello che sto dicendo qui potrebbe non essere applicabile, o non applicarlo altrettanto, in altre lingue OO.

Un programma orientato agli oggetti è uno in cui tutta la logica è strutturata attorno agli oggetti. Naturalmente questo deve essere avviato da qualche parte. Il tipico programma Delphi contiene un codice di inizializzazione che crea un oggetto singleton chiamato Application. All'inizio del programma, chiama Application.Initialize, quindi una chiamata a Application.CreateForm per ogni modulo che desideri caricare in memoria dall'inizio, quindi Application.Run, che visualizza il modulo principale sullo schermo e avvia il ciclo di input/eventi che costituisce il nucleo di tutti i programmi per computer interattivi.

L'applicazione e i moduli eseguono il polling per gli eventi in arrivo dal sistema operativo e li traducono in chiamate di metodo sull'oggetto. Una cosa molto comune è l'uso di gestori di eventi, o "delegati" in .NET-speak. Un oggetto ha un metodo che dice "fai X e Y, ma controlla anche se questo particolare gestore di eventi è assegnato e chiamalo se lo è". Un gestore di eventi è un puntatore a metodo - una chiusura molto semplice che contiene un riferimento al metodo e un riferimento all'istanza dell'oggetto - utilizzato per estendere il comportamento degli oggetti. Ad esempio, se ho un oggetto pulsante nel mio modulo, personalizzo il suo comportamento collegando un gestore eventi OnClick, che fa sì che un altro oggetto esegua un metodo quando si fa clic sul pulsante.

Quindi in un programma orientato agli oggetti, la maggior parte del lavoro viene eseguita definendo gli oggetti con determinate responsabilità e collegandoli insieme, tramite puntatori di metodo o mediante un oggetto che chiama direttamente un metodo definito nell'interfaccia pubblica di un altro oggetto. (E ora siamo tornati all'incapsulamento.) Questa è un'idea che non avevo idea di tornare prima di prendere le lezioni di OOP al college.

46
Mason Wheeler

Penso OOP è fondamentalmente solo un nome dato a qualcosa che potresti essere stato tentato di fare lungo la strada, come lo ero io.

Molto tempo fa, quando ero un piccolo programmatore, anche a Fortran, c'era qualcosa come un puntatore a una subroutine. È davvero utile poter passare un puntatore a una subroutine come argomento a un'altra subroutine.

Quindi la prossima cosa che sarebbe davvero utile sarebbe quella di memorizzare un puntatore a una subroutine all'interno di un record di una struttura di dati. In questo modo, potresti dire che il record "sa" come eseguire operazioni su se stesso.

Non sono sicuro che lo abbiano mai incorporato in Fortran, ma è facile farlo in C e nei suoi discendenti.

Quindi, sotto, è un'idea semplice e utile che potresti essere stato tentato di fare te stesso, ed è più facile da fare nelle lingue più recenti, anche se alcune persone lo hanno trasformato in un gigantesco carrozzone pieno di parole d'ordine spaventose.

6
Mike Dunlavey

Esistono vari tipi di OO, ed è difficile ottenere una definizione su cui tutti saranno d'accordo. Piuttosto che provare a mostrare come Java OO è simile al comune LISP Object System, inizierò con qualcosa di più convenzionale, passo dopo passo.

Supponiamo di avere molti oggetti esistenti come dati sparsi. I punti, ad esempio, potrebbero essere elementi in una matrice X, una Y e una Z. Per considerare un punto in sé, ha senso riunire tutti i dati in qualcosa come un C struct.

Ora, per qualsiasi oggetto dati, abbiamo i dati tutti insieme. Tuttavia, in un programma procedurale, il codice è sparso. Supponiamo di avere a che fare con forme geometriche. C'è una grande funzione per disegnare forme, e deve conoscere tutte le forme. C'è una grande funzione per trovare l'area e un'altra per il perimetro. Il codice per un cerchio è sparso attraverso più funzioni e per aggiungere un altro tipo di forma dobbiamo sapere quali funzioni cambiare. In un sistema orientato agli oggetti, raggruppiamo le funzioni nello stesso tipo di cose (class) dei dati. Pertanto, se vogliamo esaminare tutto il codice circolare, è presente nella definizione Circle, e se vogliamo aggiungere un Quartercircle scriviamo semplicemente la sua classe e abbiamo il codice .

Un vantaggio collaterale è che possiamo mantenere gli invarianti di classe, cose vere su ogni membro della classe. Limitando il codice al di fuori della classe dal pasticciare direttamente con i membri dei dati della classe, abbiamo tutto il codice che può cambiare i dati della classe in un posto e possiamo confermare che non fa nulla di strano (come avere un triangolo con una gamba) più lungo degli altri due combinati). Ciò significa che possiamo contare su alcune proprietà di ogni membro della classe e non dobbiamo verificare se un oggetto è sano ogni volta che lo usiamo.

Il vantaggio principale deriva dall'ereditarietà e dal polimorfismo. Definendo tutte queste varie forme come sottoclassi di una classe chiamata Shape, possiamo far manipolare il nostro codice Shapes, ed è compito dei sottooggetti della forma fare tutto ciò che viene richiesto dalle manipolazioni . Ciò significa che non è necessario toccare il vecchio codice testato quando si aggiungono nuove forme o si perfeziona il comportamento di quelli più vecchi. Abbiamo automaticamente un vecchio codice che può sfruttare direttamente il nuovo codice. Invece di rendere il codice di controllo consapevole di tutte le diverse forme possibili e di dover mantenere funzioni che sono consapevoli di tutte le diverse forme possibili, ci limitiamo a gestire le forme e le loro proprietà, mantenendo le sottoclassi Shape. Questo semplifica il codice di controllo.

Abbiamo diversi vantaggi qui. Dato che abbiamo invarianti di classe, possiamo ragionare su oggetti di dati più grandi nello stesso modo in cui ragioniamo sui tipi predefiniti, il che significa che spesso possiamo dividere concetti complessi in più semplici. Poiché il codice circolare è ampiamente contenuto in Circle, abbiamo aumentato la località. Poiché non esistono concetti di un cerchio sparso attraverso diverse funzioni diverse in luoghi diversi, otteniamo meno accoppiamenti tra le routine e non dobbiamo preoccuparci di mantenerli sincronizzati. Poiché le classi sono, in effetti, tipi, possiamo sfruttare il sistema di tipi esistente per catturare un uso incompatibile delle nostre classi.

5
David Thornley

OO ha molte definizioni diverse, sì. Sono sicuro che puoi trovarne molti da solo. Personalmente mi piace Rees Re: OO come un modo per dargli un senso. Immagino tu l'abbia letto da quando hai citato Paul Graham. (Lo consiglio a chiunque sia interessato a OO.) Adotterò più o meno la Java definizione qui {1,2,3,7,8,9}.

La domanda sull'utilità di OO, in particolare il modo in cui mi avvicino, merita una risposta molto più ampia con poche migliaia di righe di codice (in parte per non essere solo un mucchio di asserzioni). Tuttavia, ecco un riassunto di quell'ipotetico documento.

Non credo OO è terribilmente utile su piccola scala, diciamo, a poche centinaia di righe. In particolare, OO le lingue senza buone influenze funzionali tendono per rendere veramente doloroso eseguire operazioni semplici con qualsiasi tipo di raccolta o qualsiasi cosa che richieda molti tipi di dati. È qui che entrano in gioco la maggior parte dei modelli di progettazione; sono cerotti sulla bassa potenza del sottostante lingua .

A circa mille righe, inizia a essere più difficile tenere traccia di tutte le operazioni e strutture di dati e di come si relazionano. Aiuta, a questo punto, avere un modo per organizzare esplicitamente le strutture e le operazioni dei dati, disegnare i confini del modulo e definire le responsabilità e avere un modo conveniente per comprendere quelle definizioni mentre si sta tentando di programmare contro di esse.

Java-ish OO è una soluzione a metà strada per questi problemi che sembra aver vinto il concorso di popolarità. Perché è lo stesso meccanismo che Java persone si applicano al piccolo scala i problemi creati da un linguaggio poco potenziato, tende a cominciare a sembrare più una soluzione magica a tutto che un semplice modo di organizzarsi. Le persone che hanno familiarità con la programmazione funzionale tendono a preferire altre soluzioni, come le classi di tipo CLOS o Haskell, o la metaprogrammazione dei modelli quando bloccato in C++, oppure (come me, che lavora quotidianamente in C #) usa OO ma semplicemente non ti ecciti tanto.

3
Jesse Millikan

OOP = strutture dati + passaggio messaggi + ereditarietà, tutte evoluzioni logiche nei modelli di programmazione.

OOP può essere compreso (dai programmatori) in circa 90 secondi (vedi il mio profilo per un link). I concetti sono molto semplici.

Come applicarlo è un'altra questione. Solo perché sai come far oscillare un martello non significa che sai come progettare e costruire una casa. ;-)

1
Steven A. Lowe

OOP tenta di modellare i concetti del mondo reale in termini di oggetti e interazioni tra di essi. Come umani, tendiamo a elaborare il mondo in termini di oggetti. Il mondo è pieno di oggetti che hanno determinate proprietà e possono fare cose come interagire con altri oggetti. OOP consente di modellare il mondo in termini simili. Ad esempio,

  • La persona è un oggetto. Una persona ha alcune proprietà, come l'età e il sesso. Una persona può fare cose: mangiare, dormire, guidare una macchina.
  • Anche l'automobile è un oggetto (sebbene di diverso tipo). Ha anche proprietà come marca, modello e anno. Un'auto può fare le cose: muoversi.

Ma un'auto non può muoversi da sola, ha bisogno di una persona per guidarla - interazione tra Oggetti.

1
ysolik

Dato che capisci le strutture e comprendi i puntatori a funzione e capisci le strutture con i puntatori a funzione, dal tuo punto di vista definirei la programmazione orientata agli oggetti semplicemente come "programmazione, con un uso intenso di strutture che hanno puntatori a funzione". Sta ancora programmando in senso tradizionale: sono tutti i dati e il codice che agisce sui dati. La differenza è semplicemente come sono definite tutte queste informazioni e come ti approcci per definirle.

Forse una semplificazione eccessiva è che la programmazione tradizionale è "codice, con alcune strutture di dati", e la programmazione orientata agli oggetti è "strutture di dati, con un po 'di codice". Entrambi hanno ancora strutture di dati ed entrambi hanno ancora codice. La programmazione orientata agli oggetti, quindi, non è altro che l'atto di definire i tipi di dati in anticipo e far rispettare i contratti per il modo in cui comunicano tramite insiemi di funzioni.

Come hai osservato, esiste un'enorme classe di applicazioni per le quali questo non è un ottimo modo per implementare una soluzione. Sembra che tu viva in un mondo composto principalmente da tali applicazioni. Nel tuo post sul tuo blog citi di guardare alle implementazioni del problema "99 bottiglie di birra" (la tua "vetrina di programmazione preferita"). 99 bottiglie di birra fanno sicuramente parte di quella categoria. Cercare di comprendere la programmazione orientata agli oggetti osservando le implementazioni di 99 bottiglie di birra è un po 'come cercare di capire l'architettura di un grattacielo guardando una casa sull'albero. Anche una casa sull'albero molto ben costruita può solo insegnarti così tanto.

TL; DR: OO la programmazione è come la programmazione tradizionale, tranne per il fatto che concentri maggiormente i tuoi sforzi nel definire le strutture di dati in anticipo, e hai quelle strutture di dati che comunicano tra loro tramite puntatori a funzione.

0
Bryan Oakley

Il modo in cui l'ho capito per la prima volta è:

Prima della programmazione orientata agli oggetti, avevi Programmazione strutturata . Tutto è incentrato sul processo. La prima domanda che ti devi porre è " Cosa voglio fare con le informazioni? ".

Con la programmazione orientata agli oggetti, è incentrata sui dati. La prima domanda che ti devi porre è " Informazioni sulle streghe di cui devo occuparmi? ". Questo rende l'astrazione più semplice.

0
DavRob60

Ho scritto un post sul blog qualche tempo fa che potresti trovare utile: Procedural vs. OOP Explained .

0
VirtuosiMedia