Encapsulamiento (informática)

De Wikipedia, la enciclopedia libre
Diagrama de un objeto y su interacción mediante los métodos, no directamente sobre los datos.

En la Programación orientada a objetos (POO), la encapsulación se refiere a la agrupación de datos con los métodos que operan en esos datos, o la restricción del acceso directo a algunos de los componentes de un objeto.[1]​ La encapsulación se utiliza para ocultar los valores o el estado de un objeto de datos estructurados dentro de una clase, evitando el acceso directo a ellos por parte de los clientes de una manera que podría exponer detalles de Implementación ocultos o violar la invariancia de estado mantenida por los métodos.

Sin embargo, ocultar datos no es el alcance total de la ocultación de información. David Parnas introdujo por primera vez el concepto de ocultación de información alrededor de 1972. Argumentó que el criterio principal para la modularización del sistema debería referirse a la ocultación de decisiones críticas de diseño. Hizo hincapié en ocultar "decisiones de diseño difíciles o decisiones de diseño que probablemente cambien". Ocultar información de esa manera aísla a los clientes de requerir un conocimiento profundo del diseño para usar un módulo y de los efectos de cambiar esas decisiones.[1]

Significado[editar]

Diagrama de polimorfismo, objetos de tipos distintos con un método para responder a una misma acción. Cada animal responderá con su propio sonido, diferente al resto.

Podríamos decir por tanto que con la encapsulación logramos agrupar datos o restringir el acceso directo a algunos componentes de un objeto, mediante métodos a modo de intermediarios que harán uso de ellos mediante unas reglas definidas. Ayudándonos así por ejemplo a alcanzar otro nivel de la programación orientada a objetos como sería el polimorfismo (no confundir con el polimorfismo en el malware).

El polimorfismo se refiere a la propiedad por la que es posible enviar mensajes sintácticamente iguales a objetos de tipos distintos. El único requisito que deben cumplir los objetos que se utilizan de manera polimórfica es saber responder al mensaje que se les envía.

El ejemplo más claro de encapsulación y polimorfismo sería un programa capaz de calcular las áreas de distintos polígonos, un cliente solo debería de conocer la existencia del método encargado de devolver el área, sin necesidad de preocuparse del proceso interno para calcular las mismas, puesto que el cálculo del área de un cono no se corresponde con la de un rectángulo. Será tarea del programador asegurar la encapsulación para que los métodos que devuelven el área de los distintos objetos de diferentes polígonos respondan de la misma manera, cada uno mediante su propia fórmula matemática de cálculo del área, logrando así el mencionado polimorfismo.

De esta forma además de que el usuario de la clase puede obviar la implementación de los métodos y propiedades para concentrarse solo en cómo usarlos, evita que el usuario pueda cambiar su estado de maneras imprevistas e incontroladas.

Encapsulación y herencia[editar]

Los autores de "Design Patterns" [2]​ discuten la tensión entre la herencia y la encapsulación y afirman que, en su experiencia, los diseñadores abusan de la herencia. Afirman que la herencia a menudo rompe la encapsulación, dado que la herencia expone una subclase a los detalles de la implementación de su clase padre. Como se describe en el problema del yo-yo, el uso excesivo de la herencia y, por lo tanto, la encapsulación, puede volverse demasiado complicado y difícil de depurar.

Modificadores de acceso[editar]

Los modificadores de acceso son palabras clave que se usan para especificar la accesibilidad declarada de un miembro o un tipo. Depende del lenguaje de programación que se use, pero los más comunes son:

  • Default: En el caso del lenguaje Java, los miembros de datos, clase o métodos que no se declaran utilizando ningún modificador de acceso, es decir, que tengan un modificador de acceso predeterminado, solo son accesibles dentro del mismo paquete.[3]
  • Public: El acceso público es el nivel de acceso más permisivo. No hay ninguna restricción para el acceso a miembros públicos.[4]
  • Protected: Un miembro protegido es accesible dentro de su clase y por parte de instancias de clases derivadas.[5]
  • Private: El acceso privado es el nivel de acceso menos permisivo. Los miembros privados solo son accesibles dentro del cuerpo de la clase o el struct[6]​ en el que se declaran.[7]
  • Internal: Solo se puede tener acceso a los tipos internos o los miembros desde los archivos del mismo ensamblado.[8]
  • Protected internal: Se puede obtener acceso a un miembro protected internal desde el ensamblado actual o desde tipos que se deriven de la clase contenedora.[9]
  • Private protected: Los miembros private protected están accesibles para los tipos que se deriven de la clase contenedora, pero solo desde dentro del ensamblado correspondiente que lo contenga.[10]

