Una colección para tus nombres-Valor


Siguiendo con la biblioteca de útiles, veamos ahora de tener una clase que nos permita almacenar y manipular varias instancias de la clase NameValue de la publicación pasada.

Personalizando una lista

Esto en realidad, puede ser tan sencillo como crear una clase que herede de la genérica List. Sin embargo, para hacerla debemos definir de que clase es esa lista y, como vimos anteriormente, estamos definiendo diferentes clases, de acuerdo al tipo de dato a almacenar.

Para ello, viene  en nuestra ayuda la interfaz INameValue.

Public Class NameValueList
    Inherits List(Of INameValue)

Y ya la tenemos. Sonrisa

Sin embargo, podríamos agregar algunas funcionalidades como el agregado de un elemento, dependiendo del tipo de dato.

Esto permitiría tener un método genérico que “traduzca” tipos de datos, por ejemplo, cuando necesitamos transformar desde otros entornos (como datos venidos desde bases de datos)

Automatizando el agregado de elementos

Definamos pues, un método que cree una instancia de la clase NameValue, y le asigne el valor recibido:

Private Function CreateNameValue(
                    name As String,
                    value As Object,
                    type As Type) As INameValue

Como vemos, el valor se recibe como de tipo objeto, que admitirá cualquier valor, y en parámetro independiente, el tipo deseado.

Lo que retorna la función es la interfaz genérica INameValue.

Internamente, la función define una variable para crear el valor de retorno, y obtenemos, como cadena de caracteres, el nombre del tipo a utilizar.

        Dim retvalue As INameValue
        Dim sName As String = type.Name

Luego, seleccionamos basados en dicho nombre, para crear la instancia a retornar, utilizando la sentencia Select Case

Select Case sName
    Case "BigInt"
        retvalue =
            New NameValue(Of Int64)(name)
    Case "Binary"
        retvalue =
            New NameValue(Of Byte())(name)
    Case "Bit"
        retvalue =
            New NameValue(Of Boolean)(name)

(la lista es bastante más larga).

Una ventaja de la sentencia Select es que nos permite ejecutar una acción, al seleccionar uno de varios valores en la misma sentencia. Así, por ejemplo, para los valores de cadenas de caracteres, podemos agruparlos en un solo case. Además, en el ejemplo, vemos que no solo usamos los tipos de datos propios de .Net, sino también, otros como VarChar, NVarchar, Text, que son propios de bases de datos:

Case "NChar",
        "NText",
        "NVarChar",
        "Text",
        "VarChar",
        "Xml",
        "String"
    retvalue =
        New NameValue(Of String)(name)

Ante la ocurrencia no cubierta, mostrar el error.

Aún cuando queramos ser muy detallistas, es factible que no contemplemos todos los tipos posibles. Por ello, si nos encontramos ante esa situación, es importante informarnos de ello con una excepción específica. Cuando ninguna de las opciones de selección ocurre, (el caso diferente), procedemos a ello:

Case Else
   Debug.WriteLine(
      String.Format(
         "Case ""{0}",type.ToString))
   Debug.WriteLine(
      String.Format(
         "retvalue = New NameValue(Of {0})(name)",
         type.ToString))
   Throw New NotImplementedException(
      String.Format(
         "Missing type={0}",
         type.ToString)
      )

Pero también existen los tipos que admiten valores nulos.

Como es posible recibir de esos tipos de datos, debiéramos contemplar también esa posibilidad.

para ello, detectamos si el tipo admite nulos (es Nullable), y utilizamos un select similar, para dichos valores, dejando el ya definido, para los que no lo admiten, encapsulando esta decisión, obviamente, con una sentencia If.

        Dim isNullable As Boolean =
            type.FullName.StartsWith("System.Nullable")
        If isNullable Then
            'Get the base type Name
            Dim splitter = Split(type.FullName, "[")
            splitter = splitter(2).Split(",")
            sName = splitter(0).Replace("System.", "")
        End If
        If isNullable Then

Exponiendo la creación de nuevos elementos.

Todo este procedimiento lo hemos incluido en un miembro privado, solo visible dentro de la propia clase, para crear el nuevo elemento. Definamos pues un método visible externamente que además de crearlo, lo agregue a la lista.

    Public Sub AddElement(
                name As String,
                value As Object,
                type As Type)
        Me.Add(
            CreateNameValue(
                name,
                value,
                type)
            )
    End Sub

Obteniendo un valor por su nombre.

La clase base List solo es capaz de retornar un elemento específico por su índice pero no por un literal (lo cual, por cierto, si puede hacerlo un Dictionary).

Pero elegimos List para que fuese más liviano y además, fácilmente serializable.

Además, perfectamente podemos implementar el método que nos retorne el valor por el nombre, con el siguiente código.

    Public Function GetByName(
                        name As String) As INameValue
        Return (
            From el
            In Me
            Where
                el.Name.ToUpper =
                name.ToUpper
                ).FirstOrDefault
    End Function

Eehhh si, estoy usando LinQ. (ya voy a publicar algo específico de esto enseguida) Sonrisa

Y así quedaría el código completito

Public Class NameValueList
    Inherits List(Of INameValue)
    Public Sub AddElement(
                name As String,
                value As Object,
                type As Type)
        Me.Add(
            CreateNameValue(
                name,
                value,
                type)
            )
    End Sub
    Private Function CreateNameValue(
                        name As String,
                        value As Object,
                        type As Type) As INameValue
        Dim retvalue As INameValue
        Dim sName As String = type.Name
        Dim isNullable As Boolean =
            type.FullName.StartsWith("System.Nullable")
        If isNullable Then
            'Get the base type Name
            Dim splitter = Split(type.FullName, "[")
            splitter = splitter(2).Split(",")
            sName = splitter(0).Replace("System.", "")
        End If
        If isNullable Then
            Select Case sName
                Case "BigInt"
                    retvalue = New NameValue(Of Int64?)(name)
                Case "Bit"
                    retvalue = New NameValue(Of Boolean?)(name)
                Case "Boolean"
                    retvalue = New NameValue(Of Boolean?)(name)
                Case "Char"
                    retvalue = New NameValue(Of Char?)(name)
                Case "DateTime"
                    retvalue = New NameValue(Of DateTime?)(name)
                Case "Decimal"
                    retvalue = New NameValue(Of Decimal?)(name)
                Case "Float"
                    retvalue = New NameValue(Of Decimal?)(name)
                Case "Int"
                    retvalue = New NameValue(Of Integer?)(name)
                Case "Money"
                    retvalue = New NameValue(Of Decimal?)(name)
                Case "Real"
                    retvalue = New NameValue(Of Double?)(name)
                Case "UniqueIdentifier"
                    retvalue = New NameValue(Of Guid?)(name)
                Case "SmallDateTime"
                    retvalue = New NameValue(Of DateTime?)(name)
                Case "SmallInt"
                    retvalue = New NameValue(Of Int16?)(name)
                Case "SmallMoney"
                    retvalue = New NameValue(Of Decimal?)(name)
                Case "TinyInt"
                    retvalue = New NameValue(Of Int16?)(name)
                Case "Date", "System.DateTime"
                    retvalue = New NameValue(Of Date?)(name)
                Case "Time"
                    retvalue = New NameValue(Of DateTime?)(name)
                Case "DateTime2"
                    retvalue = New NameValue(Of DateTime?)(name)
                Case "DateTimeOffset"
                    retvalue = New NameValue(Of TimeSpan?)(name)
                Case "Int32"
                    retvalue = New NameValue(Of System.Int32?)(name)
                Case "Int16"
                    retvalue = New NameValue(Of System.Int16?)(name)
                Case "Int64"
                    retvalue = New NameValue(Of System.Int64?)(name)
                Case "Double"
                    retvalue = New NameValue(Of System.Double?)(name)
                Case Else
                    Debug.WriteLine(String.Format("Case ""{0}", type.ToString))
                    Debug.WriteLine(String.Format("retvalue = New NameValue(Of {0})(name)", type.ToString))
                    Throw New NotImplementedException(String.Format("Missing type={0}", type.ToString))
            End Select

        Else
            Select Case sName
                Case "BigInt"
                    retvalue =
                        New NameValue(Of Int64)(name)
                Case "Binary"
                    retvalue =
                        New NameValue(Of Byte())(name)
                Case "Bit"
                    retvalue =
                        New NameValue(Of Boolean)(name)
                Case "Boolean"
                    retvalue = New NameValue(Of Boolean)(name)
                Case "Char"
                    retvalue = New NameValue(Of Char)(name)
                Case "DateTime"
                    retvalue = New NameValue(Of DateTime)(name)
                Case "Decimal"
                    retvalue = New NameValue(Of Decimal)(name)
                Case "Float"
                    retvalue = New NameValue(Of Decimal)(name)
                Case "Image"
                    retvalue = New NameValue(Of Byte())(name)
                Case "Int"
                    retvalue = New NameValue(Of Integer)(name)
                Case "Money"
                    retvalue = New NameValue(Of Decimal)(name)
                Case "NChar",
                        "NText",
                        "NVarChar",
                        "Text",
                        "VarChar",
                        "Xml",
                        "String"
                    retvalue =
                        New NameValue(Of String)(name)
                Case "Real"
                    retvalue = New NameValue(Of Double)(name)
                Case "UniqueIdentifier"
                    retvalue = New NameValue(Of Guid)(name)
                Case "SmallDateTime"
                    retvalue = New NameValue(Of DateTime)(name)
                Case "SmallInt"
                    retvalue = New NameValue(Of Int16)(name)
                Case "SmallMoney"
                    retvalue = New NameValue(Of Decimal)(name)
                Case "Timestamp"
                    retvalue = New NameValue(Of Byte())(name)
                Case "TinyInt"
                    retvalue = New NameValue(Of Int16)(name)
                Case "VarBinary"
                    retvalue = New NameValue(Of Byte())(name)
                Case "Variant"
                    retvalue = New NameValue(Of Object)(name)
                Case "Udt"
                    retvalue = New NameValue(Of Object)(name)
                Case "Structured"
                    retvalue = New NameValue(Of Object)(name)
                Case "Date", "System.DateTime"
                    retvalue = New NameValue(Of Date)(name)
                Case "Time"
                    retvalue = New NameValue(Of DateTime)(name)
                Case "DateTime2"
                    retvalue = New NameValue(Of DateTime)(name)
                Case "DateTimeOffset", "TimeSpan"
                    retvalue = New NameValue(Of TimeSpan)(name)
                Case "Int32"
                    retvalue = New NameValue(Of System.Int32)(name)
                Case "Int16"
                    retvalue = New NameValue(Of System.Int16)(name)
                Case "Int64"
                    retvalue = New NameValue(Of System.Int64)(name)
                Case "Byte[]"
                    retvalue = New NameValue(Of System.Byte())(name)
                Case "Double"
                    retvalue = New NameValue(Of System.Double)(name)
                Case Else
                    Debug.WriteLine(
                        String.Format(
                            "Case ""{0}",
                            type.ToString))
                    Debug.WriteLine(
                        String.Format(
                            "retvalue = New NameValue(Of {0})(name)",
                            type.ToString))
                    Throw New NotImplementedException(
                        String.Format(
                            "Missing type={0}",
                            type.ToString)
                        )
            End Select
        End If
        retvalue.RawValue = value
        Return retvalue
    End Function
    Public Function GetByName(
                        name As String) As INameValue
        Return (
            From el
            In Me
            Where
                el.Name.ToUpper =
                name.ToUpper
                ).FirstOrDefault
    End Function
End Class

Generalizando el código


Donde empezamos a crear bibliotecas de herramientas útiles.

Finalmente, luego del parate de los últimos tiempos, volvemos al ruedo con cuestiones ya directamente prácticas.

Comencemos por definir algunas herramientas útiles a nuestros quehaceres diarios.

Para ello, crearemos un proyecto de elementos comunes (de nombre Common, por ejemplo), para definir allí elementos que serán útiles en muchos otros proyectos.

En los ejemplos, utilizaré mis propios proyectos como ejemplo. Por ello, todos ellos tienen un espacio de nombres común: DS 🙂

Entonces, comencemos creando el proyecto [ComoQuierasLlamarlo].Common.

O sea, te creas n Nuevo Proyecto, eliges el leguaje de programación de tu elección (yo, prefiero VB, pero da igual).

Te recomiendo que mantengas en una misma solución, todas tus bibliotecas de utilidades, para poder vincularlas desde otras soluciones y aislar el código.

Además, si utilizas Azure Team Foundation, o cualquier otro protector de código, con Git o similares, mantenerlo en una solución protegida te ayudará a nunca perder tu código.

Clases para intercambiar valores.

Muchas veces, es necesario intercambiar valores entre funciones, bibliotecas, elementos externos, etc. y queremos mantener aisladas las referencias a bibliotecas específicas (por ejemplo, a bibliotecas de bases de datos, de repositorios, etc.).

También sucede que algunas bibliotecas, (de nuevo, bases de datos, por ejemplo), no siempre utilizan los mismos tipos de datos que otras. Entonces, se podría necesitar pasar valores como tipo “Object”, o sea, de modo no tipificado.

