JavaRush /Java Blog /Random-IT /Programmazione ad oggetti (traduzione dell'articolo)
Exidnus
Livello 38
Санкт-Петербург

Programmazione ad oggetti (traduzione dell'articolo)

Pubblicato nel gruppo Random-IT
Dal traduttore: Sfortunatamente non ho alcuna esperienza significativa nella traduzione dall'inglese, anche se leggo parecchio in inglese. Ma si è scoperto che leggere e tradurre sono due cose diverse. Inoltre, sfortunatamente, non ho una significativa esperienza di programmazione (recentemente ho appena realizzato una semplice applicazione web in Spring MVC e Hibernate). Pertanto, la traduzione si è rivelata molto peggiore di quanto avrebbe potuto essere. Mi sono preso la libertà di correggere leggermente gli esempi di codice forniti nell'articolo, poiché non rispettano le convenzioni di denominazione in Java. Forse non valeva la pena tradurre i nomi di alcuni modelli (una traduzione del genere non fornisce molta comprensione), ma pensavo che questo fosse il male minore. Vale la pena menzionare separatamente “alta coesione” come traduzione di “alta coesione”. Sono d'accordo, non è la migliore traduzione. Ma “connettività forte” significa “accoppiamento elevato” (un altro concetto importante), ed è improbabile che la “coerenza” sia adatta in questo caso. Sono aperto alle critiche e accetterò con gratitudine commenti sull'articolo in qualsiasi forma. La programmazione orientata agli oggetti è uno stile di programmazione in cui un programma è costituito da componenti che corrispondono a oggetti del mondo reale. Qualsiasi oggetto reale ha alcune proprietà (che possono o meno cambiare nel tempo) e un comportamento (che può o meno cambiano a seconda degli altri). condizioni). Ad esempio, una matita è un oggetto del mondo reale che ha le seguenti proprietà:
  • È rosso (questo non cambia nel tempo).
  • Ora è lungo 10 centimetri (questo potrebbe cambiare se la matita viene temperata).
Ed ha il seguente comportamento:
  • Se usato correttamente lascia il segno.
  • La traccia può differire a seconda della pressione (a seconda di fattori esterni).
  • La sua lunghezza si accorcia man mano che viene affilata (comportamento permanente).
Come in questo esempio, gli oggetti del mondo reale possono avere molte proprietà, ma quando scriviamo programmi prendiamo in considerazione solo le proprietà necessarie. La programmazione orientata agli oggetti ha i suoi vantaggi. Ad esempio, rende più semplice stabilire una connessione tra un oggetto del mondo reale e un programma nel modo previsto. Ciò è davvero utile man mano che l'applicazione cresce e molti oggetti interagiscono tra loro. Questo aiuta a distribuire le responsabilità all'interno del mondo oggettivo, permettendoti di concentrarti sul pensare attraverso l'applicazione. Un'altra caratteristica importante associata all'OOP ( Object Oriented Programming) è la classificazione degli oggetti. Poiché il mondo (reale/virtuale) è pieno di oggetti, è difficile controllarli individualmente. Abbiamo bisogno di un modo per classificare questi oggetti che ci aiuti ad associare i diversi oggetti e le loro proprietà, come ad esempio una matita nera. Sarebbe indistinguibile (stesso?) se utilizzato nell'esempio precedente, ma è un oggetto diverso. Ma poiché sono entrambe matite, appartengono alla stessa classe "Matita". Mentre la penna, che è molto simile ad una matita, appartiene ad una classe diversa. Tuttavia, penna e matita sono entrambi “Strumenti di Scrittura”. La programmazione orientata agli oggetti si basa sui seguenti principi:
Astrazione
L'astrazione è definita come la qualità dell'interazione con le idee piuttosto che con gli eventi o, in altre parole, la libertà dalle qualità rappresentative . Ciò consente ai programmatori di concentrarsi su cosa programmare piuttosto che su come programmare . L'astrazione può essere pensata come un contratto attraverso il quale forniamo funzionalità. I dettagli di implementazione potrebbero essere nascosti quando si utilizza questo concetto. Se ad esempio abbiamo bisogno di una classe che scriva, allora dobbiamo essere sicuri che abbia un metodo "write", abstract class writer { write (); } cosa abbiamo fatto? Abbiamo progettato una classe di alto livello che è astratta, in altre parole, sa di quali funzionalità abbiamo bisogno, ma come implementarle esula dall'ambito di questa classe. Ciò offre molti vantaggi:
  • Divulghiamo le informazioni minime necessarie a entità esterne, questo ci consente di concentrarci sul pensiero attraverso il programma (questo consente un pensiero mirato), evitare confusione ed evitare di fare promesse non intenzionali.
  • Lasciamo spazio a miglioramenti futuri che non sarebbero possibili se i dettagli di implementazione fossero rivelati.
Eredità
"Eredità" nell'inglese comune significa "acquisire e trasmettere". Questa parola esiste nella nostra cultura da molto tempo. Gli antenati acquisirono la terra con il duro lavoro e la trasmisero ai loro figli, anche la natura favorisce l'eredità. Tutte le proprietà del corpo, come altezza, colore della pelle/occhi/capelli, ecc. dipendono dai geni che ereditiamo dai nostri genitori. L’ereditarietà impedisce di reinventare la ruota e accelera il progresso. È lo stesso in OOP. Creiamo una classe genitore con alcune proprietà/comportamenti di base. Tutte le classi che ereditano da questo genitore conterranno le stesse proprietà/comportamenti del genitore. Tuttavia, le classi ereditate possono acquisire più proprietà/comportamenti o modificare l'implementazione del comportamento. class WritingInstrument { colour; write() { } } class Pen (child of parent) { inkcolour; } Nell'esempio sopra, la classe genitore (WritingInstrument) ha una proprietà "colore" e un comportamento "scrittura". Quando viene dichiarata la classe discendente (handle), non è necessario dichiarare nuovamente la proprietà "color" e il comportamento "write". Sono presenti nella classe "handle" per ereditarietà. Tuttavia, una classe discendente può dichiarare le proprie proprietà/comportamenti aggiuntivi. Come possiamo usarlo nella pratica? Noi sviluppatori siamo molto pigri. Non vogliamo stampare qualcosa più e più volte. L’esistenza di più copie dello stesso codice è sconsigliata per le seguenti considerazioni:
  • Minore è il numero di copie del codice, più facile sarà la sua manutenzione.
  • Se non ci sono molte copie del codice, la modifica in un punto diventa visibile ovunque.
  • Meno codice, meno errori.
  • Se un codice viene utilizzato in molti posti, si ottiene la generalizzazione.
  • Ci concentriamo sulla scrittura del codice.
  • Ci concentriamo sui test.
L'ereditarietà in Java si ottiene utilizzando le parole chiave "extends" e "implements". class WritingInstrument { } class Pen extends WritingInstrument { }
Polimorfismo
La parola “polimorfismo” deriva da due parole: “Poly” , cioè “molti” / “più di uno” “morph” , cioè “forma” Letteralmente la parola “polimorfismo” si riferisce alla capacità degli oggetti di comportarsi in modi diversi a seconda delle condizioni. Nella programmazione, il polimorfismo può essere implementato in diversi punti:
  • Classi
  • Metodi
  • Operatori
Tutto quanto sopra potrebbe comportarsi diversamente a seconda delle condizioni, forse del contesto, in cui vengono utilizzati. Ciò è utile perché il client (il programmatore che utilizza le tue librerie) non ha bisogno di conoscere molte sottigliezze e la funzionalità desiderata viene implementata selezionando le informazioni necessarie dal contesto. Class WritingObject { wrire() { // пишем, используя стандартные (по дефолту) цвета } } class Pencil extends WritingObject { write() { // пишем, используя серый цвет, написанный текст можно стереть } } class Pen extends WritingObject { write() { // пишем, используя голубой цвет, написанный текст нельзя стереть } } class Main { main() { WritingObject wr = new WritingObject(); wr.write(); // первый вызов WritingObject wr = new Pen(); wr.write(); // второй вызов WritingObject wr2 = new Pencil(); wr2.write(); // третий вызов } } L'esempio precedente ha un'implementazione predefinita in WritingObject, che viene estesa/sostituita dalle classi discendenti pen e pen. Il metodo write() viene chiamato tre volte nella classe Main. Ogni volta viene chiamata un'implementazione diversa a seconda dell'oggetto su cui viene chiamato il metodo. In questo caso, il metodo write() ha molti tipi di comportamento perché è polimorfico.
Incapsulamento
L'incapsulamento è definito come la raccolta di dati/funzionalità correlati in un'unica unità. Ciò aiuta a facilitare l'accesso/modifica dei dati. Ad esempio, se dobbiamo stampare tutte le proprietà di un determinato utente, abbiamo le seguenti opzioni: printUserProperties(userName, userId, firstname, lastname, email, phone, … … ….) Abbiamo creato un metodo che prende tutte le proprietà e le stampa una dopo l'altra. All'aumentare del numero di elementi nell'elenco, non sarà più possibile identificare i campi corretti e l'aggiunta/rimozione di un campo modificherà la firma del metodo. Pertanto, dobbiamo sostituire tutti gli utenti di questo metodo, anche se non necessitano dei campi aggiunti di recente. Per rendere il codice più leggibile e facilitare le modifiche future, incapsuliamo le proprietà in una classe e la trasformiamo in un oggetto collettivo.Un class User { userName userId firstname lastname email phone .. .. .. } printUserProperties(user) {} oggetto è un insieme software di variabili e metodi associati. Puoi rappresentare oggetti del mondo reale utilizzando oggetti di programma. Puoi immaginare dei veri cani in un programma di animazione o una vera bicicletta come oggetto software all'interno di una cyclette. In OOP, una classe è un modello estensibile (modello-codice-programma) per creare oggetti, fornire loro uno stato iniziale (variabili) e implementare il comportamento (funzioni, metodi). L'acronimo SOLID è stato coniato da Michael Feather per i “primi cinque principi” nominati da Robert C. Martin all'inizio degli anni 2000. L'obiettivo dei principi, se implementati insieme, è aumentare la probabilità che il programmatore crei un sistema facile da mantenere ed estendere. I principi SOLID sono linee guida nello sviluppo del programma necessarie per rimuovere il codice “marcio” attraverso il refactoring, in seguito al quale il codice dovrebbe diventare facilmente leggibile ed estensibile. Questo fa parte della strategia di programmazione agile e adattiva.
Principio di responsabilità unica
Nell'OOP, il principio di responsabilità unica afferma che ciascuna classe dovrebbe essere responsabile di una parte delle funzionalità fornite dal programma e che la responsabilità dovrebbe essere completamente incapsulata da quella classe. Tutte le sue funzionalità dovrebbero essere strettamente correlate a questa responsabilità.
Principio aperto/chiuso
Nell’OOP, il principio aperto/chiuso afferma che “le entità software (classi, moduli, metodi, ecc.) dovrebbero essere aperte all’estensione ma chiuse al cambiamento”. In altre parole, l'entità deve consentire che il suo comportamento venga esteso senza modificare il codice sorgente.
Principio di sostituzione di Liskov
La sostituibilità è un principio dell’OOP. Afferma che se S in un programma per computer è un sottotipo di T, allora gli oggetti di tipo T devono essere tali da poter essere sostituiti da oggetti di tipo S (cioè oggetti di tipo S possono essere sostituiti da oggetti di tipo T) senza modificare eventuali programmi di proprietà richiesti (precisione, completamento delle attività, ecc.).
Principio di segregazione delle interfacce
Il principio della separazione delle interfacce afferma che il programmatore client non dovrebbe essere costretto a dipendere da metodi che non utilizza. Secondo questo principio, è necessario dividere le interfacce grandi in interfacce più piccole e più specifiche in modo che il programmatore client conosca solo i metodi che gli interessano. Lo scopo del principio di disaccoppiamento dell'interfaccia è mantenere il sistema disaccoppiato, il che renderà più semplice il refactoring, apportare modifiche e ridistribuire.
Principio di inversione delle dipendenze
Nell'OOP, il principio dell'inversione delle dipendenze significa una forma specifica di disconnessione tra i moduli del programma. Seguendo questo principio, le relazioni di dipendenza standard stabilite dai moduli di alto livello che formano l'architettura dell'applicazione (impostazione delle politiche) ai moduli dipendenti di basso livello vengono invertite (invertite), in modo che i moduli di alto livello modificati diventino indipendenti dai dettagli di implementazione di moduli di basso livello. Questo principio afferma:
  • I moduli di alto livello non dovrebbero dipendere dai moduli di basso livello. Entrambi i tipi di moduli devono dipendere da astrazioni.
  • Le astrazioni non dovrebbero dipendere dai dettagli di implementazione. I dettagli devono dipendere dalle astrazioni.
Il principio inverte il modo in cui le persone possono pensare alla progettazione orientata agli oggetti sostenendo che gli oggetti di alto e basso livello dovrebbero dipendere dalle stesse astrazioni.

Principi GRASP

I General Responsibility Assignment Software Patterns (GRASP) forniscono linee guida per l'assegnazione di responsabilità a classi e oggetti nella progettazione orientata agli oggetti.
Controllore
Il pattern Controller assegna la responsabilità di interagire con gli eventi di sistema a classi non GUI che rappresentano l'intero sistema o lo scenario del caso d'uso. Controllore:
  • Si tratta di un oggetto che non interagisce direttamente con l'utente ed è responsabile della ricezione e della risposta agli eventi di sistema.
  • Deve essere utilizzato per gestire tutti gli eventi di sistema di uno (o più casi d'uso correlati).
  • È il primo oggetto dietro la GUI che controlla le operazioni di sistema.
  • Non deve svolgere il lavoro da solo; il suo compito è controllare il flusso degli eventi.
Creatore
Il compito della classe creatrice è creare e avviare oggetti per un utilizzo successivo. Conosce i parametri di inizializzazione e quale oggetto verrà creato. A volte la classe creator crea attivamente oggetti e li inserisce nella cache e fornisce un'istanza quando necessario.
Alta coesione
L'alta coesione è un modello valutativo, il cui scopo è preservare gli oggetti in uno stato tale da mirare a svolgere un compito chiaro, facilmente controllabili e comprensibili. L'accoppiamento alto viene solitamente utilizzato per supportare l'accoppiamento basso. Un'elevata coerenza significa che le responsabilità di un dato elemento sono chiaramente definite (fortemente correlate e altamente mirate). Dividere un programma in classi e sottosistemi è un esempio di azioni che aumentano la coesione delle proprietà del sistema. L'accoppiamento lento, d'altro canto, è una situazione in cui un elemento ha troppi compiti non correlati. Gli elementi liberamente accoppiati tendono ad essere difficili da comprendere, riutilizzabili, difficili da mantenere e difficili da modificare.
Indiretto
Il modello Roundabout mantiene l'accoppiamento libero (e la riusabilità) tra due elementi assegnando la responsabilità dell'interazione tra loro a un oggetto intermedio. Un esempio è l'introduzione di un controller per mediare tra i dati (modello) e la loro visualizzazione (vista) nel pattern Model-View-Controller (MVC).
Esperto di informazioni
Information Expert (anche Expert o Expert Principle) è un principio utilizzato per determinare a chi delegare la responsabilità. Le responsabilità includono metodi, campi calcolati, ecc. Quando si utilizza questo principio nell'assegnare la responsabilità, l'approccio principale è la seguente sequenza di azioni: analizzare la responsabilità, identificare le informazioni necessarie per adempierla e infine stabilire dove si trovano queste informazioni. L'utilizzo del principio dell'esperto dell'informazione si traduce nell'assegnare la responsabilità alla classe che ha più informazioni per eseguirla.
Basso accoppiamento
L'accoppiamento libero è un modello di valutazione che specifica come assegnare le responsabilità: accoppiamento libero tra classi, la modifica di una dovrebbe avere un impatto minimo sull'altra, massimizzando la riusabilità.
Polimorfismo
Secondo il polimorfismo, la variazione del comportamento in base al tipo è assegnata ai tipi per i quali avviene tale variazione. Ciò si ottiene utilizzando operazioni polimorfiche.
Variazioni protette
Il pattern Protected Changes protegge gli elementi dalle modifiche ad altri elementi (oggetti, sistemi, sottosistemi) avvolgendo il focus dell'instabilità in un'interfaccia e utilizzando il polimorfismo per creare diverse implementazioni di quell'interfaccia.
Pura fabbricazione
La costruzione pura implica una classe che non rappresenta un concetto nel dominio del problema ed è progettata specificamente per ottenere un accoppiamento lento, un accoppiamento elevato e quindi il massimo potenziale di riutilizzo (la soluzione offerta dal modello Information Expert non raggiunge questo obiettivo). Una classe di questo tipo viene solitamente chiamata “Servizio” nella progettazione basata sul dominio.

Critica

La ricerca di Potok et al. non ha mostrato differenze significative tra l'OOP e gli approcci procedurali.
Il confronto critico dell'OOP con altre tecnologie, in particolare con le tecnologie relazionali, è difficile a causa della mancanza di una definizione di OOP che sia rigorosa e ampiamente accettata (Christopher J. Date)
Rispetto ad altri linguaggi (dialetti LISP, linguaggi funzionali, ecc.), i linguaggi OOP non presentano un vantaggio unico e impongono una complessità non necessaria. (Lawrence Krubner)
Trovo che la programmazione orientata agli oggetti sia tecnicamente fragile. Cerca di scomporre il mondo in parti in termini di interfacce che variano all'interno di un unico tipo. Per affrontare problemi reali, sono necessarie algebre multiordinate, famiglie di interfacce che si estendono a molti tipi. Trovo che la programmazione orientata agli oggetti sia filosoficamente malsana. Afferma che tutto è un oggetto. Anche se questo fosse vero, non sarebbe molto interessante: dire che tutto è un oggetto significa non dire assolutamente nulla. (Alessandro Stepanov)
La popolarità dell'OOP tra le grandi aziende è dovuta a "grandi gruppi (e frequentemente mutevoli) di programmatori mediocri". La disciplina imposta dall’OOP impedisce al programmatore di fare “troppi danni”. (Paul Graham)
La programmazione orientata agli oggetti mette i nomi al primo posto. Perché ricorrere a misure così estreme e mettere una parte del discorso su un piedistallo? Perché un concetto ha la precedenza su un altro? È impossibile che l'OOP renda improvvisamente i verbi meno importanti per il nostro pensiero. Questa è una prospettiva stranamente distorta. (Steve Yegge)
Rick Hickey, il creatore di Clojure, descrisse i sistemi di oggetti come modelli estremamente semplificati del mondo reale. Ha sottolineato l'incapacità dell'OOP di modellare correttamente il tempo, il che crea enormi problemi quando il multithreading diventa comune nei programmi. Eric S. Raymond, un programmatore Unix e sostenitore del software open source, è stato critico nei confronti dell'affermazione che l'OOP è "The One Solution" e ha scritto che l'OOP incoraggia programmi multilivello, il che ostacola la trasparenza. Come approccio opposto, Raymond ha citato l’esempio di Unix e C.

Collegamenti

Di Margaret Rouse @ WhatIs.com Wikipedia! ( Versione russa ) L'ereditarietà è polimorfismo SOLID (Object Oriented Design) ( Versione russa ) Principio di responsabilità unicaArgomenti contro OOPS ( Versione russa ) Cos'è OOPS (senza clamore pubblicitario) Traduzione: Varygin D.V.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION