GNU/Linux >> Linux Esercitazione >  >> Linux

Procedura:Programmazione orientata agli oggetti - Costruttori ed ereditarietà

In precedenza

Abbiamo quasi raggiunto la fine del nostro viaggio attraverso questo round bonus della nostra Introduzione alla programmazione orientata agli oggetti! Nel nostro ultimo articolo, abbiamo iniziato il nostro esempio di Petting Zoo con una revisione di classi, oggetti e metodi. Se ti sei perso l'inizio di questa serie, puoi raggiungerci qui. Altrimenti, tuffiamoci di nuovo!


.

Costruttori

Se hai fatto l'esercizio con due Dog oggetti, era un po' noioso, giusto? Dopotutto, non abbiamo nulla per separare i cani l'uno dall'altro e non abbiamo modo di sapere, senza guardare il codice sorgente, quale cane ha prodotto quale abbaiare.

Nell'articolo precedente ho menzionato che quando crei oggetti, chiami un metodo speciale chiamato costruttore . Il costruttore assomiglia al nome della classe scritto come metodo. Ad esempio, per un Dog class, il costruttore verrebbe chiamato Dog() .

La particolarità dei costruttori è che sono il percorso per qualsiasi nuovo oggetto, quindi sono un ottimo posto per chiamare il codice che inizializza un oggetto con valori predefiniti. Inoltre, il valore restituito da un metodo del costruttore è sempre un oggetto della classe stessa, motivo per cui possiamo assegnare il valore restituito dal costruttore a una variabile del tipo di classe che creiamo.

Tuttavia, finora, non abbiamo affatto creato un costruttore, quindi come mai possiamo ancora chiamare quel metodo?

In molti linguaggi, incluso C#, il linguaggio ti offre un costruttore libero e vuoto senza che tu debba fare nulla. È implicito che tu voglia un costruttore; altrimenti non ci sarebbe modo di usare la classe per qualsiasi cosa, quindi le lingue presuppongono semplicemente che tu ne abbia scritta una.

Questo costruttore invisibile e gratuito è chiamato costruttore predefinito e, nel nostro esempio, sarà simile a questo:

public Dog(){ }

Nota che questa sintassi è molto simile a Speak() metodo che abbiamo creato in precedenza, tranne per il fatto che non restituiamo esplicitamente un valore né dichiariamo nemmeno il tipo restituito del metodo. Come accennato in precedenza, un costruttore restituisce sempre un'istanza della classe a cui appartiene.

In questo caso, quella è la classe Dog , ed è per questo che quando scriviamo Dog myDog = new Dog() , possiamo assegnare il nuovo oggetto a una variabile denominata myDog che è di tipo Dog .

Quindi aggiungiamo il costruttore predefinito al nostro Dog classe. Puoi copiare la riga sopra o, in Visual Studio, puoi usare una scorciatoia:digita ctor e premi Tab due volte. Dovrebbe generare il costruttore predefinito per te, come mostrato nella Figura 9:

Figura 9:aggiunta di un costruttore con 'ctor'

Il costruttore predefinito in realtà non ci fornisce nulla di nuovo perché ora sta facendo esplicitamente ciò che era stato fatto prima in modo implicito. Tuttavia, è un metodo, quindi ora possiamo aggiungere contenuto tra parentesi che verrà eseguito ogni volta che chiamiamo questo costruttore. E poiché il costruttore viene eseguito come prima cosa nella costruzione di un oggetto, è un luogo perfetto per aggiungere codice di inizializzazione.

Ad esempio, potremmo impostare il Name proprietà dei nostri oggetti a qualcosa aggiungendo codice come questo:

public Dog()
{
    this.Name = "Snoopy";
}

Questo esempio imposterà il Name proprietà di tutti i nuovi oggetti su "Snoopy".

Ovviamente non è molto utile perché non tutti i cani sono chiamati "Snoopy", quindi cambiamo invece la firma del metodo del costruttore in modo che accetti un parametro.

Le parentesi dei metodi non sono lì solo per sembrare carine; servono a contenere parametri che possiamo usare per passare valori a un metodo. Questa funzione si applica a tutti i metodi, non solo ai costruttori, ma facciamolo prima per un costruttore.

Modifica la firma del costruttore predefinita in questo:

public Dog(string dogName)

Questa aggiunta ci consente di inviare una string parametro nel costruttore e che, quando lo facciamo, possiamo fare riferimento a quel parametro con il nome dogName .

Quindi, aggiungi la seguente riga al blocco del metodo:

this.Name = dogName;

Questa riga imposta la proprietà di questo oggetto Name al parametro che abbiamo inviato al costruttore.

Nota che quando modifichi la firma del costruttore, ottieni un caso di squigglie rosse nel tuo file Program.cs, come mostrato nella Figura 10.

Figura 10:Un caso di squiggles rossi dal nostro nuovo costruttore

Quando aggiungiamo i nostri costruttori espliciti, C# e .NET non creeranno implicitamente un costruttore predefinito per noi. Nel nostro file Program.cs, stiamo ancora creando il Dog oggetti utilizzando il costruttore predefinito senza parametri, che ora non esiste più.

Per risolvere questo problema, dobbiamo aggiungere un parametro alla nostra chiamata del costruttore in Program.cs. Possiamo, ad esempio, aggiornare la nostra linea di costruzione degli oggetti come tale:

Cane mio Cane =nuovo Cane(“Snoopy”);

In questo modo verranno rimossi gli squiggles rossi e sarà possibile eseguire nuovamente il codice. Se esci o imposti il ​​punto di interruzione dopo l'ultima riga di codice, puoi guardare il pannello Locali e verificare che il Name del tuo oggetto è stata effettivamente impostata, come mostrato nella Figura 11.

Figura 11:vedere la nostra proprietà del nome impostata correttamente

Fatto? Bene! Ora abbiamo la possibilità di dare un nome al nostro cane, ma non è davvero un programma utile se le persone devono eseguire il debug per vedere cosa stiamo facendo. Quindi confondiamo un po' il codice per assicurarci di visualizzare il nome del cane che sta abbaiando.

Aggiorna il tuo Dog oggetti e cambia Speak() metodo in quanto tale:

public void Speak() { Console.WriteLine(this.Name + " says: Woof"); }

La modifica che abbiamo fatto ora dice a WriteLine metodo per concatenare il nome di questo oggetto con la stringa letterale “ dice:Woof” che dovrebbe darci un output che mostra meglio i nostri sforzi. Prova a eseguire il programma ora e dovresti vedere qualcosa di simile alla Figura 12.

Figura 12:Snoopy dice:Woof

Bel lavoro! Ora puoi creare molti cani con nomi diversi e abbaieranno tutti al tuo comando.

Naturalmente, uno zoo con solo cani è un po' noioso. Voglio dire, adoro i cani, ma forse potremmo aggiungere altri animali per rendere un po' più piccante l'esperienza?
.

Eredità

Iniziamo aggiungendo un nuovo Cat classe nel nostro file Animals.cs. Aggiungi il codice seguente accanto alla classe Dog, ma sempre all'interno delle parentesi dello spazio dei nomi PettingZoo:

class Cat
{
    public Cat(string catName)
    {
        this.Name = catName;
    }
    string Name;
    public void Speak() { Console.WriteLine(this.Name + " says: Meow!"); }
}

Quindi, creiamo un Cat oggetto in Program.cs e farlo parlare:

Cat myCat = new Cat("Garfield");
myCat.Speak();

L'esecuzione di questo programma ora dovrebbe fornire un output simile alla Figura 13.

Figura 13:Garfield dice:Miao

Il nostro programma è prevedibile e funziona, ma hai notato quanto siano simili le due classi? Vedi quanto codice abbiamo duplicato nelle due classi? L'orientamento agli oggetti non doveva salvarci dalla scrittura di codice più volte?

La risposta è si. Possiamo e dobbiamo semplificare questo codice per ridurre la quantità di duplicazioni. Quello che stiamo per approfondire dimostrerà una caratteristica di OO chiamata ereditarietà .

L'ereditarietà nell'orientamento agli oggetti consente di creare una gerarchia di classi e fare in modo che ciascuna classe erediti le proprietà ei metodi di una classe padre. Ad esempio, per il nostro zoo, possiamo creare un genitore Animal classe e avere Dog e Cat ereditare da quella classe. Quindi, possiamo spostare parti del nostro codice in Animal classe in modo che sia il Dog e Cat le classi erediteranno quel codice.

