28 - Herencia


Vimos en el concepto anterior que dos clases pueden estar relacionadas por la colaboración. Ahora veremos otro tipo de relaciones entre clases en Visual Basic .Net que es la Herencia.

La herencia significa que se pueden crear nuevas clases partiendo de clases existentes, que tendrá todas los atributos, propiedades y los métodos de su 'superclase' o 'clase padre' y además se le podrán añadir otros atributos, propiedades y métodos propios.

clase padre

Clase de la que desciende o deriva una clase. Las clases hijas (descendientes) heredan (incorporan) automáticamente los atributos, propiedades y métodos de la la clase padre.

Subclase

Clase desciendiente de otra. Hereda automáticamente los atributos, propiedades y métodos de su superclase. Es una especialización de otra clase. Admiten la definición de nuevos atributos y métodos para aumentar la especialización de la clase.

Veamos algunos ejemplos teóricos de herencia:

1) Imaginemos la clase Vehículo. Qué clases podrían derivar de ella?

                            Vehiculo

   Colectivo                Moto                    Auto
                             
                                             FordK        Renault 9

Siempre hacia abajo en la jerarquía hay una especialización (las subclases añaden nuevos atributos, propiedades y métodos)

2) Imaginemos la clase Software. Qué clases podrían derivar de ella?

                                           Software

             DeAplicacion                                        DeBase

ProcesadorTexto       PlanillaDeCalculo                          SistemaOperativo

Word   WordPerfect    Excel     Lotus123                         Linux    Windows       

El primer tipo de relación que habíamos visto entre dos clases, es la de colaboración. Recordemos que es cuando una clase contiene un objeto de otra clase como atributo.
Cuando la relación entre dos clases es del tipo "...tiene un..." o "...es parte de...", no debemos implementar herencia. Estamos frente a una relación de colaboración de clases no de herencia.

Si tenemos una ClaseA y otra ClaseB y notamos que entre ellas existe una relación de tipo "... tiene un...", no debe implementarse herencia sino declarar en la clase ClaseA un atributo de la clase ClaseB.

Por ejemplo: tenemos una clase Auto, una clase Rueda y una clase Volante. Vemos que la relación entre ellas es: Auto "...tiene 4..." Rueda, Volante "...es parte de..." Auto; pero la clase Auto no debe derivar de Rueda ni Volante de Auto porque la relación no es de tipo-subtipo sino de colaboración. Debemos declarar en la clase Auto 4 atributos de tipo Rueda y 1 de tipo Volante.

Luego si vemos que dos clase responden a la pregunta ClaseA "..es un.." ClaseB es posible que haya una relación de herencia.

Por ejemplo:

Auto "es un" Vehiculo
Circulo "es una" Figura
Mouse "es un" DispositivoEntrada
Suma "es una" Operacion

Problema 1:

Ahora plantearemos el primer problema utilizando herencia. Supongamos que necesitamos implementar dos clases que llamaremos Suma y Resta. Cada clase tiene como atributo valor1, valor2 y resultado. Las propiedades a definir son valor1, valor2 y resultado, el método Operar (que en el caso de la clase "Suma" suma los dos Valores y en el caso de la clase "Resta" hace la diferencia entre Valor1 y Valor2.

Si analizamos ambas clases encontramos que muchas propiedades son idénticos. En estos casos es bueno definir una clase padre que agrupe dichas propiedades, atributos y responsabilidades comunes.

La relación de herencia que podemos disponer para este problema es:

                                        Operacion

                        Suma                              Resta
Solamente el método operar es distinto para las clases Suma y Resta (esto hace que no lo podamos disponer en la clase Operacion), luego las propiedades valor1, valor2 son idénticos a las dos clases, esto hace que podamos disponerlos en la clase Operacion. Lo mismo las propiedades valor1, valor2 y Resultado se definirán en la clase padre Operacion.

Crear un proyecto y luego crear tres clases llamadas: Operacion, Suma y Resta:

Programa:

Module Module1

    Public Class Operacion
        Protected _valor1 As Integer
        Protected _valor2 As Integer
        Protected _resultado As Integer

        Public Property valor1() As Integer
            Get
                Return _valor1
            End Get
            Set(ByVal value As Integer)
                _valor1 = value
            End Set
        End Property

        Public Property valor2() As Integer
            Get
                Return _valor2
            End Get
            Set(ByVal value As Integer)
                _valor2 = value
            End Set
        End Property

        Public Property resultado() As Integer
            Get
                Return _resultado
            End Get
            Set(ByVal value As Integer)
                _resultado = value
            End Set
        End Property

    End Class


    Public Class Suma
        Inherits Operacion

        Public Sub Operar()
            resultado = valor1 + valor2
        End Sub

    End Class


    Public Class Resta
        Inherits Operacion

        Public Sub Operar()
            resultado = valor1 - valor2
        End Sub
    End Class

    Sub Main()
        Dim suma1 As New Suma()
        suma1.valor1 = 10
        suma1.valor2 = 7
        suma1.Operar()
        Console.WriteLine("La suma de " & suma1.valor1 & " y " & suma1.valor2 & " es " & suma1.resultado)
        Dim resta1 As New Resta()
        resta1.valor1 = 8
        resta1.valor2 = 4
        resta1.Operar()
        Console.WriteLine("La diferencia de " & resta1.valor1 & " y " & resta1.valor2 & " es " & resta1.resultado)
        Console.ReadKey()
    End Sub

End Module

La clase Operación define tres atributos y sus tres propiedades que las acceden:

    Public Class Operacion
        Protected _valor1 As Integer
        Protected _valor2 As Integer
        Protected _resultado As Integer

        Public Property valor1() As Integer
            Get
                Return _valor1
            End Get
            Set(ByVal value As Integer)
                _valor1 = value
            End Set
        End Property

        Public Property valor2() As Integer
            Get
                Return _valor2
            End Get
            Set(ByVal value As Integer)
                _valor2 = value
            End Set
        End Property

        Public Property resultado() As Integer
            Get
                Return _resultado
            End Get
            Set(ByVal value As Integer)
                _resultado = value
            End Set
        End Property

    End Class


Ya veremos que definimos los atributos con este nuevo modificador de acceso (Protected) para que la subclase tenga acceso a dichos atributos. Si los definimos Private las subclases no pueden acceder a dichos atributos.

Ahora veamos como es la sintaxis para indicar que una clase hereda de otra:

    Public Class Suma
        Inherits Operacion

Disponemos la palabra clave Inherits y seguidamente el nombre de la clase padre (con esto estamos indicando que todas las propiedades de la clase Operación son también propiedades de la clase Suma.

Luego la característica que añade la clase Suma es el siguiente método:

    Public Class Suma
        Inherits Operacion

        Public Sub Operar()
            resultado = valor1 + valor2
        End Sub

    End Class

El método Operar puede acceder a las propiedades heredadas (siempre y cuando los mismos se declaren Protected, en caso que sean Private si bien lo hereda de la clase padre solo los pueden modificar métodos de dicha clase padre)

Ahora podemos decir que la clase Suma tiene tres propiedades y un método.

Luego en el procedimiento Main creamos un objeto de la clase Suma y otro de la clase Resta:

    Sub Main()
        Dim suma1 As New Suma()
        suma1.valor1 = 10
        suma1.valor2 = 7
        suma1.Operar()
        Console.WriteLine("La suma de " & suma1.valor1 & " y " & suma1.valor2 & " es " & suma1.resultado)
        Dim resta1 As New Resta()
        resta1.valor1 = 8
        resta1.valor2 = 4
        resta1.Operar()
        Console.WriteLine("La diferencia de " & resta1.valor1 & " y " & resta1.valor2 & " es " & resta1.resultado)
        Console.ReadKey()
    End Sub

Podemos llamar tanto al método propio de la clase Suma "Operar()" como acceder a las propiedades heredadas de la clase Operacion. Quien utilice la clase Suma solo debe conocer que métodos y propiedades públicas tiene (independientemente que pertenezcan a la clase Suma o a una clase superior)

La lógica es similar para declarar la clase Resta.

La clase Operación agrupa en este caso un conjunto de atributos y propiedades comunes a un conjunto de subclases (Suma, Resta). No tiene sentido definir objetos de la clase Operacion.

El planteo de jerarquías de clases es una tarea compleja que requiere un perfecto entendimiento de todas las clases que intervienen en un problema, cuales son sus atributos, propiedades y responsabilidades.

Problema 2:

Confeccionar una clase Persona que tenga como atributos el nombre y la edad (definir las propiedades para poder acceder a dichos atributos). Definir como responsabilidad un método para imprimir.
Plantear una segunda clase Empleado que herede de la clase Persona. Añadir un atributo sueldo ( y su propiedad) y el método para imprimir su sueldo.
Definir un objeto de la clase Persona y llamar a sus métodos y propiedades. También crear un objeto de la clase Empleado y llamar a sus métodos y propiedades.

Programa:

Module Module1
    Public Class Persona

        Protected _nombre As String
        Protected _edad As Integer

        Public Property nombre() As String
            Set(ByVal value As String)
                _nombre = value
            End Set
            Get
                Return _nombre
            End Get
        End Property

        Public Property edad() As Integer
            Set(ByVal value As Integer)
                _edad = value
            End Set
            Get
                Return _edad
            End Get
        End Property

        Public Sub Imprimir()
            Console.WriteLine("Nombre:" & Nombre)
            Console.WriteLine("Edad:" & edad)
        End Sub
    End Class


    Public Class Empleado
        Inherits Persona
        Protected _sueldo As Single

        Public Property sueldo() As Single
            Set(ByVal value As Single)
                _sueldo = value
            End Set
            Get
                Return _sueldo
            End Get
        End Property

        Public Overloads Sub Imprimir()
            MyBase.Imprimir()
            Console.WriteLine("Sueldo:" & sueldo)
        End Sub
    End Class


    Sub Main()
        Dim persona1 As New Persona()
        persona1.nombre = "Juan"
        persona1.edad = 25
        Console.WriteLine("Los datos de la persona son:")
        persona1.Imprimir()

        Dim empleado1 As New Empleado()
        empleado1.Nombre = "Ana"
        empleado1.Edad = 42
        empleado1.Sueldo = 2524
        Console.WriteLine("Los datos del empleado son:")
        empleado1.Imprimir()

        Console.ReadKey()

    End Sub

End Module

La clase Persona define los atributos protegidos (Protected) _nombre y _edad. Luego las propiedades públicas nombre y edad (que acceden a los atributos para modificarlos o consultar sus valores)

El método imprimir es público para que se lo pueda llamar desde donde definimos un objeto de esta clase.

    Public Class Persona

        Protected _nombre As String
        Protected _edad As Integer

        Public Property nombre() As String
            Set(ByVal value As String)
                _nombre = value
            End Set
            Get
                Return _nombre
            End Get
        End Property

        Public Property edad() As Integer
            Set(ByVal value As Integer)
                _edad = value
            End Set
            Get
                Return _edad
            End Get
        End Property

        Public Sub Imprimir()
            Console.WriteLine("Nombre:" & Nombre)
            Console.WriteLine("Edad:" & edad)
        End Sub
    End Class

La clase Empleado hereda de la clase Persona y agrega un atributo llamado _sueldo y la respectiva propiedad sueldo para acceder al atributo. Como la clase Empleado define otro método Imprimir debemos anteceder la palabra clave Overloads (con esto indicamos que sobreescribimos el método existente en la clase padre)
Para llamar desde el método imprimir de la clase Empleado al método imprimir de la clase Persona es con la sintaxis MyBase.Imprimir()

    Public Class Empleado
        Inherits Persona
        Protected _sueldo As Single

        Public Property sueldo() As Single
            Set(ByVal value As Single)
                _sueldo = value
            End Set
            Get
                Return _sueldo
            End Get
        End Property

        Public Overloads Sub Imprimir()
            MyBase.Imprimir()
            Console.WriteLine("Sueldo:" & sueldo)
        End Sub
    End Class

Por último en la Main creamos un objeto de la clase Persona y un objeto de la clase Empleado:

    Sub Main()
        Dim persona1 As New Persona()
        persona1.nombre = "Juan"
        persona1.edad = 25
        Console.WriteLine("Los datos de la persona son:")
        persona1.Imprimir()

        Dim empleado1 As New Empleado()
        empleado1.Nombre = "Ana"
        empleado1.Edad = 42
        empleado1.Sueldo = 2524
        Console.WriteLine("Los datos del empleado son:")
        empleado1.Imprimir()

        Console.ReadKey()

    End Sub

Retornar