c++ oop abstraction getter-setter data-access

c++ - ¿Debo usar variables públicas o privadas?



oop abstraction (7)

Desde el punto de vista de OOP, los getters / setters ayudan con la encapsulación y, por lo tanto, siempre deben usarse. Cuando llama a un captador / preparador, la clase puede hacer lo que quiera entre bambalinas y las partes internas de la clase no están expuestas al exterior.

Por otro lado, desde el punto de vista de C ++, también puede ser una desventaja si la clase hace muchas cosas inesperadas cuando solo quiere obtener / establecer un valor. A la gente le gusta saber si algún acceso resulta en una gran sobrecarga o si es simple y eficiente. Cuando accedes a una variable pública, sabes exactamente lo que obtienes, cuando usas un getter / setter no tienes idea.

Especialmente si solo hace un proyecto pequeño, pasar su tiempo escribiendo getters / setters y ajustándolos en consecuencia cuando decide cambiar su nombre / tipo de variable / ... produce muchas tareas para obtener pequeñas ganancias. Será mejor que pases ese tiempo escribiendo un código que haga algo útil.

El código de C ++ comúnmente no usa captadores / definidores cuando no proporcionan una ganancia real. Si diseña un proyecto de 1,000,000 de líneas con muchos módulos que tienen que ser lo más independientes posible, podría tener sentido, pero para la mayoría de los códigos de tamaño normal que escribe día a día son excesivos.

Estoy haciendo un gran proyecto por primera vez. Tengo muchas clases y algunas de ellas tienen variables públicas, algunas tienen variables privadas con métodos de establecimiento y obtención y ambas tienen ambos tipos.

Decidí reescribir este código para usar principalmente solo un tipo. Pero no sé cuál debería usar (las variables que se usan solo para los métodos en el mismo objeto son siempre privadas y no están sujetas a esta pregunta).

Conozco la teoría sobre qué medios públicos y privados, pero qué se usa en el mundo real y por qué?


Hay algunos tipos de datos cuyo único propósito es contener datos bien especificados. Estos normalmente se pueden escribir como estructuras con miembros de datos públicos. Aparte de eso, una clase debe definir una abstracción. Las variables públicas o los setters y getters triviales sugieren que el diseño no se ha pensado lo suficiente, lo que resulta en una aglomeración de abstracciones débiles que no abstraen mucho de nada. En lugar de pensar en los datos, piense en el comportamiento : esta clase debe hacer X, Y y Z. A partir de ahí, decida qué datos internos se necesitan para respaldar el comportamiento deseado. Al principio no es fácil, pero recuérdate que lo que importa son los comportamientos, no los datos.


Las variables de miembros privados se prefieren sobre las variables de miembros públicos, principalmente por las razones indicadas anteriormente (encapsulación, datos bien especificados, etc.). También proporcionan cierta protección de datos, ya que garantiza que ninguna entidad externa puede alterar la variable miembro sin pasar por el canal adecuado de un configurador si es necesario.

Otro beneficio de getters y setters es que si está usando un IDE (como Eclipse o Netbeans), puede usar la funcionalidad del IDE para buscar cada lugar en la base de código donde se llama a la función. Proporcionan visibilidad sobre dónde se está utilizando o modificando un dato de esa clase en particular. Además, puede hacer que el acceso a las hebras de las variables miembro sea seguro si tiene un mutex interno. Las funciones getter / setter capturarían esta exclusión mutua antes de acceder o modificar la variable.

Soy un defensor de la abstracción hasta el punto en que sigue siendo útil. La abstracción en aras de la abstracción generalmente resulta en un caos desordenado que es más complicado de lo que vale.


Las variables públicas generalmente no se recomiendan, y la mejor forma es hacer que todas las variables sean privadas y acceder a ellas con captadores y definidores:

private int var; public int getVar() { return var; } public void setVar(int _var) { var = _var; }

Los IDE modernos como Eclipse y otros lo ayudan a hacer esto al proporcionar características como "Implementar captadores y definidores" y "Encapsular campo" (que reemplaza todos los accesos directos de variables con las llamadas correspondientes de captador y configurador).


No existe una regla rígida sobre lo que debe ser privado / público o protegido.