Se, invece, spostiamo le parti duplicate del nostro codice in questo Animal classe, quindi il Dog e Cat le classi erediteranno ciascuna le stesse proprietà e metodi, rendendo i figli cloni delle classi padre. Questa organizzazione non è molto utile perché vogliamo avere tipi distinti di animali. Sarebbe terribilmente noioso se gatti, cani e tutti gli altri animali fossero uguali. In effetti, ai fini del nostro programma, non avrebbe alcun senso chiamarli in modo diverso.

Ora, se l'ereditarietà significasse che potremmo creare solo classi figlie identiche alle loro classi padre, non avrebbe molto senso dedicarsi a tutto questo sforzo. Quindi, mentre ereditiamo tutte le parti di una classe genitore, possiamo anche sovrascrivere alcune parti di una classe genitore nelle classi figlie per rendere distinte le classi figlie.

Esaminiamolo e vediamo come funziona, vero?
.

Creazione di classi genitori e figli

Faremo qualche passo indietro e ricreare il Dog e Cat classi in un modo migliore. Per iniziare, abbiamo bisogno di una classe genitore che definisca le proprietà condivise ei metodi delle classi figlie.

Nel tuo file Animals.cs, rimuovi il Dog e Cat classi e aggiungi la seguente definizione di classe:

class Animal
{
    public string Name;
    public string Sound;
    public void Speak() { Console.WriteLine(this.Name + " says " + this.Sound); }
}

Non sorprende che questa classe assomigli al precedente Dog e Cat classi, con la nota eccezione che abbiamo reso pubbliche tutte le proprietà e i metodi e che abbiamo introdotto una nuova proprietà chiamata Sound di tipo string . Infine, abbiamo aggiornato Speak metodo per utilizzare il Suono variabile.

A questo punto, il tuo Program.cs non funzionerà e, se fai clic su quella scheda, dovresti vedere diversi messaggi di errore, il che è del tutto naturale perché abbiamo rimosso il Cat e Dog classi. Ricreiamoli e facciamolo usando l'ereditarietà. Nel tuo file Animals.cs, subito dopo Animal classe, aggiungi il seguente codice:

class Dog : Animal { }
class Cat : Animal { }

Queste nuove classi sono molto più brevi di prima e non replicano alcun codice. La nuova sintassi qui è un due punti seguito dal nome della classe Animal , che dice a C# che vogliamo entrambi Dog e Cat ereditare da Animal classe. In effetti, Dog e Cat diventano classi figlie di Animal come illustrato nel diagramma mostrato in Figura 14.

Figura 14:diagramma dell'ereditarietà della classe animale

Nota:ho utilizzato il termine Proprietà finora, il che è leggermente impreciso perché il termine corretto è campo come puoi vedere nel diagramma in Figura 14. Il campo è meno chiaro al di fuori dell'ambito della programmazione, quindi ho usato invece Property. Tecnicamente, una proprietà è un wrapper attorno a un campo in C#.

.
Abbiamo ancora un problema con il nostro file Program.cs, tuttavia, a causa del costruttore personalizzato che abbiamo creato. Se provi a eseguire o eseguire il debug del nostro programma, dovresti visualizzare un messaggio di errore che dice che "'PettingZoo.Cat' non contiene un costruttore che accetta 1 argomento" e uno simile per "PettingZoo.Dog".

Per correggere questo errore, dobbiamo aggiungere nuovamente i costruttori. Questa volta, tuttavia, utilizzeremo l'ereditarietà per ereditare il costruttore e mostreremo come estendere il costruttore per modificare la funzionalità di una classe genitore.

Per prima cosa, dobbiamo creare un costruttore di base per Animal , che assomiglierà ai costruttori precedenti che abbiamo creato in precedenza. Aggiungi il seguente codice al tuo Animal classe:

public Animal(string animalName)
{
    this.Name = animalName;
}

Come prima, il nostro costruttore imposta il nome dell'animale su ciò che gli passiamo. Tuttavia, questo non è sufficiente perché dobbiamo anche aggiungere un costruttore a entrambi Dog e Cat classi. Lo useremo anche per definire quale suono emette ogni animale.

class Dog : Animal
{
    public Dog(string dogName) : base(dogName)
    {
        this.Sound = "Woof";
    }
}
class Cat : Animal
{
    public Cat(string catName) : base(catName)
    {
        this.Sound = "Meow";
    }
}

Per entrambe le classi, aggiungiamo un costruttore che accetta un Name parametro. Impostiamo anche il Sound di questo oggetto proprietà al suono che vogliamo che l'animale faccia.

