JavaRush /Java Blog /Random-IT /Principi OOP

Principi OOP

Pubblicato nel gruppo Random-IT
Java è un linguaggio orientato agli oggetti. Ciò significa che è necessario scrivere programmi Java utilizzando uno stile orientato agli oggetti. E questo stile si basa sull'uso di oggetti e classi nel programma.

Principi di base dell'OOP:

Principi dell'OOP - 1Proviamo, con l'aiuto di esempi, a capire cosa sono le classi e gli oggetti e come applicare nella pratica i principi di base dell'OOP: astrazione, ereditarietà, polimorfismo e incapsulamento.

Cos'è un oggetto?

Il mondo in cui viviamo è costituito da oggetti. Se ci guardiamo intorno, vedremo che siamo circondati da case, alberi, automobili, mobili, stoviglie, computer. Tutti questi elementi sono oggetti e ciascuno di essi ha una serie di caratteristiche, comportamenti e scopi specifici. Siamo abituati agli oggetti e li usiamo sempre per scopi molto specifici. Ad esempio, se dobbiamo andare al lavoro usiamo l'auto, se vogliamo mangiare usiamo i piatti e se abbiamo bisogno di rilassarci abbiamo bisogno di un comodo divano. Una persona è abituata a pensare in modo obiettivo per risolvere i problemi nella vita di tutti i giorni. Questo era uno dei motivi per utilizzare gli oggetti nella programmazione e questo approccio alla creazione di programmi era chiamato orientato agli oggetti. Facciamo un esempio. Immagina di aver sviluppato un nuovo modello di telefono e di voler avviarne la produzione in serie. Come progettista di telefoni, sai a cosa serve, come funzionerà e da quali parti sarà composto (custodia, microfono, altoparlante, cavi, pulsanti, ecc.). Tuttavia, solo tu sai come collegare queste parti. Tuttavia, non hai intenzione di produrre telefoni personalmente, per questo hai un intero staff di dipendenti. Per non doverti spiegare ogni volta come collegare le parti del telefono e affinché tutti i telefoni in produzione risultino uguali, prima di iniziare a produrli dovrai fare un disegno sotto forma di descrizione della struttura del telefono. In OOP, tale descrizione, disegno, diagramma o modello è chiamato classe, dalla quale viene creato un oggetto quando il programma viene eseguito. Una classe è una descrizione di un oggetto non ancora creato, come un modello generale composto da campi, metodi e un costruttore, e un oggetto è un'istanza di una classe creata sulla base di questa descrizione.

Astrazione OOP

Pensiamo ora a come possiamo passare da un oggetto nel mondo reale a un oggetto in un programma, usando il telefono come esempio. La storia di questo mezzo di comunicazione supera i 100 anni e il telefono moderno, a differenza del suo predecessore del XIX secolo, è un apparecchio molto più complesso. Quando utilizziamo un telefono, non pensiamo alla sua struttura e ai processi che avvengono al suo interno. Utilizziamo semplicemente le funzioni fornite dagli sviluppatori del telefono: pulsanti o touch screen per selezionare un numero ed effettuare chiamate. Una delle prime interfacce telefoniche era una manopola che giravi per effettuare una chiamata. Ovviamente questo non era molto conveniente. Ciononostante la maniglia ha svolto correttamente la sua funzione. Se guardi il telefono più moderno e il primo in assoluto, puoi immediatamente identificare i dettagli più importanti che sono importanti sia per un dispositivo della fine del XIX secolo che per uno smartphone ultramoderno. Questo significa effettuare una chiamata (comporre un numero) e ricevere una chiamata. In sostanza, questo è ciò che rende un telefono un telefono e non qualcos'altro. Ora abbiamo applicato il principio nell'OOP, evidenziando le caratteristiche e le informazioni più importanti su un oggetto. Questo principio dell'OOP è chiamato astrazione. L'astrazione in OOP può anche essere definita come un modo di rappresentare elementi di un problema del mondo reale come oggetti in un programma. L'astrazione è sempre associata alla generalizzazione di alcune informazioni sulle proprietà di oggetti o oggetti, quindi la cosa principale è separare le informazioni significative da quelle insignificanti nel contesto del problema da risolvere. In questo caso possono esserci diversi livelli di astrazione. Proviamo ad applicare il principio di astrazione ai nostri telefoni. Innanzitutto, evidenziamo i tipi di telefoni più comuni dai primi ai giorni nostri. Ad esempio, possono essere rappresentati sotto forma di un diagramma mostrato nella Figura 1. Principi dell'OOP - 2Ora, con l'aiuto dell'astrazione, possiamo evidenziare informazioni generali in questa gerarchia di oggetti: un tipo astratto comune di oggetti - telefono, una caratteristica generale di il telefono - l'anno della sua creazione e un'interfaccia comune - tutti i telefoni sono in grado di ricevere e inviare 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 outputNumber);
    public abstract void ring (int inputNumber);
}
Sulla base di questa classe astratta, saremo in grado di creare nuovi tipi di telefoni nel programma utilizzando altri principi Java OOP di base, che considereremo di seguito.

