Propósito de los miembros privados en una clase


¿Cuáles son los propósitos de tener miembros privados/protegidos de una clase/estructura en la programación orientada a objetos? ¿Cuál es el daño en que todos los miembros sean públicos?

Author: Jim G., 2010-03-04

10 answers

Encapsulación. Es decir, ocultar la implementación de sus datos de clase. Esto le permite cambiarlo más tarde, sin romper todo el código del cliente. Por ejemplo, si tiene

class MyClass {
    public int foo;
}

Sus clientes pueden escribir código como

MyClass bar = new MyClass();
bar.foo++;

Ahora, si te das cuenta de que foo debería ser en realidad un doble en lugar de int, lo cambias:

class MyClass {
    public double foo;
}

Y el código cliente falla al compilar: - (

Con una interfaz bien diseñada, el cambio de las partes internas (partes privadas) puede incluso incluir la variable miembro en un cálculo o viceversa:

class Person {
    public String getName();
    public String getStreetAddress();
    public String getZipCode();
    public String getCountryCode();
    public int hashCode();
}

(usar propiedades de cadena en aras de la simplicidad - en un diseño del mundo real, algunas de estas probablemente merecerían tener su propio tipo.)

Con este diseño, usted es libre de, por ejemplo, introducir una propiedad Address internamente, que contendría la dirección, el código postal y el código de país, y reescribir sus accesos para usar los campos de este miembro privado en su lugar, sin que sus clientes noten nada.

Usted también podría decidir libremente si calcular el código hash cada vez, o almacenarlo en caché en una variable privada con el fin de mejorar el rendimiento. Sin embargo, si ese campo de caché fuera público, cualquiera podría cambiarlo, lo que podría arruinar el comportamiento del mapa de hash e introducir errores sutiles. Por lo tanto, la encapsulación es clave para garantizar la consistencia del estado interno de su objeto. Por ejemplo, en el ejemplo anterior, sus configuradores pueden validar fácilmente el código postal y el código de país, para evitar establecer valores no válidos. Puedes incluso asegúrese de que el formato del código postal sea válido para el país real, es decir, asegure un criterio de validez que abarque varias propiedades. Con una interfaz bien diseñada, puede hacer cumplir este enlace, por ejemplo, proporcionando solo un setter para establecer ambas propiedades al mismo tiempo:

    public void setCountryCodeAndZip(String countryCode, String zipCode);

Sin embargo, con los campos públicos simplemente no tiene estas opciones.

Un caso de uso especial para los campos privados son los objetos inmutables; esto es muy común en, por ejemplo, Java, los ejemplos son String y BigDecimal. Estas clases no tienen configuradores públicos en absoluto, lo que garantiza que sus objetos, una vez creados, no cambiarán su estado. Esto permite una gran cantidad de optimizaciones de rendimiento, así como las hace más fáciles de usar en, por ejemplo, programas multiproceso, OR, etc.

 27
Author: Péter Török,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-04 10:23:38

Es posible que desee leer el tema Ocultar información en wikipedia.

Esencialmente, los miembros privados permiten que una clase oculte sus detalles de implementación de los consumidores externos. Esto permite a una clase controlar mejor cómo se expresarán los datos y el comportamiento de ti, y permite al consumidor ignorar detalles que no son relevantes para el propósito principal de la clase.

Ocultar detalles de implementación mejora la capacidad de mantenimiento de un program evitando que el código externo de la clase establezca dependencias sobre esos detalles. Esto permite que la implementación cambie independientemente de los consumidores externos, con un riesgo reducido de romper el comportamiento existente. Cuando los detalles de la implementación privada se hacen públicos, no se pueden cambiar sin la posibilidad de romper con los consumidores de la clase que dependen de esos detalles.

Los miembros privados también permiten que una clase proteja su implementación de abuso. Típicamente, el estado de una clase tiene dependencias internas que definen cuando el estado es válido - y cuando no lo es. Podemos considerar que las reglas que gobiernan la validez de la información de estado son invariantes, lo que significa que la clase siempre espera que sean verdaderas. Exponer detalles privados, permite que el código externo modifique este estado de una manera que puede violar los invariantes, y por lo tanto comprometer la validez (y el comportamiento) de la clase.

Un adicional el beneficio de ocultar información, es que reduce el área de superficie que los consumidores de la clase tienen que entender con el fin de interactuar adecuadamente con la clase. La simplificación es generalmente algo bueno. Permite a los consumidores centrarse en la comprensión de la interfaz pública, y no en cómo la clase logra su funcionalidad.

 9
Author: LBushkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 21:20:43

Muy bien explicado en Sección 7.4: Proteja sus Partes privadas de este tutorial en línea de C++ .

¿Por qué molestarse con estas cosas?

Los especificadores permiten que una clase sea muy complejo, con muchas funciones de miembro y los miembros de datos, mientras que tiene un interfaz pública simple que otros las clases pueden usar. Una clase que tiene dos cien miembros de datos y cien las funciones miembro pueden ser muy complicado de escribir; pero si hay sólo tres o cuatro miembros públicos funciones, y el resto son todos privado, puede ser fácil para alguien aprende a usar la clase. Él sólo necesita entender cómo usar un pequeño un puñado de funciones públicas, y no necesita molestarse con los dos cientos de miembros de data, porque no es se le permite acceder a estos datos. Él puede acceder únicamente a los datos privados a través de la interfaz pública de la clase. Sin duda, en un pequeño programa, utilizando estos los especificadores pueden parecer innecesarios. Sin embargo, vale la pena entender si usted planea hacer cualquier programa de tamaño razonable (más de un par cien líneas). En general, es bueno practique para que los miembros de datos sean privados. Funciones miembro que deben ser llamadas desde fuera de la clase debe ser público, y las funciones de los miembros que son solo se llama desde dentro de la clase (también conocido como "funciones auxiliares") probablemente debería ser privado. Estos especificadores son especialmente útiles en un programa grande que involucra a más de un programador.

La explicación anterior explica cómo usar private facilita la curva de aprendizaje. Aquí hay un ejemplo que explica el aspecto de "ruptura de código":

Aquí hay una clase ParameterIO que lee y escribe un vector de parámetros enteros

class ParameterIO
{
public:
    // Main member
    vector<int> *Params;
    string param_path;

    // Generate path
    void GeneratePath()
    {       
        char szPath[MAX_PATH];
        sprintf(szPath,"params_%d.dat",Params->size());
        param_path = szPath;
    }

    // Write to file
    void WriteParams()
    {
        assert_this(!Params->empty(),"Parameter vector is empty!");
        ofstream fout(param_path.c_str());
        assert_this(!fout.fail(),"Unable to open file for writing ...");
        copy(Params->begin(),Params->end(),ostream_iterator<int>(fout,"\n"));
        fout.close();
    }

    // Read parameters
    void ReadParams(const size_t Param_Size)
    {
        // Get the path
        Params->resize(Param_Size);
        GeneratePath();
        // Read
        ifstream fin(param_path.c_str());
        assert_this(!fin.fail(),"Unable to open file for reading ...");
        // Temporary integer
        for(size_t i = 0; i < Params->size() && !fin.eof() ; ++i) fin>>(*Params)[i];
        fin.close();
    }

    // Constructor
    ParameterIO(vector<int> * params):Params(params)
    {
        GeneratePath();
    }

    // Destructor
    ~ParameterIO()
    {
    }      

    // Assert
    void assert_this(const bool assertion, string msg)
    {
        if(assertion == false) 
        {
            cout<<msg<<endl;
            exit(1);
        }
    }
};

El siguiente código rompe esta clase:

const size_t len = 20;
vector<int> dummy(len);
for(size_t i = 0; i < len; ++i) dummy[i] = static_cast<int>(i);
ParameterIO writer(&dummy);

// ParameterIO breaks here!
// param_path should be private because 
    // the design of ParameterIO requires a standardized path
writer.param_path = "my_cool_path.dat";
// Write parameters to custom path
writer.WriteParams();

vector<int> dunce;
ParameterIO reader(&dunce);
// There is no such file!
reader.ReadParams(len);
 7
Author: Jacob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 22:16:10

Metafóricamente, exponer a los miembros privados como públicos es como tener una opción en el salpicadero de su automóvil que le permite ajustar la presión del aceite del motor.

El coche debe gestionar eso internamente (en privado), y el usuario debe estar protegido de jugar con él directamente (encapsulación), por razones obvias.

 5
Author: Daniel Vassallo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 21:18:16

Realmente depende de tu ideología. La idea es ocultar información que no debería ser expuesta por alguna razón.

Si tiene una biblioteca que desea publicar en línea, mucha gente la descargará y algunos pueden usarla en su código. Si mantiene su API pública al mínimo y oculta los detalles de la implementación, tendrá menos dificultades para actualizarla cuando encuentre errores o desee mejorar el código.

Además, en Java, por ejemplo, no tiene forma de restringir el acceso a una variable miembro sin cambiar su visibilidad, por lo que a menudo te encuentras prematuramente creando getters y setters y haciendo que la variable en sí sea privada o protegida. En Python, por ejemplo, ese problema no existe porque puedes hacer que los getters y setters se comporten como variables para el acceso directo (se llaman propiedades allí).

Por último, a veces es necesario tener métodos que requieren un estado consistente para ser útil y daría lugar a problemas si se accede directamente.

Una regla general es: si expones algo, alguien lo usará. Y la mayoría de las veces lo usarán por las razones incorrectas (es decir, no cómo pretendías que se usaran). En este caso, la ocultación de información es el equivalente a las cerraduras para niños en los gabinetes de armas.

 4
Author: Alan Plum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 21:16:25

Para agregar a la respuesta de Peter, digamos que su clase almacena un nombre, y desea cambiarlo de usar una sola cadena de nombre a una cadena de nombre y una cadena de apellido. Si sus miembros fueran públicos, otras clases podrían leer (o escribir) la variable name directamente, y se romperían cuando esa variable desapareciera.

Sin mencionar que es posible que no desee que otras clases tengan la capacidad de editar sus miembros en absoluto.

 3
Author: Pops,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 21:16:35

A veces no quieres revelar información privada a todo el mundo. Por ejemplo, no desea que su edad sea conocida por el público, pero es posible que desee decirle a la gente si tiene más de 25 años.

 1
Author: fastcodejava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 21:19:12

¿Cuáles son los propósitos de tener órganos internos en el cuerpo humano? ¿Qué hay de malo en tener todos los órganos afuera?

¡Exactamente!

La respuesta corta sería: Porque los necesitas para no poder vivir sin ellos y no puedes exponerlos a todo el mundo para modificarlos y jugar con ellos porque eso podría matarte (hacer que tu clase no funcione correctamente).

 1
Author: Leo Jweda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 21:52:20

Ningún daño en absoluto dependiendo de la audiencia y el consumo de la clase. Permítanme repetirlo una vez más para que se hunda.

Ningún daño en absoluto dependiendo de la audiencia y el consumo de la clase.

Para muchos proyectos pequeños de una o dos personas que toman un mes más o menos, conseguir todas las definiciones privadas y públicas perfectamente podría aumentar la carga de trabajo sustancialmente. En grandes proyectos, sin embargo, donde puede haber varios equipos y los equipos no están geográficamente ubicados juntos, obtener el diseño correcto de todas las interfaces públicas desde el principio puede aumentar en gran medida la probabilidad de éxito del proyecto en general.

Así que realmente tienes que mirar cómo va a ser consumida una clase y por quién antes de que puedas siquiera comenzar a responder esta pregunta. Del mismo modo, ¿cuánto tiempo va a ser el ciclo de vida del desarrollo de software? ¿Son meses? Años? Décadas? ¿Habrá otras personas además de ti usando la clase?

Cuanto más "público" la clase (es decir, cuantas más personas consuman y usen la clase), más importante es clavar una interfaz pública sólida y adherirse a ella.

 1
Author: Steven Noyes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 22:31:45

Un breve ejemplo: Es posible que deba asegurarse de ciertas condiciones en ese valor. En este caso, configurarlo directamente puede romper tal condición.

Muchas personas argumentan como "puede que no quieras que todo el mundo lo lea", pero creo que la restricción de establecer un valor es un ejemplo más útil.

 0
Author: Johann Philipp Strathausen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-03 21:48:41