viernes, 12 de febrero de 2010

Herencia

La herencia, junto con la encapsulación y el polimorfismo, es una de las tres características principales (o "pilares") de la programación orientada a objetos. La herencia permite crear nuevas clases que reutilizan, extienden y modifican el comportamiento que se define en otras clases. La clase cuyos miembros se heredan se denomina clase base y la clase que hereda esos miembros se denomina clase derivada.

Nota

Las estructuras no admiten la herencia, pero pueden implementar interfaces.


Conceptualmente, una clase derivada es una especialización de la clase base. Por ejemplo, si tiene una clase base Animal, puede tener una clase derivada denominada Mammal y otra clase derivada denominada Reptile. Mammal es Animaly Reptile es Animal, pero cada clase derivada representa especializaciones diferentes de la clase base. Al definir una clase para derivar de otra clase, la clase derivada obtiene implícitamente todos los miembros de la clase base, salvo sus constructores y destructores. La clase derivada puede, por tanto, reutilizar el código de la clase base sin tener que volver a implementarlo. En la clase derivada, puede agregar más miembros. De esta manera, la clase derivada extiende la funcionalidad de la clase base. En la ilustración siguiente se muestra una clase ArticuloTrabajo que representa un elemento de trabajo en algún proceso de negocio. Como todas las clases, deriva de System..::.Object y hereda todos sus métodos. ArticuloTrabajo agrega cinco miembros propios. Esto incluye un constructor, porque los constructores no se heredan. PedirCambio hereda de ArticuloTrabajo y representa un tipo determinado de elemento de trabajo. PedirCambio agrega dos miembros más a los miembros que hereda de ArticuloTrabajo y Object. Debe agregar su propio constructor y también agrega un miembro que permitirá asociar PedirCambio al elemento ArticuloTrabajo  original al que se aplica el cambio.


En el ejemplo siguiente se muestra cómo se expresan en C# las relaciones de clase presentadas en la ilustración anterior. En el ejemplo también se muestra cómo ArticuloTrabajo invalida el método virtual Object..::.ToString y cómo la clase PedirCambio hereda la implementación de ArticuloTrabajo del método.
// ArituloTrabajo implícitamente hereda de la clase Object.
public class ArticuloTrabajo
{
    // El campo estático IDactual almacena el ID de trabajo del último ArticuloTrabajo que
    // ha sido creado.
    private static int IDactual;

    //Propiedades.
    protected int ID { get; set; }
    protected string Titulo { get; set; }
    protected string Descripcion { get; set; }
    protected TimeSpan TiempoTrabajo { get; set; }

    // Constructor predeterminado. Si una clase derivada no invoca una clase-
    // base constructor explícitamente, el constructor predeterminado se llama
    // implícitamente.
    public ArticuloTrabajo()
    {
        ID = 0;
        Titulo = "Título predeterminado";
        Descripcion = "Descripción predeterminada.";
        tiempoTrabajo = new TimeSpan();
    }

    // Constructor de instancias que tiene tres parámetros.
    public ArticuloTrabajo(string titulo, string desc, TimeSpan tiempotrabajo)
    {
        this.ID = GetNextID();
        this.Titulo = titulo;
        this.Description = desc;
        this.TiempoTrabajo = tiempotrabajo;
    }

    // Constructor estático para inicializar el miembro estático, IDactual. Esto
    // El constructor se llama una vez, automáticamente, antes que cualquier instancia.
    // de ArticuloTrabajo o PedirCambio se crea, o se hace referencia a IDactual.
    static ArticuloTrabajo() => IDactual = 0;

    // IDactual es un campo estático. Se incrementa cada vez que una nueva
    // instancia de ArticuloTrabajo es creada.
    protected int GetNextID() => ++IDactual;

    // Método Actualizar le permite actualizar el título y la duración del trabajo de un
    // existente objeto de tipo ArticuloTrabajo.
    public void Actualizar(string titulo, TimeSpan tiempotrabajo)
    {
        this.Titulo = titulo;
        this.TiempoTrabajo = tiempotrabajo;
    }

    // Anulación del método virtual del método ToString que se hereda
     // de System.Object.
    public override string ToString() =>
        $"{this.ID} - {this.Titulo}";
}

// PedirCambio deriva de ArticuloTrabajo y agrega una propiedad (originalItemID)
// y dos constructores.
public class Pedircambio: ArticuloTrabajo
{
    protected int originalItemID { get; set; }

    // Constructores. Porque ninguno de los constructores llama a una clase base
     // constructor explícitamente, el constructor predeterminado en la clase base
     // se llama implícitamente. La clase base debe contener un valor predeterminado
     // constructor.

   // Constructor predeterminado para la clase derivada.
    public Pedircambio() { }

