CodeGym/Java Blog/Random-IT/Principi di OOP
John Squirrels
Livello 41
San Francisco

Principi di OOP

Pubblicato nel gruppo Random-IT
membri
Java è un linguaggio orientato agli oggetti. Ciò significa che è necessario scrivere programmi Java utilizzando un paradigma orientato agli oggetti. E questo paradigma implica l'uso di oggetti e classi nei tuoi programmi. Proviamo a utilizzare esempi per capire cosa sono le classi e gli oggetti e come applicare nella pratica i principi OOP di base (astrazione, ereditarietà, polimorfismo e incapsulamento).

Cos'è un oggetto?

Il mondo in cui viviamo è fatto di oggetti. Guardandoci intorno, possiamo vedere che siamo circondati da case, alberi, automobili, mobili, stoviglie e computer. Tutte queste cose sono oggetti e ognuna di esse ha un insieme di caratteristiche, comportamenti e scopi specifici. Siamo abituati agli oggetti e li usiamo sempre per scopi ben precisi. Ad esempio, se dobbiamo andare al lavoro, usiamo un'auto. Se vogliamo mangiare, usiamo i piatti. E se vogliamo riposare, troviamo un comodo divano. Gli esseri umani sono abituati a pensare in termini di oggetti per risolvere i problemi nella vita di tutti i giorni. Questo è uno dei motivi per cui gli oggetti vengono utilizzati nella programmazione. Questo approccio è chiamato programmazione orientata agli oggetti. Facciamo un esempio. Immagina di aver sviluppato un nuovo telefono e di voler iniziare la produzione di massa. Come sviluppatore del telefono, sai a cosa serve, come funziona e quali sono le sue parti (corpo, microfono, altoparlante, cavi, pulsanti, ecc.). Inoltre, solo tu sai come collegare queste parti. Ma non hai intenzione di realizzare i telefoni personalmente: hai un intero team di lavoratori per farlo. Per eliminare la necessità di spiegare ripetutamente come collegare le parti del telefono e per garantire che tutti i telefoni siano realizzati allo stesso modo, prima di iniziare a produrli, è necessario realizzare un disegno che descriva come è organizzato il telefono. In OOP chiamiamo una tale descrizione, disegno, diagramma o modello una classe. Costituisce la base per la creazione di oggetti quando il programma è in esecuzione. Una classe è una descrizione di oggetti di un certo tipo, come un modello comune costituito da campi, metodi e un costruttore. Un oggetto è un'istanza di una classe.

Astrazione

Pensiamo ora a come possiamo passare da un oggetto nel mondo reale a un oggetto in un programma. Useremo il telefono come esempio. Questo mezzo di comunicazione ha una storia che abbraccia più di 100 anni. Il telefono moderno è un dispositivo molto più complesso del suo predecessore del XIX secolo. Quando si utilizza il telefono, non si pensa alla sua organizzazione e ai processi che si verificano al suo interno. Utilizziamo semplicemente le funzioni fornite dagli sviluppatori del telefono: pulsanti o un touch screen per inserire un numero di telefono ed effettuare chiamate. Una delle prime interfacce telefoniche era una manovella che doveva essere ruotata per effettuare una chiamata. Certo, questo non era molto conveniente. Ma ha svolto la sua funzione in modo impeccabile. Se confronti i telefoni più moderni e i primissimi, si individuano subito le funzioni più importanti per il dispositivo di fine Ottocento e per il moderno smartphone. Sono la capacità di effettuare chiamate e la capacità di ricevere chiamate. In effetti, questo è ciò che rende il telefono un telefono e non qualcos'altro. Ora ho appena applicato un principio di OOP: identificare le caratteristiche e le informazioni più importanti di un oggetto. Questo principio si chiama astrazione. In OOP, l'astrazione può anche essere definita come un metodo per rappresentare elementi di un'attività del mondo reale come oggetti in un programma. L'astrazione è sempre associata alla generalizzazione di determinate proprietà di un oggetto, quindi l'importante è separare le informazioni significative da quelle insignificanti nel contesto dell'attività da svolgere. Inoltre, possono esserci diversi livelli di astrazione. Permettere' Proviamo ad applicare il principio di astrazione ai nostri telefoni. Per iniziare, identificheremo i tipi più comuni di telefoni, dai primissimi telefoni a quelli dei giorni nostri. Ad esempio, potremmo rappresentarli sotto forma di diagramma in Figura 1. Principi di OOP - 2Utilizzando l'astrazione, possiamo ora identificare le informazioni generali in questa gerarchia di oggetti: l'oggetto astratto generale (telefono), le caratteristiche comuni del telefono (ad esempio l'anno della sua creazione) e l'interfaccia comune (tutti i telefoni possono ricevere ed effettuare chiamate). Ecco come appare in Java:
public abstract class AbstractPhone {
    private int year;