Incapsulamento

Con l'aiuto dell'astrazione, evidenziamo ciò che è comune a tutti gli oggetti. Tuttavia, ogni modello di telefono è individuale e leggermente diverso dagli altri. Come possiamo tracciare i confini del programma e designare questa individualità? Come possiamo assicurarci che nessuno degli utenti possa accidentalmente o intenzionalmente rompere il nostro telefono o tentare di convertire un modello in un altro? Per il mondo degli oggetti reali la risposta è ovvia: è necessario inserire tutte le parti nel corpo del telefono. Dopotutto, se non lo facciamo e lasciamo fuori tutte le parti interne del telefono e i cavi che le collegano, ci sarà sicuramente uno sperimentatore curioso che vorrà "migliorare" il funzionamento del nostro telefono. Per evitare tale interferenza nella progettazione e nel funzionamento di un oggetto, l'OOP utilizza il principio di incapsulamento - un altro principio base dell'OOP, in cui gli attributi e il comportamento di un oggetto sono combinati in un'unica classe, l'implementazione interna dell'oggetto è nascosta l'utente e viene fornita un'interfaccia aperta per lavorare con l'oggetto. Il compito del programmatore è determinare quali attributi e metodi saranno accessibili pubblicamente e quali sono implementazioni interne dell'oggetto e non dovrebbero essere modificate.

Incapsulamento e controllo degli accessi

Diciamo che durante la produzione, sul retro del telefono vengono incise informazioni a riguardo: l'anno di produzione o il logo dell'azienda produttrice. Queste informazioni caratterizzano in modo abbastanza specifico questo modello: le sue condizioni. Possiamo dire che lo sviluppatore del telefono si è preso cura dell'immutabilità di queste informazioni: difficilmente qualcuno penserà di rimuovere l'incisione. Nel mondo Java, lo stato degli oggetti futuri è descritto in una classe utilizzando i campi e il loro comportamento è descritto utilizzando metodi. La possibilità di modificare lo stato e il comportamento viene eseguita utilizzando i modificatori di accesso a campi e metodi: private, protected, publice default(accesso predefinito). Ad esempio, abbiamo deciso che l'anno di creazione, il nome del produttore del telefono e uno dei metodi appartengono all'implementazione interna della classe e non possono essere modificati da altri oggetti nel programma. Utilizzando il codice, la classe può essere descritta come segue:
public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    //findComutator
    //openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("I'm calling a number");
}

public void ring() {
    System.out.println("Дзынь-дзынь");
}

 }
Un modificatore privaterende i campi e i metodi di una classe disponibili solo all'interno di quella classe. Ciò significa che privatenon è possibile accedere ai campi dall'esterno, né è possibile privatechiamare metodi. Nascondere l'accesso a un metodo openConnectionci lascia anche l'opportunità di modificare liberamente l'implementazione interna di questo metodo, poiché è garantito che questo metodo non verrà utilizzato da altri oggetti e non ne interromperà il funzionamento. Per lavorare con il nostro oggetto, lasciamo aperti i metodi callutilizzando ringil modificatore public. Anche fornire metodi pubblici per lavorare con un oggetto fa parte del meccanismo di incapsulamento, poiché se l'accesso a un oggetto viene completamente negato, diventerà inutile.

Eredità

Diamo nuovamente un'occhiata alla tabella telefonica. Puoi vedere che rappresenta una gerarchia in cui il modello situato sotto ha tutte le caratteristiche dei modelli situati più in alto sul ramo, più le 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). In questo caso possiamo parlare di ereditarietà delle proprietà dell'oggetto. Nella programmazione, l'ereditarietà è l'uso di classi esistenti per definirne di nuove. Diamo un'occhiata a un esempio di creazione di una classe smartphone utilizzando l'ereditarietà. Tutti i telefoni cordless sono alimentati da batterie ricaricabili, che hanno una certa autonomia operativa in poche ore. Aggiungiamo quindi questa proprietà alla classe dei telefoni wireless:
public abstract class WirelessPhone extends AbstractPhone {

    private int hour;

