Programación orientada a objetos (POO)

Introducción

En un lenguaje de procedimientos, no hay ningún enlace entre los datos y los procedimientos que los manejan. Por el contrario en un lenguaje orientado a objetos agrupamos los datos con el código que lo manejan. Las CLASES son la representación simbólica de los OBJETOS. Las CLASES describen campos, propiedades, métodos y eventos.

Haciendo una analogía con el trabajo de un ingeniero, la CLASE se correspondería con el plano de un motor y e OBJETO serían los diferentes motores que se construyen a partir de ese plano. Crear un OBJETO a partir de un una clase es lo que se llama INSTANCIAR.

Las tres propiedades de la ORIENTACIÓN A OBJETOS son:

  1. ENCAPSULACIÓN: Agrupa datos y códigos en una única clase.
  2. HERENCIA: Permite la creación de una nueva clase a partir de otra ya existente, de la cual se hereda todo y puede personalizarse añadiendo o modificando propiedades y métodos heredados. Las clases creadas a partir de otras existentes se llaman CLASES DERIVADAS.
  3. POLIMORFISMO: Gracias a esta propiedad se pueden utilizar diferentes clases de forma intercambiable. Otros conceptos asociados al polimorfismo son la SOBRECARGA,  SOBREESCRITURA y la OCULTACIÓN.

UML_POO1

Ejemplo CLASE Persona con notación UML.

 

 

 

 

Declaración de una clase

Sintaxis general:

[atributos] [modificadores] [parcial] class NombreDeLaClase [ : clase base] [, interfaz1, interfaz2, ...] {
	Código de la clase
}

Para determinar la visibilidad el lenguaje cuenta con las siguientes palabras clave:

  1. public: la clase puede ser utilizada en cualquier proyecto.
  2. internal: la clase está limitada al proyecto en el cual está definida.
  3. private: la clase sólo puede usarse en el módulo en la que está definida.
  4. protected: la clase sólo puede ser utilizada en una subclase. Es decir sólo se puede utilizar protected para una clase declarada en otra clase.
  5. protected internal: lo mismo que internal + protected.
  6. abstract: no permite crear instancias de esta clase, sólo sirve para ser heredada como clase base. Suelen tener los métodos definidos pero sin ninguna operatividad con lo que se suele escribir estos métodos en las clases derivadas.
  7. sealed: cuando una clase es la última de una jerarquía, por lo que no podrá ser utilizada como base de otra clase.

 

public class Persona {
  String Apellido;
  String Nombre;
  DateTime FechaNac;
}

Clase Parcial

Podemos definir una clase en varias declaraciones, pudiendo así utilizar varios archivos fuente para declarar una clase. Se utiliza normalmente para permitir la personalización de clases generadas automáticamente. el código generado suele colocarse en un archivo llamado .designer.cs; y durante la compilación se agrupan todas las definiciones parciales para obtener el código fuente de la clase.

public partial class Empleado {
    public void HacerTrabajo(){
      ... lo que sea ...
    }
}

public partial class Empleado {
    public void IrAComer() {
      ... lo que sea ...
    } 
}

Creación de propiedades privadas

public class Persona {
  private String elApellido;
  private String elNombre;
  private DateTime laFechaNac;
  public String Apellido {
    get {return elApellido;}
    set {elApellido=value.ToUpper();}
  }
  public String Nombre{
    get {return elNombre;}
    set {elNombre=value.ToLower();}
  }
  public DateTime FechaNac{
    get {return laFechaNac;}
    set { 
      if (value.Year >= 1900) {
        laFechaNac = value;}
    }
  }
}

Sólo lectura y sólo escritura

Se pueden restringir los accesos a una propiedad, si sólo incluimos la opción “get” lo que estamos haciendo es dando permisos de lectura; en cambio si incluimos la opción “set” lo que hacemos es dar permisos de escritura.