Por otra parte ya vimos que siempre que sea factible, es mejor utilizar los tipos de datos específicos.

Y necesitaremos conocer el nombre de cada uno de los valores que queremos intercambiar.

Entonces necesitaríamos

  • Una clase que exponga un valor con su tipo especifico
  • Que posea el nombre del valor en cuestión (o sea, nombre del argumento o propiedad)
  • Una forma genérica de acceder al valor, como si fuera objeto.

Si además quisiéramos contenerlas en una lista, colección o diccionario, si las hiciésemos específicas, sería muy complejo definir dicha colección, dadas las diferencias de tipos.

Por ello, recurriremos a una interfaz de nuestros objetos “Valor-Nombre”:

Public Interface INameValue
    Property Name As String
    Property RawValue As Object
End Interface

Creando clases genéricas

Estando así las cosas, y dadas nuestras necesidades, es cuando surge la posibilidad de utilizar generalizaciones (Generics) para poder definir nuestra clase de nombre-valor, acorde a cada tipo de dato.

Una clase definida como genérica, permite definir la misma para que en el momento de crear una instancia, recién allí se defina el tipo de dato en  cuestión.

Se debe declarar la clase, indicando que será de un “tipo no definido” utilizando la sintaxis (Of T). (<T> en C#)

Entonces, en cada método o función que se requiera especificar el tipo que se asignará cuando se cree la instancia, se utiliza la letra T (puede ser cualquier letra, sencillamente se usa la T de Type = Tipo)

Por ejemplo, nuestra clase expondrá el valor con el tipo específico, con la propiedad Value; entonces, la declaratoria sería:

    Public Property Value As T
  • La propiedad Nombre
  • La propiedad RawValue que, al recibir un valor, debe convertirlo al tipo de datos correcto.
#Region "INameValue"
    Implements INameValue
    Public Property Name As String Implements INameValue.Name
    Public Property Value As T
    Public Property RawValue As Object Implements INameValue.RawValue
        Get
            Return Me.Value
        End Get
        Set(value As Object)
            If Not value Is Nothing AndAlso Not TypeOf value Is DBNull OrElse TypeOf Me.Value Is Nullable Then
                Me.Value = CType(value, T)
            End If
        End Set
    End Property
#End Region

Además, al ser tipos no específicos, cabe que tengamos que manipular los operadores. Como ejemplo, vayan estos dos:

Public Shared Operator =(first As NameValue(Of T), second As NameValue(Of T)) As Boolean
        Return first.RawValue.ToString =  second.RawValue.ToString
End Operator
Public Shared Operator <>(first As NameValue(Of T), second As NameValue(Of T)) As Boolean
        Return first.RawValue.ToString <> second.RawValue.ToString
End Operator

Finalmente, agreguemos un constructor para poder definir nuevas instancias pasando el nombre.

    Sub New(name As String)
        Me.Name = name
    End Sub

El mismo código, en lenguaje C#.

 

public class NameValue<T> : INameValue
{
    public string Name { get; set; }
    public object RawValue
    {
        get
        {
            return this.Value;
        }
        set
        {
            if (value != null && value != DBNull.Value || Nullable.GetUnderlyingType(this.Value.GetType()) != null)
            {
                this.Value = (T)value;
            }
        }
    }
    public T Value;
    public static bool operator ==(NameValue<T> first, NameValue<T> second)
    {
        return first.RawValue.ToString() == second.RawValue.ToString();
    }
    public static bool operator !=(NameValue<T> first, NameValue<T> second)
    {
        return first.RawValue.ToString() != second.RawValue.ToString();
    }
    public NameValue(string name)
    {
        this.Name = name;
    }
}

Serie de videos en Channel 9–Tipos de variables


Como el amigo Matías se ha tomado el trabajo de grabar vídeos con elementos importantes de .Net, les dejo aqui alguno para que repasen.

Matías Iacono

channel9logo_55552f9b

Otro video de la serie.

En esta oportunidad hablamos sobre los diferentes tipos de variables que podemos encontrar en C# y sus principales diferencias.

http://channel9.msdn.com/Blogs/DevWow/-100devdays-Tipos-y-Variables-en-C-

Ver la entrada original

interfaces


Cuando se trata de generalizar nuestro código, existe otra forma posible de hacer esto, que tiene algunas diferencias en cuanto a su implementación, pero además ciertos usos que nos permiten definir mecánicas, inclusive con actores esto es programadores, externos que usen otros desarrollos.

Es el caso de las interfaces. Por definirlo brevemente, un interfaz es ni más ni menos, un contrato que debemos cumplir nuestro código.

La diferencia fundamental entre un interfaz y una clase base, es que la interfaz en sí misma no contiene código de ejecución. Sencillamente lo que hace este definir cómo se verán los objetos desde fuera.

A diferencia de lo que sucede con una clase base, un objeto definido por nosotros, puede implementar varias interfaces distintas, mientras que sólo puede heredar de una clase base.

Esto ha llevado a muchas discusiones teóricas, dado que en varios entornos se pretenden heredades de múltiples clase base. Aun cuando esto podría ser útil en ciertos y determinados casos, desde mi punto de vista, esto hace que el mantenimiento de ese código resulte mucho más dificultoso.

Llegado el caso de pretenderse heredar de múltiples clases, lo que debiera hacerse es definir en cascada las herencias, de modo tal de poder implementar código del más básico hasta el más especializado, y no hacer que se hereden desde clases base distintas.

Así, en el ejemplo que estábamos utilizando, la clase Vegetal que hereda de la clase SerVivo, podría a su vez ser heredada, por una clase Árbol.

Esta podría definir nueva propiedades, como tipo de raíces, formato de la hoja, cantidad ramificaciones, etc. Y luego tendríamos otras clases que al heredar de Árbol, también estarían heredando en forma indirecta de Vegetal y también de SerVivo.

Uso de Interfaces

Una cosa distinta son las interfaces. Un interfaz es, en definitiva, una especificación de algo que se debe cumplir, esto es, que cada clase  que implementa dicha interfaz, obligatoriamente debe exponer todos y cada uno de los miembros que la interfaz define.

Es en ese sentido que decimos que la interfaz es un contrato. Al existir, una biblioteca de código que conozca la interfaz, podrá acceder a cualquiera de sus métodos, aún sin tener una referencia directa, de la biblioteca de clases que implementa dichos métodos.

Esto se suele utilizar, cuando no podemos tener conocimiento claro o relación directa, con el conjunto de código con el cual vamos a tratar.

Imaginemos que por ejemplo, estamos creando un proceso, que necesita utilizar datos obtenidos de distintos orígenes, y que no sabemos a ciencia cierta, ni cuáles serán, ni cómo serán implementados.

O mejor aún, que estamos creando un componente, que sea capaz de realizar ciertas tareas, y que luego otra programadores pueden utilizar, en futuras aplicaciones o implementaciones de los orígenes de datos.

Inclusive a futuro, otro programadores de otras empresas, podrían implementar sus propios orígenes de datos sin tener que referenciar todo nuestro código, bastando sencillamente que conocieran las interfaces correctas.

Así, las interfaces podrían definirse en una biblioteca, que podría entregarse a cualquier programador externo, para que haga sus propias implementaciones de orígenes de datos, que luego nuestro componente o aplicativo pudiese utilizar sin mayores inconvenientes.

Criterios de definición de un interfaz.

Estos son los criterios básicos para la definición de cualquier interfaz.

  • No contiene código ejecutable.
  • No se pueden definir alcances: todos los miembros declarados en un interfaz son en forma predeterminada, públicos.
  • Se pueden definir tanto propiedades como métodos; si la propiedad es de sólo lectura o sólo escritura quedará consignado en la declaración del interfaz, y luego las clase que implemente solo podrán exponer estos métodos.
  • En la declaración se podrán utilizar tipo de datos personalizados, como podrían ser enumeraciones, clases personalizadas, estructuras, etc. Sin embargo, en dicho caso será conveniente, que estos tipos de datos se encuentran declarados en la misma biblioteca de clases que las interfaces, para mantenerlas aisladas del resto de los componentes, y así poder distribuirlos sin mayores inconvenientes.

Ejemplo.

El siguiente ejemplo muestra cómo sería la interfaz IVida, que se correspondería con nuestra clase base de SerVivo, si quisiéramos definir sus miembros aisladamente de la clase base.

Por convención, se suele definir el nombre de una interfaz, comenzando con I mayúscula.

VB

Public Interface IVida
    Sub Nacer()
    Sub Crecer()
    Sub Reproducir()
    Sub Morir()
    Sub GenerarEnergía()
End Interface

CS

 

interface IVida
{
    void Nacer();
    void Crecer();
        
    void Reproducir();
    void Morir();
        
    void GenerarEnergía();
}

Implementando la interfaz

 

Una vez definida un interfaz, cualquier clase que quiera indicar que cumple este contrato, deberá implementar la interfaz.

Como suele suceder, diferentes lenguajes de programación, utilizan distintos mecanismos para indicar esto. Para Visual Basic existe una palabra reservada, implements, mientras que el C#, se define exactamente igual que una herencia esto es, indicando después del nombre de declaración de la clase, el caracter dos puntos (:) y el nombre de la interfaz o interfaces que se implementan,  separados por comas. En el caso que además de implementar interfaces, se quisiera heredar de otra clase, el César la clase de la cual se hereda debe estar inmediatamente luego del carácter dos puntos.

Así quedaría entonces, la clase vegetal heredando de SerVivo e implementando IVida.

VB

Public Class Vegetal
    Inherits SerVivo
    Implements IVida
    Private Sub Fotosíntesis() Implements IVida.GenerarEnergía
    End Sub
End Class

CS

public class Vegetal : SerVivo,IVida
{
   
    private void Fotosíntesis() 
    {
    }
    void IVida.GenerarEnergía()
    {
        Fotosíntesis();
    }

Deberíamos resaltar que, mientras en Visual Basic la implementación de cada miembro de la interfaz es declarativa, utilizando la palabra reservada “Implements”, en C# la implementación se hace por coincidencia de nombre. Es por eso que, en VB, el método FotoSíntesis puede implementar directamente el GenerarEnergía de la interfaz, mientras que en C# se necesitan ambos métodos, y hacer la llamada en cascada.

Finalmente, un método que Implementa una interfaz en Visual Basic, puede ser privado (y por tanto, solo visible desde la interfaz), mientras que, nativamente, en C# debe cumplir con el alcance.

Sin embargo, como se ve en el ejemplo, el método que implementará interfaz, puedas hacerlo implícitamente, utilizando sólo el nombre del método, o como en este caso, explícitamente antecediendo al nombre del método, el nombre de la interfaz seguido por un punto.

Al hacer la llamada en cascada, el método que realmente posee el código, en nuestro ejemplo FotoSíntesis, queda declarado  igualmente como privado, con lo cual estamos obteniendo el mismo resultado final.

Son sencillamente, diferentes formas de hacer la misma cosa.

En la siguiente publicación, comenzaremos a utilizar combinatoria as de estas características, en un conjunto de bibliotecas que nos permitan realizar finalmente, tareas útiles.

Hablemos de Herencia (2)


Ya hemos visto en Hablemos de Herencia, los conceptos básicos de este tema.

Analicemos entonces como queda el código de lo ya definido, y algunas variantes al respecto.

Ser vivo.

Por recordar, esta era su representación gráfica.

image

Y así quedaría el código:

VB

Public MustInherit Class SerVivo
    Public Sub Nacer()
    End Sub
    Public Sub Crecer()
    End Sub
    Public Sub Reproducir()
    End Sub
    Public Sub Morir()
    End Sub
    Public MustOverride Sub GenerarEnergía()
End Class

CS

public abstract class SerVivo
{
    public void Nacer()
    {
    }
    public void Crecer()
    {
    }
    public void Reproducir()
    {
    }
    public void Morir()
    {
    }
    public abstract void GenerarEnergía();
}

Como vemos, la clase se declara como que debe heredarse (MustInherit en VB, abstract en C#), y luego se definen los distintos métodos.

El método que debe codificarse en las clases derivadas, se declara como MustOverride (VB), abstract (C#).

Presta atención que, en C#, el mismo término significa cosas distintas, dependiendo del entorno. No es muy común, pero a veces pasa.

En ambos casos, los métodos que deben codificarse en los descendientes, no tienen cuerpo (No hay End Sub en VB, ni llaves en C#, donde termina con punto y coma).

Las clases derivadas.

image

VB

Public Class Vegetal
    Inherits SerVivo
    Public Overrides Sub GenerarEnergía()
        Fotosíntesis()
    End Sub
    Private Sub Fotosíntesis()
    End Sub
End Class

CS

public class Vegetal : SerVivo
{
    public override void GenerarEnergía()
    {
        Fotosíntesis();
    }
    private void Fotosíntesis()
    {
    }
}

Como vemos, ese método “no implementado” en la clase base, se debe terminar de definir en las clases derivadas (“sobre escribirse”).

En ambos lenguajes, la definición es casi idéntica.

En Visual Studio, al definir que una clase hereda de otra, el entorno te indica “como error” que deben escribirse esos métodos y hasta se ofrece a generarte el esquema base Sonrisa

Se indica que una clase hereda por distintos mecanismos, según el idioma.

En Visual Basic, se declara taxativamente que hereda “Inherits” de otra clase.

En C#, ese concepto se “entiende” porque luego de la declaración de la clase, se sigue con dos puntos, y el nombre de la clase que se hereda.

A partir de aquí, se podrá seguir heredando para cubrir, progresivamente, mayores especializaciones y características.

Lo interesante, es que cualquier modificación de una clase base, se reflejará (en realidad, se “heredará”), en cualquiera de sus derivadas.

Esto de la herencia, en definitiva, nos permite “comprometer” que nuestro código se vea siempre de la misma forma, en todas las clases derivadas/heredadas. Aún cuando una clase heredada presente más miembros, si la “vemos” como una clase base, siempre encontraremos los mismos métodos.

Una clase vegetal vista como ser vivo

Una clase animal vista como ser vivo

Pero hay otra forma de “comprometer” tu código para cumplir un convenio.

Eso es tema de la siguiente publicación.

Hablemos de Herencia


Una de las características más importantes de la programación orientada a objetos, es la posibilidad de estructurar mostró código en distintos niveles o capas, de modo tal de poder ir encapsulando funcionalidades desde las más básicas hasta las más complejas, incluyendo modificaciones en la funcionalidad de cualquiera en otros objetos, dependiendo de con quién están tratando o qué tipo de datos se van manipular o acciones a realizar.

Sin embargo, esto tiene una contrapartida; si no se piensan adecuadamente las cosas a implementar, se puede caer en una Maraña de códigos que van llamando funcionalidades hay into niveles y por tanto a ser que dicho código resulte un poco inmanejable, o inclusive que tienda a a incrementar las posibilidades de error.

Por tanto, lo más importante de todo, más allá de conocer las distintas te indica y sintaxis respecto escritura de código, es la comprensión clara de que se trata precisamente esto de la herencia.

Desde la realidad.

La mejor manera de comprender esto entonces, es observar que es lo que sucede dentro de la naturaleza. Así por ejemplo, podemos consideran desde el punto de vista biológico, que absolutamente cualquier ente vivo compre cuatro funciones básicas a saber:

  • Nacer
  • Crecer
  • Reproducirse
  • Morir

Cada reino y cada especie a su vez, podrán realizar cualquiera de estas actividades de distinta forma e inclusive podrán incrementar sus características, dependiendo del grado el colectivo.

Tomemos por caso la producción de energía: en todos los seres vivos este hecho en necesario, pero la forma de hacerlo es totalmente diferente cuando hablamos del reino vegetal y el reino animal. Entretanto agreguemos sencillamente esta funcionalidad,  imprescindible en cualquier ente vivo.

  • Generar energía.

Así, el reino vegetal utiliza en General la fotosíntesis, mientras que en el reino animal la generación de energía se produce por mecanismos de digestión de los alimentos. Para decirlo muy genéricamente, los vegetales toman sol, los animales combing.

 

Sin embargo, cualquiera estados acciones están pensadas para obtener el mismo resultado: energía.

 

Si fuésemos must identificó extraterrestre, lo que trataremos de investigar es precisamente, más en estas cosas que están en el planeta tierra para generar energía. Nos encontraríamos entonces que investigando la generación, veríamos dos mecanismos totalmente diferentes entre sí (en realidad no son tan diferentes desde un punto de vista bioquímico dado que termina en procesos bastante similares y que en alguna parte son los mismos).

Podríamos entonces considerar que, cualquiera sea vegetal o animal hereby características que son comunes: las cinco que hemos indicado con viñetas.

Cierto es también, que alguna de esas actividades por ejemplo generar energía, si bien desde fuera (científico extraterrestre) lo veríamos cómo una sola acción, internamente cada uno de los reinos lo estaría realizando de maneras diferentes.

Esto nos lleva a entender algunas definiciones respecto the la exposición de las funcionalidades desde nuestros objetos hacía observadores externos, o para darles un hombre más adecuado a nuestro entorno, llamadores or instanciadores.

Interfaz

Una interfaz es en definitiva, un contrato que los objetos confirman respecto de cómo se exponen a sí mismos y como brindan sus funcionalidades de modo tal que cualquier otro objeto que pretenda utilizar los tenga claro cuáles son los mecanismos y propiedades que podrá encontrar en dichos objetos.

La interfaz en definitiva, es una tipificación de que miembros, entendiendo como miembros el conjunto de propiedades, métodos y eventos, que expone un objeto y cuáles son las posibles formas de utilizarlos en cada caso. Se dice que cada miembro expone una firma o sea el nombre por el que se lo llamará, los argumentos que puede recibir, y el tipo de valor que retornará.

A veces el propio objeto será responsable de exponer su interfaz, mientras que en otros casos la interfaz será declarada en forma independiente y luego implementada por el objeto en cuestión.

 

Además, en términos de herencia, una clase que sirve como base para definir otras puede ser responsable de definir la interfaz que va a ser común a todas ellas, aunque a su vez las descendientes podrán tener miembros agregados que modifiquen o expandan la interfaz de cada uno de ellos.

Ejemplo

En conclusión entonces, podríamos crear una clase para el ejemplo que esté utilizando llamada SerVivo que tendría cinco métodos, como se muestra en el siguiente esquema.

image

Una segunda clase que hereda de esta llamada Vegetal, y que dentro del miembro genera energía utilizará los métodos de fotosíntesis.

image

Y otra clase que hereda también de SerVivo, llamada Animal que en el miembro generar energía utilizará procedimientos de digestión.

image

Algunas cuestiones de imagen.

Tenemos bastante para hablar respecto de cómo se representa las cosas, sin embargo para esta primera entrega quiero dejarle claro los elementos que estamos viendo en estos tres gráficos.

En principio un rectángulo de bordes redondeados representa una clase: lo sabemos además porque debajo del nombre precisamente aparece la palabra class.

Cada método está definido por debajo del grupo métodos, como un cubo con el nombre del método su lado. En el caso de la clase SerVivo, uno de ellos aparece en cursiva porque si bien la clase y lo define, no lo tiene implementado; delega la implementación en cada una de las clases que heredan de esta; decimos entonces que ese método debe heredarse, y normalmente una clase  que tiene métodos con estas características pasa a ser también una clase que obligadamente debe heredarse.

Finalmente, los métodos Fotosíntesis de la clase Vegetal y Digestión de la clase Animal están representadas como métodos con un cubo pero con el icono de un candado abajo la derecha: esto significa que ese método es privado a la clase y por tanto no es accesible desde fuera por parte de ningún otro objeto. Los miembros GenerarEnergía de Animal y de Vegetal internamente deberán llamar cada uno al que le corresponde.

image

En la representación gráfica del conjunto completo, la clase SerVivo tiene un indicador de que debe heredarse y  a su vez las clases Vegetal y Animal indican con una flecha hacia la derecha que heredan de SerVivo. Finalmente el diagrama General no representa esto gráficamente con las flechas que apunta desde las clases derivadas hacia la clase base

En la próxima entrega estaremos analizando cómo quedaría dicho código.

Comparando valo…


Comparando valores y referencias en varios lenguajes de programación De Eduard Tomás

Del Amigo Eduard Tomás https://twitter.com/eiximenis,  una comparativa de “comparaciones”.. útil para entender mejor las operaciones entre tipos por valor y tipòs por referencia en distintos lenguajes.. Aunque le falte VB🙂
http://www.campusmvp.es/recursos/post/Comparando-valores-y-referencias-en-varios-lenguajes-de-programacion.aspx 

El blog de Dani Seara

Microsoft Azure Blog

El blog de Dani Seara

VIVIENDO RODANDO

de rodar con cámara a rodar en una silla

Matías Iacono

De los bits a la psiquis

Leandro Tuttini Blog

El blog de Dani Seara

WindowServer

El blog de los paso a paso

campusMVP.es

El blog de Dani Seara

Angel \"Java\" Lopez on Blog

Software Development, in the Third Millenium

Angel "Java" Lopez

El blog de Dani Seara

Atascado en los 70 II (El regreso)

Segunda época del rockblog "Atascado en los 70". VIEJAS canciones y artistas PASADOS DE MODA. Tratamos al lector de usted y escribimos "rocanrol" y "roquero" con ortografía castellana.

Santiago Porras Rodríguez

El blog de Dani Seara

Todo en ASP.NET

Blog dedicado al desarrollo Web y temas relacionado con tecnologías Microsoft.

El Bruno

Innovation Craftsman

Cajon desastre

Just another WordPress.com site

Pasión por la tecnología...

Todo sobre tecnología Microsoft en general, y SharePoint en partícular...

return(GiS);

Mi sitio geek

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.