    public AbstractPhone(int year) {
        this.year = year;
    }
    public abstract void call(int outgoingNumber);
    public abstract void ring(int incomingNumber);
}
In un programma, possiamo creare nuovi tipi di telefoni utilizzando questa classe astratta e applicando altri principi di base dell'OOP, che esploreremo di seguito.

Incapsulamento

Con l'astrazione identifichiamo ciò che è comune a tutti gli oggetti. Ma ogni tipo di telefono è unico, in qualche modo diverso dagli altri. In un programma, come tracciamo i confini e identifichiamo questa individualità? Come facciamo in modo che nessuno possa accidentalmente o deliberatamente rompere il nostro telefono o tentare di convertire un modello in un altro? Nel mondo reale, la risposta è ovvia: devi mettere tutte le parti in una custodia del telefono. Dopotutto, se non lo fai, invece di lasciare tutte le parti interne del telefono e i cavi di collegamento all'esterno, qualche curioso sperimentatore vorrà sicuramente "migliorare" il nostro telefono. Per prevenire tali ritocchi, il principio dell'incapsulamento viene utilizzato nella progettazione e nel funzionamento di un oggetto. Questo principio afferma che gli attributi e il comportamento di un oggetto sono combinati in un'unica classe, l'oggetto' L'implementazione interna di s è nascosta all'utente e viene fornita un'interfaccia pubblica per lavorare con l'oggetto. Il compito del programmatore è determinare quali degli attributi e dei metodi di un oggetto dovrebbero essere disponibili per l'accesso pubblico e quali sono i dettagli di implementazione interni che dovrebbero essere inaccessibili.

Incapsulamento e controllo degli accessi

Supponiamo che le informazioni su un telefono (l'anno di produzione o il logo del produttore) siano incise sul retro al momento della fabbricazione. L'informazione (il suo stato) è specifica per questo particolare modello. Possiamo dire che il produttore si è assicurato che queste informazioni fossero immutabili: è improbabile che qualcuno pensi di rimuovere l'incisione. Nel mondo Java, una classe descrive lo stato degli oggetti futuri utilizzando i campi e il loro comportamento è descritto utilizzando i metodi. L'accesso allo stato e al comportamento di un oggetto è controllato mediante modificatori applicati a campi e metodi: privato, protetto, pubblico e predefinito. Ad esempio, abbiamo deciso che l'anno di produzione, il nome del produttore e uno dei metodi sono dettagli di implementazione interna della classe e non possono essere modificati da altri oggetti nel programma. Nel codice,
public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    // findSwitch
    // openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("Calling");
}

public void ring() {
    System.out.println("Ring-ring");
}

 }
Il modificatore private consente l'accesso ai campi e ai metodi della classe solo all'interno di questa classe. Ciò significa che è impossibile accedere ai campi privati ​​dall'esterno, perché i metodi privati ​​non possono essere chiamati. Limitare l'accesso al metodo openConnection ci lascia anche la possibilità di modificare liberamente l'implementazione interna del metodo, poiché è garantito che il metodo non verrà utilizzato o non interromperà il lavoro di altri oggetti. Per lavorare con il nostro oggetto, lasciamo disponibili i metodi call e ring usando il modificatore public. Anche fornire metodi pubblici per lavorare con gli oggetti fa parte dell'incapsulamento, poiché se l'accesso fosse negato completamente, diventerebbe inutile.

Eredità

Diamo un'altra occhiata al diagramma dei telefoni. Puoi vedere che è una gerarchia in cui un modello ha tutte le caratteristiche dei modelli situati più in alto lungo il suo ramo e ne aggiunge alcune proprie. Ad esempio, uno smartphone utilizza una rete cellulare per la comunicazione (ha le proprietà di un telefono cellulare), è wireless e portatile (ha le proprietà di un telefono cordless) e può ricevere ed effettuare chiamate (ha le proprietà di un telefono). Quello che abbiamo qui è l'ereditarietà delle proprietà degli oggetti. In programmazione, ereditarietà significa utilizzare le classi esistenti per definirne di nuove. Consideriamo un esempio di utilizzo dell'ereditarietà per creare una classe smartphone. Tutti i telefoni cordless sono alimentati da batterie ricaricabili, che hanno una certa autonomia. Di conseguenza, aggiungiamo questa proprietà alla classe dei telefoni cordless:
public abstract class CordlessPhone extends AbstractPhone {

    private int hour;

