27 - Concepto de propiedad


Muchos lenguajes de programación orientado a objetos acceden a sus atributos a través de métodos. Esto lo vimos en el concepto anterior cuando accedíamos al atributo monto de un cliente:

        Public Sub Depositar(ByVal m As Integer)
            monto = monto + m
        End Sub

        Public Function RetornarMonto() As Integer
            Return monto
        End Function

Vimos que luego llamamos a dichos métodos con la sintaxis:

  cliente3.Depositar(200)
  cliente3.Extraer(150)

En Visual Basic .Net normalmente este tipo de problemas se lo resuelve implementado una propiedad. Veamos el mismo problemas resolviéndolo utilizando propiedades.

Problema 1:

El problema era : Un banco tiene 3 clientes que pueden hacer depósitos y extracciones. También el banco requiere que al final del día calcule la cantidad de dinero que hay depositada.

Programa:

Module Module1
    Class Cliente

        Private _nombre As String
        Private _monto As Integer

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

        Public Property monto() As Integer
            Get
                Return _monto
            End Get
            Set(ByVal value As Integer)
                _monto = value
            End Set
        End Property

        Public Sub Imprimir()
            Console.WriteLine(nombre & " tiene depositado la suma de " & monto)
        End Sub

    End Class


    Public Class Banco

        Private cliente1, cliente2, cliente3 As Cliente

        Public Sub New()
            cliente1 = New Cliente()
            cliente1.nombre = "Juan"
            cliente1.monto = 0
            cliente2 = New Cliente()
            cliente2.nombre = "Ana"
            cliente2.monto = 0
            cliente3 = New Cliente()
            cliente3.nombre = "Pedro"
            cliente3.monto = 0
        End Sub

        Public Sub Operar()
            cliente1.monto = cliente1.monto + 100
            cliente2.monto = cliente2.monto + 150
            cliente3.monto = cliente3.monto + 200
        End Sub

        Public Sub DepositosTotales()
            Dim t As Integer = cliente1.monto + cliente2.monto + cliente3.monto
            Console.WriteLine("El total de dinero en el banco es:" & t)
            cliente1.Imprimir()
            cliente2.Imprimir()
            cliente3.Imprimir()
        End Sub
    End Class


    Sub Main()
        Dim banco1 = New Banco()
        banco1.Operar()
        banco1.DepositosTotales()
        Console.ReadKey()
    End Sub

End Module

Lo más importante es entender que una propiedad es una forma de acceder al contenido de un atributo, tanto para consultar su valor como modificarlo.


    Class Cliente

        Private _nombre As String
        Private _monto As Integer

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

        Public Property monto() As Integer
            Get
                Return _monto
            End Get
            Set(ByVal value As Integer)
                _monto = value
            End Set
        End Property

Los atributos los llamamos _nombre y _monto. Las propiedades las llamamos nombre y monto. Las propiedades no almacenan valores sino que normalmente acceden a los atributos para modificarlos o consultarlos.

La propiedad Nombre mediante el modificador set inicializa el atributo _nombre con el valor que llega del objeto:

            cliente1.nombre = "Juan"

Como vemos donde definimos el objeto cliente1 accedemos a la propiedad mediante el operador punto y le asignamos un valor (en este caso un String porque la propiedad es de tipo String)

Si queremos consultar el atributo nombre lo podemos hacer mediante la propiedad Nombre. Es común definir el nombre que le damos al atributo con el mismo nombre que tiene la propiedad pero agregando el caracter _:

        'atributo _monto
        Private _monto As Integer

        'propiedad monto
        Public Property monto() As Integer
            Get
                'valor devuelto por la propiedad
                Return _monto
            End Get
            Set(ByVal value As Integer)
                 'value indica el valor que se le asigna a la propiedad
                _monto = value
            End Set
        End Property

Podemos observar que la sintaxis para acceder a las propiedades donde definimos objetos es mucho mas intuitiva y sencillas, por ejemplo para saber cuanto dinero hay en el banco la sintaxis con propiedades es:

            Dim t As Integer = cliente1.monto + cliente2.monto + cliente3.monto

Y como la vimos anteriormente por medio de un método que retorna el monto tenemos la siguiente sintaxis:

            Dim t As Integer = cliente1.RetornarMonto() +
                               cliente2.RetornarMonto() +
                               cliente3.RetornarMonto()

Lo primero que nos viene a la mente es porque no definir los atributos con el modificador Public :

        Public _monto as Integer

Para luego poder consultarlos y/o modificarlos con la sintaxis:

            dim t as Integer = cliente1._monto + cliente2._monto + cliente3._monto

Ahora veamos que cuando consultamos o inicializamos una propiedad en realidad lo que está sucediendo es la ejecución de un método (Set o Get) donde podemos disponer código donde validar el valor asignado. Por ejemplo si disponemos la restricción que el Monto siempre debe ser positivo para que se almacene, luego debemos codificar la propiedad con la siguiente sintaxis:

        Public Property monto() As Integer
            Get
                Return _monto
            End Get
            Set(ByVal value As Integer)
                If (value > 0) Then
                    _monto = value
                Else
                    Console.WriteLine("No se puede tener un monto negativo.")
                End If
            End Set
        End Property

Es decir si el valor que le asignamos a la propiedad Monto es negativo luego no se inicializa el atributo _monto con dicho valor.

Si ejecutamos este código luego debe mostrar un mensaje indicando que "No se puede tener monto negativo":

 cliente1.Monto = -100