public class Persona {
  private String elApellido;
  private String elNombre;
  private DateTime laFechaNac;
  private String laClave;
  public String Apellido {
    get {return elApellido;}
    set {elApellido=value.ToUpper();}
  }
  public String Nombre{
    get {return elNombre;}
    set {elNombre=value.ToLower();}
  }
  public DateTime FechaNac{
    get {return laFechaNac;}
    set { 
      if (value.Year >= 1900) {
        laFechaNac = value;}
    }
  }
  public int Edad {
    get { return DateTime.Now.Year - laFechaNac.Year;}
  }  
  public String Clave {
    set { laClave = value;}
  }
}

Propiedades indexadas

public class Persona {
  private String elApellido;
  private String elNombre;
  private DateTime laFechaNac;
  private String laClave;
  private Persona[] losHijos = new Persona [10];
  public String Apellido {
    get {return elApellido;}
    set {elApellido=value.ToUpper();}
  }
  public String Nombre{
    get {return elNombre;}
    set {elNombre=value.ToLower();}
  }
  public DateTime FechaNac{
    get {return laFechaNac;}
    set { 
      if (value.Year >= 1900) {
        laFechaNac = value;}
    }
  }
  public int Edad {
    get { return DateTime.Now.Year - laFechaNac.Year;}
  }  
  public String Clave {
    set { laClave = value;}
  }
  public Persona this[int index] {
    get { return losHijos[index];}
    set { losHijos[index]=value;}
  }
}

Propiedades Indexadas La propiedad indexada esperará como parámetro un índice que permita especificar el “hijo” con el cual queremos trabajar. Veamos un ejemplo de instanciación de esta clase:Ejemplo Propiedades Indexadas

 

 


  private void button1_Click(object sender, EventArgs e)
        {
            Persona p = new Persona();
            Persona h1 = new Persona();
            Persona h2 = new Persona();
            p.Apellido = "Pedraza";
            p.Nombre = "Juanjo";
            p.FechaNac = new DateTime(1969, 3, 27);
            h1.Apellido = p.Apellido;
            h1.Nombre = "Juan";
            h1.FechaNac = new DateTime(2010, 3, 27);
            h2.Nombre = "Carla";
            h2.Apellido = p.Apellido;
            h2.FechaNac = new DateTime(2013, 8, 7);
            //Asignamos hijos a la persona
            p[0] = h1;
            p[1] = h2;
            MessageBox.Show(
                "Sr. "+p.Apellido+" "+p.Nombre+" nacido el "+p.FechaNac+" tiene 2 hijos\n"+
                "Hijo/a 1: "+p[0].Nombre+" nacido/a el "+p[0].FechaNac +"\n"+
                "Hijo/a 2: "+p[1].Nombre+" nacido/a el "+p[1].FechaNac +"\n");

        }

Creación de métodos, sobrecarga y sobreescritura.

Son procedimientos o funciones definidos dentro de una CLASE. Los métodos pueden manejar los campos de la clase incluso si son privados.

La sobrecarga es la creación dentro de la clase, de un grupo de métodos que tienen el mismo nombre pero con un número de parámetros distinto y/o bien distintos tipos de datos.

public void visualización () {
  MessageBox.Show("Sr. "+Apellido+" "+Nombre+" nacido el "+FechaNac); 
}

public void visualización (string idioma) {
  switch (idioma) {
    case "es":  MessageBox.Show("Sr. "+Apellido+" "+Nombre+" nacido el "+FechaNac); break;
    case "en":  MessageBox.Show("Mr. "+Apellido+" "+Nombre+" was born "+FechaNac); break;
  }
}

Sabemos que las clases derivadas heredan las propiedades y métodos de su clase base. Se pueden usar sin ninguna modificación, pero sí el método no está adaptado a la nueva clase podemos sobrescribirlo. Para ello utilizamos la palabra reservada override. También es obligatorio que permitir la sobrescritura de mediante el de la palabra reservada virtual. Esto se utiliza para asegurar el polimorfismo entre las clases.

 