Tuttavia, a differenza di prima, ora chiamiamo il costruttore della classe genitore e passiamo il Name parametro dal costruttore padre. Questa chiamata arriva attraverso il : base() metodo e aggiungendo il dogName ricevuto o catName parametri tra parentesi.

Nota:questo : base() la sintassi è unica per i costruttori. Vedremo altri modi per modificare o estendere la funzionalità del caso in seguito.

.
Con questi aggiornamenti al nostro codice, ora possiamo eseguire nuovamente il nostro programma e vedere un risultato simile alla Figura 15.

Figura 15:Gli animali parlano, usando l'ereditarietà

Nota che non abbiamo né un Sound né un Name proprietà definita in Dog o Cat classi. Inoltre non abbiamo un Speak() metodo. Risiedono invece nel genitore Animal classe e il Dog e Cat le classi ereditano queste proprietà e metodi.

Allo stesso modo ora possiamo creare altre classi, per qualsiasi tipo di animale e dovremo solo replicare lo schema del Dog e Cat classi. Ad esempio, potremmo creare queste classi:

class Parrot : Animal
{
    public Parrot(string parrotName)
        : base(parrotName)
    {
        this.Sound = "I want a cracker!";
    }
}
class Pig : Animal
{
    public Pig(string pigName)
        : base(pigName)
    {
        this.Sound = "Oink";
    }
}

Quindi, possiamo creare un'istanza di queste classi in Program.cs:

Parrot myParrot = new Parrot("Polly");
myParrot.Speak();

Pig myPig = new Pig("Bacon");
myPig.Speak();

Ora avremo un vero zoo a portata di mano quando eseguiremo il programma, come mostrato nella Figura 16.

Figura 16:uno zoo di quattro animali che parlano

Puoi anche creare ulteriori classi secondarie di classi figlio esistenti. Ad esempio, potresti separare il Dog classe in Beagle e Pointer o avere il Parrot la classe eredita da un genitore Bird classe che, a sua volta, eredita da Animal . Tuttavia, la creazione di questo tipo di gerarchia va oltre lo scopo di questo articolo, quindi andiamo avanti e finalmente vediamo come possiamo sovrascrivere, modificare o estendere la funzionalità delle classi padre nelle classi figlie.
.

Modifica dell'eredità

Possiamo cambiare il metodo di una classe genitore usando una tecnica chiamata overriding . La sintassi per l'override può differire tra le lingue, ma il principio di cambiare il metodo del genitore nella classe figlia rimane lo stesso.

In C#, aggiungiamo la parola chiave override seguito dalla firma del metodo che vuoi sovrascrivere. Dobbiamo anche dichiarare nella classe genitore che stiamo permettendo ai bambini di sovrascrivere i metodi aggiungendo il virtual parola chiave alla firma del metodo nella classe padre. (Altre lingue potrebbero non richiedere questa indicazione aggiuntiva.)

Nel nostro programma, dobbiamo aggiornare Speak() metodo nel Animal classe.

public virtual void Speak() { Console.WriteLine(this.Name + " says " + this.Sound); }

Ora possiamo inserire il nostro override di Speak() metodo nel Dog classe all'inizio del nostro blocco di codice per questa classe.

class Dog : Animal
{
    public override void Speak()
    {
        base.Speak();
        Console.WriteLine("...and then runs around, chasing his tail");
    }
[remaining code omitted]

Questo codice ci dice che vogliamo sovrascrivere ciò che accade nel metodo genitore Speak() . Eseguiremo comunque il metodo padre usando base.Speak() chiama prima di produrre una linea extra subito dopo.

Perché vorresti farlo? Bene, forse vogliamo che i nostri cani siano tanto entusiasti quanto possono esserlo di persona. Esegui il programma e verifica tu stesso:

Figura 17:Il nostro cane ha la precedenza sul suo metodo di classe

Innanzitutto, Snoopy abbaia normalmente:il base.Speak() il metodo chiama Speak() metodo dal genitore Animal classe. Quindi il resto del codice restituisce una seconda riga alla console. In effetti, estendiamo la classe genitore aggiungendo nuove funzionalità solo alla classe figlia. Guarda come il Cat la classe non è interessata.

Ovviamente, i gatti sono notoriamente difficili da fare come desideri, quindi riflettiamolo nel codice. Aggiorna il Cat class e aggiungi il seguente metodo di override, simile a Dog classe.

class Cat : Animal
{
    public override void Speak()
    {
        Console.WriteLine(Name + " doesn't speak but just sits there wondering when you will feed it.");
    }
[remaining code omitted]

A differenza di quanto abbiamo fatto in Dog override, non chiamiamo base.Speak() metodo questa volta. Senza quella chiamata, non eseguiamo mai Animal Speak() metodo e cambiare completamente ciò che accade in questo metodo. Guarda tu stesso:

Figura 18:Anche il nostro gatto ha la precedenza sul suo metodo di classe

Si noti inoltre che, poiché abbiamo ricostruito le nostre classi, non abbiamo modificato il codice Program.cs. Queste modifiche servono come un buon esempio del perché l'orientamento agli oggetti è una tecnica molto potente; abbiamo cambiato completamente il codice sottostante senza toccare il programma che utilizza quel codice. [L'orientamento agli oggetti ci ha permesso di isolare molti dei dettagli di implementazione e di esporre semplicemente un piccolo insieme di interfacce con cui il programma deve interagire.]

Dove abbiamo piegato le regole

Prima di concludere, voglio sottolineare alcune cose, principalmente alcune cose "cattive" che abbiamo fatto.

Innanzitutto, nelle nostre classi, chiamiamo Console.WriteLine() metodo direttamente. Questo utilizzo non è una buona decisione di progettazione perché le classi dovrebbero essere indipendenti dal modo in cui emettiamo i risultati delle classi.

Un approccio migliore sarebbe invece restituire i dati dalle classi e quindi emetterli come parte del programma. Consentiamo quindi al programma di decidere cosa fare con l'output anziché con le classi, consentendoci di utilizzare le stesse classi in molti tipi diversi di programmi, siano essi pagine Web, Windows Form, QT o applicazioni Console.

In secondo luogo, nei nostri esempi successivi, tutte le proprietà erano public . Rendere pubbliche queste proprietà indebolisce l'aspetto della sicurezza dell'orientamento agli oggetti, in cui cerchiamo di proteggere i dati nelle classi dalla manipolazione dall'esterno. Tuttavia, la protezione di proprietà e metodi diventa rapidamente complessa quando si ha a che fare con l'eredità, quindi volevo evitare questa complicazione, almeno in questa fase introduttiva.

Infine, non ha davvero senso avere Sound impostato come parte del costruttore figlio perché stavamo duplicando anche quel codice. Sarebbe un progetto migliore creare un costruttore nella classe genitore che accettasse sia un nome che un suono, e quindi chiamarlo semplicemente come parte del costruttore figlio. Tuttavia, ancora una volta, per motivi di semplicità, non volevo introdurre firme di costruttori diverse in questa fase.

Quindi, anche se abbiamo imparato molto in questo tutorial bonus, puoi vedere che c'è ancora molto da imparare. Tuttavia, non preoccuparti ancora troppo. Puoi prenderti un momento per congratularti con te stesso per aver seguito tutto il percorso. Se hai seguito tutti i passaggi, hai imparato:

  • Perché utilizziamo l'orientamento agli oggetti
  • Come creare classi
  • Come creare proprietà e metodi
  • Come creare costruttori e cosa fanno
  • Come sfruttare l'ereditarietà e perché è una potente funzionalità dell'orientamento agli oggetti

Spero che questa lezione extra ti sia piaciuta e che tu sia interessato ad esplorarne di più. Assicurati di controllare con noi per nuovi contenuti sul blog Atlantic.Net e considera uno dei nostri server di hosting privato virtuale leader del settore.
.


Linux
  1. Come eseguire il dual-boot di Linux e Windows

  2. Come installare Elasticsearch e Kibana su Linux

  3. Come gestire ed elencare i servizi in Linux

  4. Procedura:replica e configurazione DRBD

  5. Procedura:programmazione socket in Python

Come installare e utilizzare il linguaggio di programmazione R in Ubuntu 20.04 LTS

Come installare e utilizzare il linguaggio di programmazione "R" su Ubuntu

Come installare e configurare Grafana

Procedura:Introduzione alla programmazione:variabili, tipi e manipolazione dei dati

Procedura:Programmazione orientata agli oggetti – Altro con classi e oggetti

Come collegare in rete Ubuntu e Windows 10?