Problema 2:

Plantear un programa que permita jugar a los dados. Las reglas de juego son: se tiran tres dados si los tres salen con el mismo valor mostrar un mensaje que "gano", sino "perdió".

Programa:

Module Module1
    Public Class Dado

        Private _valor As Integer

        Public Property valor() As Integer
            Get
                Return _valor
            End Get
            Private Set(value As Integer)
                _valor = value
            End Set
        End Property

        Private Shared aleatorio As Random

        Public Sub New()
            aleatorio = New Random()
        End Sub

        Public Sub Tirar()
            valor = aleatorio.Next(1, 7)
        End Sub

        Public Sub Imprimir()
            Console.WriteLine("El valor del dado es:" & valor)
        End Sub

    End Class


    Public Class JuegoDeDados
        Private dado1, dado2, dado3 As Dado

        Public Sub New()
            dado1 = New Dado()
            dado2 = New Dado()
            dado3 = New Dado()
        End Sub

        Public Sub Jugar()
            dado1.Tirar()
            dado1.Imprimir()
            dado2.Tirar()
            dado2.Imprimir()
            dado3.Tirar()
            dado3.Imprimir()
            If dado1.valor = dado2.valor And dado1.valor = dado3.valor Then
                Console.WriteLine("Ganó")
            Else
                Console.WriteLine("Perdió")
            End If
            Console.ReadKey()
        End Sub
    End Class


    Sub Main()
        Dim j = New JuegoDeDados()
        j.Jugar()
    End Sub

End Module

El atributo _valor se lo accede por medio de la propiedad valor:

        Private _valor As Integer

        Public Property valor() As Integer
            Get
                Return _valor
            End Get
            Set(value As Integer)
                _valor = value
            End Set
        End Property

Luego cuando queremos consultar el valor del dado desde el jugo de dados por medio de la sintaxis siguiente podemos comparar si los tres dados tienen el mismo número:

            If dado1.valor = dado2.valor And dado1.valor = dado3.valor Then
                Console.WriteLine("Ganó")
            Else
                Console.WriteLine("Perdió")
            End If

Algo importante es poder restringir la ejecución del set o get desde fuera de la clase, por ejemplo en este caso queremos evitar que desde la clase JuegoDeDados se puede cambiar el valor del dado con la siguiente sintaxis:

    dado1.Valor=7

La línea anterior provocará un error ya que sección del set de la propiedad la hemos definido de tipo Private (con esto hacemos que solo los métodos de la clase puedan ejecuta el Set). La sintaxis para acceder a la propiedad Valor desde la clase es:

        Public Sub Tirar()
            valor = aleatorio.Next(1, 7)
        End Sub

Esto es correcto ya que el método Tirar pertenece a la clase Dado y por lo tanto puede asignarle un dato a la propiedad valor (cuando se asigna un valor a una propiedad se ejecuta el Set)

Acotaciones.

En las últimas versiones de Visual Studio .Net podemos generar el código fuente de una propiedad en forma automática, para ello disponemos el cursor sobre el atributo de la clase y seleccionamos del lado izquierdo la opción:

generar propiedad en forma automática Visual Basic .Net con el visual Studio

Luego de esto tenemos generado en forma automática el código fuente de la definición de la propiedad:

generar propiedad en forma automática Visual Basic .Net con el visual Studio

Podemos luego modificar el código generado y agregar validaciones por ejemplo.

Problemas propuestos

  1. Plantear una clase Club y otra clase Socio.
    La clase Socio debe tener los siguientes atributos privados: nombre y la antigüedad en el club (en años) Definir dos propiedades para poder acceder al nombre y la antigüedad del socio(no permitir cargar un valor negativo en la antigüedad). La clase Club debe tener como atributos 3 objetos de la clase Socio. Definir una responsabilidad para imprimir el nombre del socio con mayor antigüedad en el club.
Solución
Module Module1
    Public Class Socio

        Private _nombre As String
        Private _antiguedad 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 Antiguedad() As Integer
            Set(ByVal value As Integer)
                If value >= 0 Then
                    _antiguedad = value
                Else
                    Console.Write("No se puede asignar aun valor negativo a la antiguedad")
                End If
            End Set
            Get
                Return _antiguedad
            End Get
        End Property

    End Class


    Public Class Club

        Private socio1, socio2, socio3 As Socio

        Public Sub New()
            socio1 = New Socio()
            socio1.Nombre = "Juan"
            socio1.Antiguedad = 7
            socio2 = New Socio()
            socio2.Nombre = "Ana"
            socio2.Antiguedad = 3
            socio3 = New Socio()
            socio3.Nombre = "Martin"
            socio3.Antiguedad = 25
        End Sub

        Public Sub MayorAntiguedad()
            If socio1.Antiguedad > socio2.Antiguedad And
                socio1.Antiguedad > socio3.Antiguedad Then
                Console.WriteLine("Socio com mayor antiguedad:" & socio1.Nombre)
            Else
                If socio2.Antiguedad > socio3.Antiguedad Then
                    Console.WriteLine("Socio com mayor antiguedad:" & socio2.Nombre)
                Else
                    Console.WriteLine("Socio com mayor antiguedad:" & socio3.Nombre)
                End If
            End If
        End Sub
    End Class


    Sub Main()
        Dim club1 As New Club()
        club1.MayorAntiguedad()
        Console.ReadKey()
    End Sub

End Module

Retornar