    // Constructor de instancia que tiene cuatro parámetros.
    public Pedircambio(string titulo, string desc, TimeSpan tiempotrabajo,
                         int originalID)
    {
        // Las siguientes propiedades y el método GetNexID se heredan
         // de ArticuloTrabajo.
        this.ID = GetNextID();
        this.Title = title;
        this.Descripcion = desc;
        this.TiempoTrabajo = tiempotrabajo;

        // La propiedad originalItemId es miembro de PedirCambio, pero no
         // de ArticuloTrabajo.
        this.originalItemID = originalID;
    }
}

El siguiente bloque muestra cómo utilizar las clases base y derivadas:


// Crea una instancia de ArticuloTrabajo usando el constructor en el
// clase base que toma tres argumentos.
ArticuloTrabajo item = new ArticuloTrabajo("Corregir errores",
                            "Corregir todos los errores en mi código",
                            new TimeSpan(3, 4, 0, 0));

// Crea una instancia de PedirCambio usando el constructor en
// la clase derivada que toma cuatro argumentos.
Pedircambio cambio = new Pedircambio("Cambiar el diseño de la clase base",
                                        "Agregar miembros a la clase",
                                        new TimeSpan(4, 0, 0),
                                        1);

// Usa el método ToString definido en ArticuloTrabajo.
Console.WriteLine(item.ToString());

// Utilice el método de actualización heredado para cambiar el título del
// Objeto PedirCambio.
cambio.Actualizar("Cambiar el diseño de la clase base",
    new TimeSpan(4, 0, 0));

// PedirCambio hereda la sobrescritura de ToString desde ArticuloTrabajo
Console.WriteLine(cambio.ToString());
/* Salida:
    1 - Corregir errores
    2 - Cambiar el diseño de la clase base
*/


Métodos abstractos y virtuales Cuando una clase base declara un método como virtual, una clase derivada puede invalidar el método con su propia implementación. Si una clase base declara un miembro como abstracto, ese método se debe invalidar en cualquier clase no abstracta que herede directamente de dicha clase. Si una clase derivada es abstracta en sí misma, hereda los miembros abstractos sin implementarlos. Los miembros abstractos y virtuales son la base para el polimorfismo, la segunda característica principal de la programación orientada a objetos. Para obtener más información, vea Polimorfismo

Clases base abstractas Puede declarar una clase como abstracta si desea evitar la creación directa de instancias por medio de la palabra clave new. Si hace esto, la clase solo se puede utilizar si una nueva clase se deriva de ella. Una clase abstracta puede contener una o más firmas de método que se declaran a sí mismas como abstractas. Estas firmas especifican los parámetros y el valor devuelto pero no tienen ninguna implementación (cuerpo del método). Una clase abstracta no tiene que contener miembros abstractos; sin embargo, si una clase contiene un miembro abstracto, la propia clase se debe declarar como abstracta. Las clases derivadas que no son abstractas por sí mismas deben proporcionar la implementación de cualquier método abstracto de una clase base abstracta. Para obtener más información, vea Clases y miembros de clase abstractos y sellados y Diseño de clases abstractas. 

Interfaces Una interfaz es un tipo de referencia similar en cierto modo a una clase base abstracta compuesta únicamente por miembros abstractos. Cuando una clase deriva de una interfaz, debe proporcionar una implementación para todos los miembros de la interfaz. Una clase puede implementar varias interfaces aunque solo puede derivar de una única clase base directa. Las interfaces se utilizan para definir funciones específicas para las clases que no tienen necesariamente una relación de identidad. Por ejemplo, cualquier clase o estructura que tenga que habilitar el código de cliente para determinar si dos objetos de un tipo son equivalentes (cualquiera que sea la forma en que el tipo defina la equivalencia), puede implementar la interfaz IEquatable[`1]. IEquatable (T) no implica el mismo tipo de relación de identidad que existe entre una clase base y una clase derivada (por ejemplo, Mammal es Animal). Para obtener más información, vea Interfaces

Acceso de la clase derivada a los miembros de la clase base Una clase derivada tiene acceso a los miembros públicos, protegidos, internos e internos protegidos de una clase base. Aunque una clase derivada hereda los miembros privados de una clase base, no puede tener acceso a estos miembros. Sin embargo, todos los miembros privados siguen presentes en la clase derivada y pueden hacer el mismo trabajo que harían en la propia clase base. Por ejemplo, supongamos que un método protegido de la clase base tiene acceso a un campo privado. Este campo debe estar presente en la clase derivada para que el método heredado de la clase base funcione correctamente. 

Evitar la derivación adicional Una clase puede evitar que otras clases hereden de ella, o de cualquiera de sus miembros, declarándose a sí misma o al miembro como sealed. Para obtener más información, vea Clases y miembros de clase abstractos y sellados

Ocultar miembros de la clase base en la clase derivada Una clase derivada puede ocultar miembros de la clase base si los declara con el mismo nombre y firma. Se puede utilizar el modificador new para indicar explícitamente que no se pretende que el miembro sea una invalidación del miembro base. No es necesario utilizar new, pero se generará una advertencia del compilador si no se usa new.

No hay comentarios.:

Publicar un comentario

Preguntas;Comentarios;Aportes;Criticas Positivas;Recomendaciones.