    public WirelessPhone(int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
callI telefoni cellulari ereditano le proprietà di un telefono wireless, abbiamo anche aggiunto un'implementazione dei metodi e a questa classe ring:
public class CellPhone extends WirelessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

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

    @Override
    public void ring(int inputNumber) {
        System.out.println("A subscriber is calling you" + inputNumber);
    }
}
E infine la classe degli smartphone, che, a differenza dei classici cellulari, dispone di un sistema operativo a tutti gli effetti. Puoi aggiungere nuovi programmi supportati da questo sistema operativo al tuo smartphone, espandendone così le funzionalità. Utilizzando il 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, Smartphoneabbiamo creato pochissimo nuovo codice per descrivere la classe, ma abbiamo ottenuto una nuova classe con nuove funzionalità. L’utilizzo del principio di ereditarietà OOP può ridurre significativamente la quantità di codice e quindi facilitare il lavoro del programmatore.

Polimorfismo

Se consideriamo tutti i modelli di telefono, nonostante le differenze nell'aspetto e nel design dei modelli, possiamo identificare alcuni comportamenti comuni in essi: tutti possono ricevere ed effettuare chiamate e hanno un set di pulsanti di controllo abbastanza chiaro e semplice. Applicando uno dei principi di base dell'OOP, a noi già noto, l'astrazione in termini di programmazione, possiamo dire che l'oggetto telefono ha un'interfaccia comune. Pertanto, gli utenti del telefono possono utilizzare comodamente diversi modelli utilizzando gli stessi pulsanti di controllo (meccanici o touch), senza entrare nei dettagli tecnici del dispositivo. Quindi usi costantemente un telefono cellulare e puoi facilmente effettuare una chiamata dalla sua controparte fissa. Il principio in OOP secondo cui un programma può utilizzare oggetti con la stessa interfaccia senza informazioni sulla struttura interna dell'oggetto è chiamato polimorfismo . Immaginiamo che nel nostro programma dobbiamo descrivere un utente che può utilizzare qualsiasi modello di telefono per chiamare un altro utente. Ecco come farlo:
public class User {
    private String name;

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

    public void callAnotherUser(int number, AbstractPhone phone){
// here it is polymorphism - using the abstract type AbstractPhone phone in the code!
        phone.call(number);
    }
}
 }
Ora descriviamo i diversi modelli di telefono. Uno dei primi modelli di telefono:
public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outputNumber) {
        System.out.println("Turn the Handle");
        System.out.println("Give me the phone number, sir");
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("Phone calls");
    }
}
Telefono fisso normale:
public class Phone extends AbstractPhone {

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

    @Override
    public void call(int outputNumber) {
        System.out.println("I'm calling a number" + outputNumber);
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("Phone calls");
    }
}
E infine, un fantastico videotelefono:
public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outputNumber) {
        System.out.println("I connect a video channel for the subscriber" + outputNumber );
    }
    @Override
    public void ring(int inputNumber) {
        System.out.println("You have an incoming video call..." + inputNumber);
    }
  }
Creiamo oggetti nel metodo main()e testiamo il metodo callAnotherUser:
AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Andrey");
user.callAnotherUser(224466,firstPhone);
// Rotate the knob
// Tell me the number of the subscriber, sir
user.callAnotherUser(224466,phone);
//Call number 224466
user.callAnotherUser(224466,videoPhone);
//I connect the video channel for subscriber 224466
Chiamando lo stesso metodo sull'oggetto user, abbiamo ottenuto risultati diversi. La selezione di una determinata implementazione del metodo callall'interno di un metodo callAnotherUserè stata effettuata dinamicamente in base al tipo specifico dell'oggetto chiamante durante l'esecuzione del programma. Questo è il vantaggio principale del polimorfismo: la scelta dell'implementazione durante l'esecuzione del programma. Negli esempi di classi phone precedenti abbiamo utilizzato l'override del metodo, una tecnica che modifica l'implementazione del metodo definita nella classe base senza modificare la firma del metodo. Si tratta essenzialmente di una sostituzione del metodo ed è il nuovo metodo definito nella sottoclasse che viene chiamato quando viene eseguito il programma. In genere, quando si sovrascrive un metodo, viene utilizzata l'annotazione @Override, che indica al compilatore di verificare le firme dei metodi sovrascritti e sovrascritti. Di conseguenza , per garantire che lo stile del tuo programma sia conforme al concetto di OOP e ai principi di OOP Java, segui questi suggerimenti:
  • evidenziare le caratteristiche principali dell'oggetto;
  • evidenziare proprietà e comportamenti comuni e utilizzare l'ereditarietà durante la creazione di oggetti;
  • utilizzare tipi astratti per descrivere oggetti;
  • Cerca di nascondere sempre metodi e campi relativi all'implementazione interna della classe.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION