GNU/Linux >> Tutoriales Linux >  >> Linux

Cómo:programación orientada a objetos:constructores y herencia

Anteriormente

¡Ya casi hemos llegado al final de nuestro viaje a través de esta ronda de bonificación de nuestra Introducción a la programación orientada a objetos! En nuestro último artículo, comenzamos nuestro ejemplo del zoológico de mascotas con una revisión de clases, objetos y métodos. Si se perdió el comienzo de esta serie, puede volver a ponerse al día con nosotros aquí. De lo contrario, ¡volvamos a sumergirnos!


.

Constructores

Si hiciste el ejercicio con dos Dog objetos, fue un poco aburrido, ¿verdad? Después de todo, no tenemos nada que separe a los perros entre sí y no hay forma de saber, sin mirar el código fuente, qué perro produjo qué ladrido.

En el artículo anterior, mencioné que cuando creas objetos, llamas a un método especial llamado constructor . El constructor se parece al nombre de la clase escrito como método. Por ejemplo, para un Dog class, el constructor se llamaría Dog() .

Lo especial de los constructores es que son la ruta a cualquier objeto nuevo, por lo que son un excelente lugar para llamar al código que inicializa un objeto con valores predeterminados. Además, el valor de retorno de un método constructor siempre es un objeto de la propia clase, por lo que podemos asignar el valor de retorno del constructor a una variable del tipo de clase que creamos.

Sin embargo, hasta ahora, no hemos creado un constructor en absoluto, entonces, ¿cómo es que todavía podemos llamar a ese método?

En muchos lenguajes, incluido C#, el lenguaje le brinda un constructor gratuito y vacío sin que tenga que hacer nada. Se da a entender que desea un constructor; de lo contrario, no habría forma de usar la clase para nada, por lo que los lenguajes simplemente asumen que ha escrito una.

Este constructor invisible y gratuito se denomina constructor predeterminado y, en nuestro ejemplo, se verá así:

public Dog(){ }

Tenga en cuenta que esta sintaxis es muy similar a Speak() método que creamos anteriormente, excepto que no devolvemos explícitamente un valor ni declaramos el tipo de retorno del método. Como mencioné anteriormente, un constructor siempre devuelve una instancia de la clase a la que pertenece.

En este caso, esa es la clase Dog , y por eso cuando escribimos Dog myDog = new Dog() , podemos asignar el nuevo objeto a una variable llamada myDog que es de tipo Dog .

Así que agreguemos el constructor predeterminado a nuestro Dog clase. Puede copiar la línea de arriba o, en Visual Studio, puede usar un atajo:escriba ctor y presiona Tab dos veces. Debería generar el constructor predeterminado para usted, como se muestra en la Figura 9:

Figura 9:Agregar un constructor con 'ctor'

El constructor predeterminado en realidad no nos da nada nuevo porque ahora está haciendo explícitamente lo que antes se hacía implícitamente. Sin embargo, es un método, por lo que ahora podemos agregar contenido dentro de los corchetes que se ejecutará cada vez que llamemos a este constructor. Y debido a que el constructor se ejecuta como lo primero en la construcción de un objeto, es un lugar perfecto para agregar código de inicialización.

Por ejemplo, podríamos establecer el Name propiedad de nuestros objetos a algo agregando código como este:

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

Este ejemplo establecerá el Name propiedad de cualquier objeto nuevo para "Snoopy".

Por supuesto, eso no es muy útil porque no todos los perros se llaman "Snoopy", así que en su lugar, cambiemos la firma del método del constructor para que acepte un parámetro.

Los paréntesis de los métodos no solo están ahí para verse bonitos; sirven para contener parámetros que podemos usar para pasar valores a un método. Esta función se aplica a todos los métodos, no solo a los constructores, pero hagámoslo primero para un constructor.

Cambie la firma del constructor por defecto a esto:

public Dog(string dogName)

Esta adición nos permite enviar una string parámetro en el constructor, y cuando lo hacemos, podemos referirnos a ese parámetro por el nombre dogName .

Luego, agregue la siguiente línea al bloque de método:

this.Name = dogName;

Esta línea establece la propiedad de este objeto Name al parámetro que enviamos al constructor.

Tenga en cuenta que cuando cambia la firma del constructor, obtiene un caso de los garabatos rojos en su archivo Program.cs, como se muestra en la Figura 10.

Figura 10:Un caso de los garabatos rojos de nuestro nuevo constructor

Cuando agregamos nuestros propios constructores explícitos, C# y .NET no crearán implícitamente un constructor predeterminado para nosotros. En nuestro archivo Program.cs, todavía estamos creando el Dog objetos utilizando el constructor sin parámetros predeterminado, que ahora ya no existe.

Para solucionar este problema, debemos agregar un parámetro a nuestra llamada de constructor en Program.cs. Podemos, por ejemplo, actualizar nuestra línea de construcción de objetos como tal:

Perro miPerro =nuevo Perro(“Snoopy”);

Si lo hace, eliminará los garabatos rojos y le permitirá ejecutar el código nuevamente. Si deja o establece su punto de interrupción después de la última línea de código, puede mirar el panel Locales y verificar que el Name de su objeto de hecho, se ha establecido la propiedad, como se muestra en la Figura 11.

Figura 11:Ver nuestra propiedad de nombre configurada correctamente

¿Entiendo? ¡Bueno! Ahora tenemos la capacidad de nombrar a nuestro perro, pero en realidad no es un programa útil si la gente tiene que depurarlo para ver lo que estamos haciendo. Así que mezclemos un poco el código para asegurarnos de mostrar el nombre del perro que está ladrando.

Actualiza tu Dog objetos y cambie el Speak() método como tal:

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

El cambio que hemos hecho ahora le dice a WriteLine método para concatenar el nombre de este objeto con la cadena literal "dice:Guau", lo que debería darnos una salida que muestre mejor nuestros esfuerzos. Intente ejecutar el programa ahora y debería ver algo parecido a la Figura 12.

Figura 12:Snoopy dice:Guau

¡Buen trabajo! Ahora puedes crear muchos perros con diferentes nombres, y todos ladrarán a tus órdenes.

Por supuesto, un zoológico de mascotas solo con perros es algo aburrido. Quiero decir, me encantan los perros, pero tal vez podríamos agregar algunos animales adicionales para animar un poco la experiencia.
.

Herencia

Comencemos agregando un nuevo Cat class a nuestro archivo Animals.cs. Agregue el siguiente código junto a la clase Perro, pero aún dentro de los corchetes del espacio de nombres de PettingZoo:

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

Luego, creemos un Cat objeto en Program.cs y haz que hable:

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

Ejecutar este programa ahora debería proporcionarle un resultado similar al de la Figura 13.

Figura 13:Garfield dice:Miau

Nuestro programa es predecible y funciona, pero ¿se dio cuenta de lo similares que son realmente las dos clases? ¿Ves cuánto código hemos duplicado en las dos clases? ¿No se suponía que la orientación a objetos nos salvaría de escribir código varias veces?

La respuesta es sí. Podemos, y debemos, simplificar este código para reducir la cantidad de duplicación. Lo que estamos a punto de abordar demostrará una característica de OO llamada herencia .

La herencia en la orientación a objetos le permite crear una jerarquía de clases y hacer que cada clase herede las propiedades y métodos de una clase principal. Por ejemplo, para nuestro zoológico interactivo, podemos crear un padre Animal clase y tener Dog y Cat heredar de esa clase. Luego, podemos mover partes de nuestro código al Animal class para que tanto el Dog y Cat las clases heredarán ese código.

Sin embargo, si movemos las partes duplicadas de nuestro código a este Animal clase, luego el Dog y Cat cada una de las clases heredaría las mismas propiedades y métodos, lo que haría que los hijos fueran clones de las clases principales. Esta organización no es muy útil porque queremos tener distintos tipos de animales. Sería terriblemente aburrido si los gatos, los perros y todos los demás animales fueran iguales. De hecho, para los propósitos de nuestro programa, no tendría sentido llamarlos de diferentes maneras.

Ahora, si la herencia significa que solo podemos crear clases secundarias que sean idénticas a sus clases principales, no tendría mucho sentido hacer todo este esfuerzo. Entonces, aunque heredamos todas las partes de una clase principal, también podemos anular ciertas partes de una clase principal en las clases secundarias para hacer que las clases secundarias sean distintas.

Analicemos esto y veamos cómo funciona, ¿de acuerdo?
.

Creación de clases para padres e hijos

Vamos a dar unos pasos atrás y recrear el Dog y Cat clases de una mejor manera. Para empezar, necesitamos una clase principal que defina las propiedades y los métodos compartidos de las clases secundarias.

En su archivo Animals.cs, elimine el Dog y Cat clases y agregue la siguiente definición de clase:

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

No sorprende que esta clase se parezca al anterior Dog y Cat clases, con las excepciones señaladas que hemos hecho públicas todas las propiedades y métodos y que hemos introducido una nueva propiedad llamada Sound de tipo string . Finalmente, hemos actualizado el Speak método para usar el Sonido variables.

En este punto, su Program.cs no funcionará y, si hace clic en esa pestaña, debería ver varios mensajes de error, lo cual es natural porque hemos eliminado el Cat y Dog clases Recreémoslos y hagámoslo usando la herencia. En su archivo Animals.cs, justo después del Animal clase, agregue el siguiente código:

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

Estas nuevas clases son mucho más cortas que antes y no replican ningún código. La nueva sintaxis aquí es dos puntos seguidos del nombre de la clase Animal , que le dice a C# que queremos ambos Dog y Cat heredar del Animal clase. En efecto, Dog y Cat convertirse en clases secundarias de Animal como se ilustra en el diagrama que se muestra en la Figura 14.

Figura 14:Diagrama de herencia de clases de animales

Nota:he estado usando el término Propiedad hasta ahora, lo cual es un poco inexacto porque el término correcto es campo como puede ver en el diagrama de la Figura 14. Sin embargo, el campo es menos claro fuera del alcance de la programación, por lo que he usado Property en su lugar. Técnicamente, una propiedad es un contenedor alrededor de un campo en C#.

.
Todavía tenemos un problema con nuestro archivo Program.cs, sin embargo, debido al constructor personalizado que creamos. Si intenta ejecutar o depurar nuestro programa, debería ver un mensaje de error que dice que "'PettingZoo.Cat' no contiene un constructor que toma 1 argumentos" y uno similar para "PettingZoo.Dog".

Para corregir este error, necesitamos volver a agregar los constructores. Esta vez, sin embargo, vamos a utilizar la herencia para heredar el constructor y mostrar cómo puede extender el constructor para cambiar la funcionalidad de una clase principal.

Primero, necesitamos crear un constructor base para Animal , que se parecerá a los constructores anteriores que creamos anteriormente. Agrega el siguiente código a tu Animal clase:

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

Como antes, nuestro constructor establece el nombre del animal a lo que le pasamos. Sin embargo, esto no es suficiente porque también necesitamos agregar un constructor tanto al Dog y Cat clases Usaremos esto para definir qué sonido hace cada animal también.

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

Para ambas clases, agregamos un constructor que acepta un Name parámetro. También configuramos el Sound de este objeto propiedad al sonido que queremos que haga el animal.

Sin embargo, a diferencia de antes, ahora llamamos al constructor de la clase padre y le pasamos el Name parámetro del constructor padre. Esta llamada viene a través del : base() y agregando el dogName recibido o catName parámetros dentro de los paréntesis.

Nota:Este : base() La sintaxis es exclusiva de los constructores. Más adelante veremos otras formas de cambiar o ampliar la funcionalidad de los casos.

.
Con estas actualizaciones a nuestro código, ahora podemos ejecutar nuestro programa nuevamente y ver un resultado similar a la Figura 15.

Figura 15:Los animales hablan, usando la herencia

Tenga en cuenta que no tenemos ni un Sound ni un Name propiedad definida en el Dog o Cat clases Tampoco tenemos un Speak() método. En su lugar, residen en el padre Animal clase, y el Dog y Cat las clases heredan estas propiedades y métodos.

De manera similar podemos crear otras clases ahora, para cualquier tipo de animal y solo necesitaremos replicar el patrón del Dog y Cat clases Por ejemplo, podríamos crear estas clases:

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";
    }
}

Luego, podemos instanciar estas clases en Program.cs:

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

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

Ahora tendremos un verdadero zoológico en nuestras manos cuando ejecutemos el programa, como se muestra en la Figura 16.

Figura 16:Un zoológico de cuatro animales hablando

También puede crear más clases secundarias de clases secundarias existentes. Por ejemplo, podrías separar el Dog clase en Beagle y Pointer o tener el Parrot la clase hereda de un padre Bird clase que, a su vez, hereda de Animal . Sin embargo, la creación de este tipo de jerarquía está más allá del alcance de este artículo, así que avancemos y finalmente veamos cómo podemos anular, cambiar o ampliar la funcionalidad de las clases principales en las clases secundarias.
.

Modificación de la herencia

Podemos cambiar el método de una clase principal usando una técnica llamada anular . La sintaxis para anular puede diferir entre idiomas, pero el principio de cambiar el método principal en la clase secundaria sigue siendo el mismo.

En C#, agregamos la palabra clave override seguido de la firma del método que desea anular. También debemos declarar en la clase principal que estamos permitiendo que los niños anulen los métodos agregando el virtual palabra clave a la firma del método en la clase principal. (Es posible que otros idiomas no requieran esta indicación adicional).

En nuestro programa, necesitamos actualizar Speak() método en el Animal clase.

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

Ahora podemos insertar nuestra anulación de Speak() método en el Dog clase al comienzo de nuestro bloque de código para esta clase.

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