Depende del rol de tu clase y de lo que ofrezca.

  • Todos los métodos y miembros que constituyen el funcionamiento interno de la clase deben hacerse privados .
  • Todo lo que una clase ofrece al mundo exterior debe ser público .
  • Los miembros y métodos que pueden tener que ser extendidos en una especialización de esta clase, podrían declararse como protegidos .

Ya que dice que conoce la teoría, y otras respuestas han profundizado en el significado de público / privado, captadores y definidores, me gustaría centrarme en el por qué de usar accesores en lugar de crear atributos públicos (datos de miembros en C ++) .

Imagine que tiene un camión de clase en un proyecto de logística:

class Truck { public: double capacity; // lots of more things... };

Siempre que sea norteamericano, probablemente use galones para representar la capacidad de sus camiones. Imagine que su proyecto está terminado, funciona perfectamente, aunque se realizan muchos usos directos de Truck::capacity . En realidad, su proyecto se convierte en un éxito, por lo que algunas empresas europeas le piden que adapte su proyecto a ellas; desafortunadamente, el proyecto debe usar el sistema métrico ahora, por lo que se deben emplear litros en lugar de galones para la capacidad.

Ahora, esto podría ser un desastre. Por supuesto, una posibilidad sería preparar una base de código solo para América del Norte, y una base de código solo para Europa. Pero esto significa que las correcciones de errores se deben aplicar en dos fuentes de código diferentes, y se decide que esto no es factible.

La solución es crear una posibilidad de configuración en su proyecto. El usuario debe poder configurar galones o litros, en lugar de ser una opción fija y programada de galones.

Con el enfoque visto arriba, esto significará mucho trabajo, tendrá que rastrear todos los usos de Truck::capacity y decidir qué hacer con ellos. Esto probablemente significará modificar archivos a lo largo de toda la base de código. Supongamos, como alternativa, que ha decidido un enfoque más theoretic .

class Truck { public: double getCapacity() const { return capacity; } // lots of more things... private: double capacity; };

Un posible cambio alternativo no implica ninguna modificación de la interfaz de la clase:

class Truck { public: double getCapacity() const { if ( Configuration::Measure == Gallons ) { return capacity; } else { return ( capacity * 3.78 ); } } // lots of more things... private: double capacity; };

(Por favor, tenga en cuenta que hay muchas maneras de hacer esto, que solo es una posibilidad, y esto es solo un ejemplo)

Tendrá que crear la configuración de la clase de utilidad global (pero tuvo que hacerlo de todos modos), y agregar una inclusión en truck.h para la configuration.h , pero todos estos son cambios locales, el resto de su base de código permanece sin cambios, por lo tanto Evitando posibles bugs.

Finalmente, también afirma que está trabajando ahora en un gran proyecto, que creo que es el tipo de campo en el que estas razones realmente tienen más sentido. Recuerde que el objetivo que debe tener en cuenta al trabajar en grandes proyectos es crear un código que se pueda mantener, es decir, un código que pueda corregir y ampliar con nuevas funcionalidades. Puedes olvidarte de los captadores y los instaladores en pequeños proyectos personales, aunque intentaría acostumbrarme a ellos.

Espero que esto ayude.


private miembros de datos private generalmente se consideran buenos porque proporcionan encapsulación.

Proporcionar captadores y definidores para ellos rompe esa encapsulación, pero sigue siendo mejor que public miembros de datos public porque solo hay un punto de acceso a esos datos.

Lo notarás durante la depuración. Si es privado, sabes que solo puedes modificar la variable dentro de la clase. Si es público, tendrás que buscar en la base de código completa para ver dónde se puede modificar.

En la medida de lo posible, prohíba getters / setters y haga que las propiedades sean private . Esto sigue el principio de ocultación de la información: no debe preocuparse por las propiedades que tiene una clase. Debe ser autocontenido. Por supuesto, en la práctica esto no es factible, y si lo es, un diseño que sigue a esto será más abarrotado y más difícil de mantener que uno que no lo haga.

Esta es, por supuesto, una regla de oro: por ejemplo, solo usaría una struct (equivalente a una class con acceso público) para, por ejemplo, una clase de puntos simple:

struct Point2D { double x; double y; };