    public CordlessPhone (int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
I telefoni cellulari ereditano le proprietà di un telefono cordless e implementiamo i metodi di chiamata e suoneria in questa classe:
public class CellPhone extends CordlessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming call from " + incomingNumber);
    }
}
E infine, abbiamo la classe degli smartphone, che, a differenza dei classici telefoni cellulari, ha un sistema operativo completo. Puoi espandere le funzionalità del tuo smartphone aggiungendo nuovi programmi che possono essere eseguiti sul suo sistema operativo. Nel codice, la classe può essere descritta come segue:
public class Smartphone extends CellPhone {

    private String operationSystem;

    public Smartphone(int year, int hour, String operationSystem) {
        super(year, hour);
        this.operationSystem = operationSystem;
    }
public void install(String program) {
    System.out.println("Installing " + program + " for " + operationSystem);
}

}
Come puoi vedere, abbiamo creato un bel po' di nuovo codice per descrivere la classe Smartphone , ma abbiamo ottenuto una nuova classe con nuove funzionalità. Questo principio di OOP consente di ridurre notevolmente la quantità di codice Java richiesto, semplificando così la vita del programmatore.

Polimorfismo

Nonostante le differenze nell'aspetto e nel design dei vari tipi di telefoni, possiamo identificare alcuni comportamenti comuni: tutti possono ricevere ed effettuare chiamate e tutti hanno un set di controlli abbastanza chiaro e semplice. In termini di programmazione, il principio di astrazione (che già conosciamo) ci permette di dire che gli oggetti telefono hanno un'interfaccia comune. Ecco perché le persone possono facilmente utilizzare diversi modelli di telefoni che hanno gli stessi controlli (pulsanti meccanici o touchscreen), senza entrare nei dettagli tecnici del dispositivo. Pertanto, utilizzi costantemente un telefono cellulare e puoi facilmente effettuare una chiamata dalla rete fissa del tuo amico. Il principio di OOP che afferma che un programma può utilizzare oggetti con un'interfaccia comune senza alcuna informazione sulla struttura interna dell'oggetto è chiamato polimorfismo. Permettere' Immaginiamo di aver bisogno del nostro programma per descrivere un utente che può utilizzare qualsiasi telefono per chiamare un altro utente. Ecco come possiamo farlo:
public class User {
    private String name;

    public User(String name) {
        this.name = name;
            }

    public void callAnotherUser(int number, AbstractPhone phone){
// And here's polymorphism: using the AbstractPhone type in the code!
        phone.call(number);
    }
}
 }
Ora descriveremo diversi tipi di telefoni. Uno dei primi telefoni:
public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Crank the handle");
        System.out.println("What number would you like to connect to?");
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
Un normale telefono fisso:
public class Phone extends AbstractPhone {

    public Phone(int year) {
        super(year);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
E infine, un bel videotelefono:
public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Connecting video call to " + outgoingNumber);
    }
    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming video call from " + incomingNumber);
    }
  }
Creeremo oggetti nel metodo main() e testeremo il metodo callAnotherUser() :
AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Jason");
user.callAnotherUser(224466, firstPhone);
// Crank the handle
// What number would you like to connect to?
user.callAnotherUser(224466, phone);
// Calling 224466
user.callAnotherUser(224466, videoPhone);
// Connecting video call to 224466
La chiamata dello stesso metodo sull'oggetto utente produce risultati diversi. Un'implementazione specifica del metodo call viene selezionata dinamicamente all'interno del metodo callAnotherUser() in base al tipo specifico di oggetto passato quando il programma è in esecuzione. Questo è il vantaggio principale del polimorfismo: la possibilità di scegliere un'implementazione in fase di esecuzione. Negli esempi di classi telefoniche sopra riportati, abbiamo utilizzato l'override del metodo, un trucco in cui cambiamo l'implementazione di un metodo definito nella classe base senza modificare la firma del metodo. Questo essenzialmente sostituisce il metodo: il nuovo metodo definito nella sottoclasse viene chiamato quando il programma viene eseguito. Di solito, quando sovrascriviamo un metodo, @Overrideviene utilizzata l'annotazione. Dice al compilatore di controllare le firme dei metodi sovrascritti e sovrascritti. Infine, per assicurarti che i tuoi programmi Java siano coerenti con i principi dell'OOP, segui questi suggerimenti:
  • identificare le caratteristiche principali di un oggetto;
  • identificare proprietà e comportamenti comuni e utilizzare l'ereditarietà durante la creazione di classi;
  • utilizzare tipi astratti per descrivere oggetti;
  • cerca di nascondere sempre metodi e campi relativi all'implementazione interna di una classe.
Commenti
  • Popolari
  • Nuovi
  • Vecchi
Devi avere effettuato l'accesso per lasciare un commento
Questa pagina non ha ancora commenti