Este código nos dice que queremos anular lo que sucede en el método padre Speak() . Todavía ejecutaremos el método padre usando base.Speak() llame antes de que emitamos una línea adicional justo después.

¿Por qué querrías hacerlo? Bueno, tal vez queremos que nuestros perros sean tan entusiastas como pueden ser en persona. Ejecute el programa y compruébelo usted mismo:

Figura 17:Nuestro perro anula su método de clase

Primero, Snoopy ladra normalmente:el base.Speak() método llama al Speak() método del padre Animal clase. Luego, el resto del código genera una segunda línea en la consola. Efectivamente, ampliamos la clase principal agregando nuevas funciones solo a la clase secundaria. Mira cómo el Cat la clase no se ve afectada.

Por supuesto, los gatos son notoriamente difíciles de conseguir para hacer lo que deseas, así que reflejemos eso en el código. Actualiza el Cat class y agregue la siguiente anulación del método, similar a Dog clase.

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 diferencia de lo que hicimos en el Dog override, no llamamos al base.Speak() método esta vez. Sin esa llamada, nunca ejecutamos el Animal Speak() método y cambiar completamente lo que sucede en este método. Compruébelo usted mismo:

Figura 18:Nuestro gato también anula su método de clase

También tenga en cuenta que como hemos reconstruido nuestras clases, no hemos cambiado el código de Program.cs. Estos cambios sirven como un buen ejemplo de por qué la orientación a objetos es una técnica muy poderosa; hemos cambiado completamente el código subyacente sin tocar el programa que usa ese código. [La orientación a objetos nos ha permitido aislar muchos de los detalles de implementación y simplemente exponer un pequeño conjunto de interfaz con el que el programa necesita interactuar.]

Donde doblamos las reglas

Antes de terminar, quiero señalar algunas cosas, principalmente algunas cosas "malas" que hemos hecho.

Primero, en nuestras clases, llamamos a Console.WriteLine() método directamente. Este uso no es una buena decisión de diseño porque las clases deben ser independientes de cómo generamos los resultados de las clases.

Un mejor enfoque sería devolver datos de las clases y luego generarlos como parte del programa. Luego permitiríamos que el programa decida qué hacer con la salida en lugar de las clases, permitiéndonos usar las mismas clases en muchos tipos diferentes de programas, ya sean páginas web, Windows Forms, QT o aplicaciones de Consola.

En segundo lugar, en nuestros ejemplos posteriores, todas las propiedades eran public . Hacer públicas estas propiedades debilita el aspecto de seguridad de la orientación a objetos, donde buscamos proteger los datos en las clases de la manipulación desde el exterior. Sin embargo, la protección de propiedades y métodos se vuelve rápidamente compleja cuando se trata de herencia, por lo que quería evitar esa complicación, al menos en esta etapa introductoria.

Finalmente, realmente no tiene sentido tener Sound establecido como parte del constructor secundario porque también estábamos duplicando ese código. Sería un mejor diseño crear un constructor en la clase principal que aceptara tanto un nombre como un sonido, y luego simplemente llamarlo como parte del constructor secundario. Sin embargo, nuevamente, en aras de la simplicidad, no quería presentar firmas de constructores diferentes en esta etapa.

Entonces, si bien hemos aprendido mucho en este tutorial adicional, puede ver que aún hay más por aprender. Sin embargo, no te preocupes demasiado por eso todavía. Puedes tomarte un momento para felicitarte por haber seguido todo el camino. Si has seguido todos los pasos, has aprendido:

  • Por qué usamos la orientación a objetos
  • Cómo crear clases
  • Cómo crear propiedades y métodos
  • Cómo crear constructores y qué hacen
  • Cómo aprovechar la herencia y por qué es una característica poderosa de la orientación a objetos

Espero que haya disfrutado de esta lección adicional y esté interesado en explorar más. Asegúrese de volver a consultarnos si hay contenido nuevo en el blog de Atlantic.Net y considere uno de nuestros servidores de alojamiento privado virtual líderes en la industria.
.


Linux
  1. Cómo hacer un arranque dual de Linux y Windows

  2. Cómo instalar Elasticsearch y Kibana en Linux

  3. Cómo administrar y enumerar servicios en Linux

  4. Cómo:replicación y configuración de DRBD

  5. Cómo:Programación de sockets en Python

Cómo instalar y usar el lenguaje de programación R en Ubuntu 20.04 LTS

Cómo instalar y usar el lenguaje de programación 'R' en Ubuntu

Cómo instalar y configurar Grafana

Cómo:Introducción a la programación:variables, tipos y manipulación de datos

Cómo:Programación orientada a objetos:más con clases y objetos

¿Cómo conectar en red Ubuntu y Windows 10?