Ejemplos[editar]

Un ejemplo de la lógica empleada en pseudocódigo:

Declaración de la clase genérica
{
	Declaración de variable, modificador de acceso y tipo

	[ENCAPSULACIÓN] Declaración de método para leer variable
	{
		Acceso al valor de la variable
	}

	[ENCAPSULACIÓN] Declaración de método para sobrescribir variable
	{
		Acceso a la variable para insertar nuevo valor
	}
}

Declaración de la clase principal
{
	Declaración de un objeto de la clase genérica

	Llamada al método de sobreescritura del valor de la variable del objeto creado de la clase genérica

	Llamada al método de lectura del valor de la variable del objeto creado de la clase genérica
}

[Salida]:

Valor de la variable, introducido en el objeto mediante la llamada al método de sobreescritura

Un ejemplo real en el lenguaje de programación C#:

// ===================================================================================
//  Ejemplo de encapsulación en C#
// ===================================================================================

// Declaración de la clase "ClaseEjemplo"
public class ClaseEjemplo
{
    // Campos declarados como privados
    // Por lo que solo podrán ser utilizados mediante métodos públicos de la clase

    // Campo de valor alfanumérico, su valor será tomado como texto
    // De introducirse números no serán matemáticamente computables
    private String nombre;
    // Campo de valor entero (integer), no aceptaría otra cosa que números enteros
    // Estos números si serían matemáticamente computables, por lo que se le podrían sumar años, por ejemplo 
    private int edad;

    // Descriptores de acceso get/set para controlar el acceso al atributo nombre
    public String DescriptorNombre
    {
        get
        {
            // Devolvemos el valor del campo nombre
            return nombre;
        }

        set
        {
            // Asignamos valor al campo nombre
            // Es obligatorio usar value
            nombre = value;
        }
    }

    // Descriptores de acceso get/set para controlar el acceso al atributo edad
    public int DescriptorEdad
    {
        get
        {
            // Devolvemos el valor del campo edad
            return edad;
        }

        set
        {
            // Asignamos valor al campo edad
            // Es obligatorio usar value
            edad = value;
        }
    }
}

// Declaración de la clase principal
class ClasePrincipal
{
    // Método principal, es obligatorio que se llame "Main"
    static public void Main()
    {
        // Creamos un objeto de la clase "ClaseEjemplo"
        ClaseEjemplo objeto = new ClaseEjemplo();

        // Llamamos al disruptor de acceso al atributo “nombre” del objeto creado
        // Le pasamos "Nombre de la persona" como valor alfanumérico para el parámetro value
        objeto.DescriptorNombre = "Nombre de la persona";

        // Llamamos al disruptor de acceso al atributo “edad” del objeto creado
        // Le pasamos "00" como valor entero para el parámetro value
        objeto.DescriptorEdad = 00;

        // Mostramos el valor de los campos en la consola de comandos
        // Al no pasarle un valor, el descriptor entiende que lo que debe de hacer es devolverlo
        Console.WriteLine("Nombre: " + objeto.DescriptorNombre);
        Console.WriteLine("Edad: " + objeto.DescriptorEdad);
    }
}

Salida:

Consola de comandos
Nombre: Nombre de la persona

Edad: 00



En caso de intentar acceder directamente a uno de los campos de la clase, saltándonos por tanto la encapsulación:

Console.WriteLine("Nombre: " + objeto.nombre);

El propio compilador ya nos avisaría del error con respecto a los modificadores de acceso:[11]

Error    CS0122   "ClaseEjemplo.nombre' no es accesible debido a su nivel de protección"

Referencias[editar]

  1. a b Wm. Paul Rogers (18 de mayo de 2001). «Encapsulation is not information hiding» (en inglés). 
  2. Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns (en inglés). Addison-Wesley. ISBN 978-0-201-63361-0. (requiere registro). 
  3. Oracle - Java. «Controlling Access to Members of a Class» (en inglés). 
  4. Microsoft. «public (Referencia de C#)». 
  5. Microsoft. «protected (Referencia de C#)». 
  6. Microsoft. «struct (C++)». 
  7. Microsoft. «private (Referencia de C#)». 
  8. Microsoft. «internal (Referencia de C#)». 
  9. Microsoft. «protected internal (Referencia de C#)». 
  10. Microsoft. «private protected (Referencia de C#)». 
  11. Microsoft. «Error del compilador CS0122».