public override void visualización () {
  MessageBox.Show("Sr. "+Apellido+" "+Nombre+" nacido el "+FechaNac+" cobra "+Salario+".-€uros/mes."); 
}

public sealead override void visualización () {
  base.visualizacion();
  MessageBox.Show("y cobra "+Salario+".-€uros/mes."); 
}

Ejemplo de método abstracto

  ...
  public abstract string EstadoCivil();
  //de este método no hay implementación sólo definición.
  // la clase también estará marcada como abstracta.
  ...

Método parcial

  ...
  public string Apellido {
  get {return elApellido}
  set {
    elApellido = value.ToUpper();
    ApellidoCambiado();
  }
  ...
  partial void ApellidoCambiado();
  ...
  ...
  namespace LoQueSea {
    partial class Persona {
      partial void ApellidoCambiado() {
        MessageBox.Show("Ojo se cambió el apellido");
      }
    }
  }
  ...
  ...
  Persona p = new Persona();
  p.Apellido = "Pedraza";
  p.Nombre = "Juanjo";
  p.FechaNac = new DateTime(1969,3,27);
  MessageBox.Show(p.Apellido);
  ...

Métodos de extensión

Los métodos de extensión permiten añadir funcionalidades a una clase ya definida sin tener que modificar el código de esta clase. Se deben respetar las siguientes reglas:

  • Pueden ser de tipo procedimientos o función. NUNCA propiedad.
  • El primer parámetro irá precedido de la palabra this. La palabra clave this hace referencia a la instancia actual de la clase ,pero también se utiliza como modificador del primer parámetro de un método de extensión.
  • El tipo del primer parámetro del método determina el tipo extendido por este método.
  • En el momento de la ejecución, éste primer parámetro representa la instancia de la clase sobre la cual se llama el método.
  • Se deben definir una clase static.
  • Ellos mismos deben ser static.

Ejemplo de extensión de la clase Persona con el método “presentacion”, ya que es en este caso un procedimiento, el primer parámetro va precedido de “this”, el tipo del primer parámetro en este caso es la propia clase “Persona” que determina el tipo extendido por esta nueva propiedad y representa la instancia de la clase sobre la cual se llama al método, está definido como “static”:

static class ExtesionPersona {
    public static void presentacion(this Persona p) {
        Console.WriteLine("Apellido: {0}", p.apellido);
        Console.WriteLine("Nombre: {0}",p.nombre);
        Console.writeLine("Fecha de nacimiento: {0}", p.fecha_naci);
    }
}

Los métodos de extensión también también se pueden definir para los tipos básicos del Framework, como por ejemplo la clase string. El siguiente código añade a la clase string un método que permite convertir el primer carácter de una cadena en mayúsculas.

public static class ExtensionString
{
    public static string PrimeroMayusculas(this String s)
    {
        if ((s == null) || (s.Length == 0))
        {
            return s;
        }
        else if (s.Length == 1)
        {
            
            return s.ToUpper();
        }
        else
        {
            return s.Substring(0, 1).ToUpper() + s.Substring(1, s.Length - 1);
        }
    }
}

Ejercicio: Crear una extensión de la clase string que ponga en mayúsculas un determinado número de caracteres.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    public static class ExtensionClaseString
    {
        public static string PrimeroMayusculas(this String s, int recorta)
        {
            if ((recorta >= s.Length) || (s.Length == 1))
            {
                return s.ToUpper();
            }
            else if ((s == null) || (s.Length == 0))
            {
                return s;
            }
            else
            {
                return s.Substring(0, recorta).ToUpper() + s.Substring(recorta);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string cadena;
            cadena = "hola mundO";
            cadena = cadena.PrimeroMayusculas(4);
            Console.WriteLine(cadena);
            Console.ReadLine();
        }
    }
}