tutorialspoint java polymorphism




¿Por qué usar getters y setters/accessors? (20)

Utilizamos getters y setters:

  • por reutilización
  • Realizar validación en etapas posteriores de programación.

Los métodos de getter y setter son interfaces públicas para acceder a miembros de clases privadas.

Mantra de encapsulacion

El mantra de la encapsulación es hacer campos privados y métodos públicos.

Métodos de Getter: Podemos acceder a variables privadas.

Métodos de Setter: Podemos modificar campos privados.

A pesar de que los métodos getter y setter no agregan una nueva funcionalidad, podemos cambiar nuestra mente y volver más tarde para hacer ese método.

  • mejor;
  • más seguro y
  • Más rápido.

En cualquier lugar donde se pueda usar un valor, se puede agregar un método que devuelva ese valor. En lugar de:

int x = 1000 - 500

utilizar

int x = 1000 - class_name.getValue();

En términos sencillos

Supongamos que necesitamos almacenar los detalles de esta Person . Esta Person tiene los campos name , age y sex . Hacer esto implica crear métodos para el name , la age y el sex . Ahora, si necesitamos crear otra persona, se hace necesario crear los métodos para el name , la age , el sex nuevamente.

En lugar de hacer esto, podemos crear una class(Person) bean class(Person) con los métodos get y set. Así que mañana podemos crear objetos de esta class(Person class) Bean class(Person class) cuando sea necesario agregar una nueva persona (ver la figura). Por lo tanto, estamos reutilizando los campos y métodos de la clase de bean, que es mucho mejor.

¿Cuál es la ventaja de usar captadores y definidores, que solo se obtienen y configuran, en lugar de simplemente usar campos públicos para esas variables?

Si los captadores y setters alguna vez hacen algo más que el simple get / set, puedo resolver esto muy rápidamente, pero no estoy 100% claro sobre cómo:

public String foo;

es peor que

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

Mientras que el primero toma mucho menos código repetitivo.


Bueno, solo quiero agregar que incluso si a veces son necesarias para la encapsulación y la seguridad de sus variables / objetos, si queremos codificar un Programa Orientado a Objetos real, entonces debemos DETENER ABUSO DE LOS ACCESORES , porque a veces dependemos mucho en ellos cuando no es realmente necesario y eso hace casi lo mismo que si pusiéramos las variables públicas.


Depende de tu idioma. Ha etiquetado este "orientado a objetos" en lugar de "Java", por lo que me gustaría señalar que la respuesta de ChssPly76 depende del idioma. En Python, por ejemplo, no hay razón para usar getters y setters. Si necesita cambiar el comportamiento, puede usar una propiedad, que envuelve al que obtiene y establece el acceso a los atributos básicos. Algo como esto:

class Simple(object):
   def _get_value(self):
       return self._value -1

   def _set_value(self, new_value):
       self._value = new_value + 1

   def _del_value(self):
       self.old_values.append(self._value)
       del self._value

   value = property(_get_value, _set_value, _del_value)

En un mundo puro orientado a objetos, los captadores y definidores son un anti-patrón terrible . Lee este artículo: Getters / Setters. Mal. Periodo En pocas palabras, alientan a los programadores a pensar en objetos como en estructuras de datos, y este tipo de pensamiento es puramente procedimental (como en COBOL o C). En un lenguaje orientado a objetos, no hay estructuras de datos, sino solo objetos que exponen el comportamiento (¡no atributos / propiedades!)

Puede encontrar más información sobre ellos en la Sección 3.5 de Objetos elegantes (mi libro sobre programación orientada a objetos).


Hay muchas razones. Mi favorito es cuando necesita cambiar el comportamiento o regular lo que puede establecer en una variable. Por ejemplo, digamos que tenía un método setSpeed ​​(int speed). Pero quieres que solo puedas establecer una velocidad máxima de 100. Harías algo como:

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}

Ahora, ¿qué pasa si EN TODAS PARTES de su código estaba usando el campo público y luego se dio cuenta de que necesita el requisito anterior? Diviértete buscando todos los usos del campo público en lugar de solo modificar tu configurador.

Mis 2 centavos :)


Mucha gente habla acerca de las ventajas de los que consiguen y los que ponen, pero quiero jugar a ser el defensor del diablo. En este momento estoy depurando un programa muy grande en el que los programadores decidieron hacer todo lo que tiene que hacer y configurar. Eso puede parecer agradable, pero es una pesadilla de ingeniería inversa.

Digamos que estás viendo cientos de líneas de código y te encuentras con esto:

person.name = "Joe";

Es un código maravillosamente simple hasta que te das cuenta de que es un setter. Ahora, sigue ese setter y encuentra que también establece person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName, y llama a person.update (), que envía una consulta a la base de datos, etc. Oh, eso es donde estaba ocurriendo su pérdida de memoria.

La comprensión de una pieza de código local a primera vista es una propiedad importante de buena legibilidad que los captadores y definidores tienden a romper. Es por eso que trato de evitarlos cuando puedo, y minimizar lo que hacen cuando los uso.


Pasé bastante tiempo pensando en esto para el caso de Java, y creo que las razones reales son:

  1. Código a la interfaz, no a la implementación.
  2. Las interfaces solo especifican métodos, no campos

En otras palabras, la única manera de especificar un campo en una interfaz es proporcionar un método para escribir un nuevo valor y un método para leer el valor actual.

Esos métodos son el infame getter y setter ....


Puede ser útil para la carga perezosa. Digamos que el objeto en cuestión está almacenado en una base de datos, y no quiere ir a buscarlo a menos que lo necesite. Si el objeto es recuperado por un captador, entonces el objeto interno puede ser nulo hasta que alguien lo solicite, entonces puede obtenerlo en la primera llamada al captador.

Recibí una clase de página base en un proyecto que me fue entregado y que cargaba algunos datos de un par de llamadas a servicios web diferentes, pero los datos de esas llamadas a servicios web no siempre se usaban en todas las páginas secundarias. Los servicios web, para todos los beneficios, promueven nuevas definiciones de "lento", por lo que no desea hacer una llamada de servicio web si no tiene que hacerlo.

Pasé de los campos públicos a los buscadores, y ahora los captadores revisan el caché y, si no está allí, llaman al servicio web. Así que con un poco de ajuste, se evitaron muchas llamadas de servicio web.

Así que el que lo consigue me evita intentar averiguar, en cada página secundaria, lo que necesitaré. Si lo necesito, llamo al captador y me lo va a buscar si aún no lo tengo.

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }

Un campo público no es peor que un par getter / setter que no hace nada, excepto devolver el campo y asignarlo. Primero, está claro que (en la mayoría de los idiomas) no hay diferencia funcional. Cualquier diferencia debe estar en otros factores, como facilidad de mantenimiento o legibilidad.

Una ventaja mencionada a menudo de los pares getter / setter, no lo es. Existe la afirmación de que puede cambiar la implementación y que sus clientes no tienen que volver a compilarse. Supuestamente, los configuradores le permiten agregar funcionalidades como la validación más adelante y sus clientes ni siquiera necesitan saberlo. Sin embargo, agregar la validación a un colocador es un cambio a sus condiciones previas, una violación del contrato anterior , que era, simplemente, "puede poner cualquier cosa aquí, y puede obtener esa misma cosa más tarde de quien la recibe".

Por lo tanto, ahora que rompió el contrato, cambiar cada archivo en el código base es algo que debe hacer, no evitar. Si lo evita, está asumiendo que todo el código asumió que el contrato para esos métodos era diferente.

Si ese no debería haber sido el contrato, entonces la interfaz permitía a los clientes poner el objeto en estados no válidos. Eso es exactamente lo contrario de la encapsulación. Si ese campo realmente no se puede establecer en nada desde el principio, ¿por qué no fue la validación desde el principio?

Este mismo argumento se aplica a otras supuestas ventajas de estos pares de getter / setter de transferencia: si más tarde decide cambiar el valor que se está estableciendo, está rompiendo el contrato. Si reemplaza la funcionalidad predeterminada en una clase derivada, de una manera que va más allá de algunas modificaciones inofensivas (como el registro u otro comportamiento no observable), está incumpliendo el contrato de la clase base. Esa es una violación del Principio de Sustituibilidad de Liskov, que se considera uno de los principios de OO.

Si una clase tiene estos captadores y organizadores tontos para cada campo, entonces es una clase que no tiene invariantes de ningún tipo, ningún contrato . ¿Es eso realmente un diseño orientado a objetos? Si todo lo que la clase tiene son esos captadores y definidores, es solo un titular de datos tontos, y los titulares de datos tontos deben parecer titulares de datos tontos:

class Foo {
public:
    int DaysLeft;
    int ContestantNumber;
};

Agregar pares de getter / setter a esta clase no agrega ningún valor. Otras clases deben proporcionar operaciones significativas, no solo operaciones que los campos ya proporcionan. Así es como puedes definir y mantener invariantes útiles.

Cliente : "¿Qué puedo hacer con un objeto de esta clase?"
Diseñador : "Puedes leer y escribir varias variables".
Cliente : "Oh ... bien, supongo?"

Hay razones para usar captadores y definidores, pero si esas razones no existen, hacer pares de captador / definidor en nombre de falsos dioses de encapsulación no es algo bueno. Las razones válidas para hacer captadores o definidores incluyen las cosas que a menudo se mencionan como los posibles cambios que puede realizar más adelante, como la validación o diferentes representaciones internas. O tal vez el valor debería ser legible para los clientes pero no escribible (por ejemplo, leer el tamaño de un diccionario), por lo que un simple captador es una buena opción. Pero esas razones deberían estar ahí cuando haga la elección, y no solo como una cosa potencial que puede desear más adelante. Esta es una instancia de YAGNI ( No lo vas a necesitar ).


Una ventaja de los accesores y mutadores es que puede realizar la validación.

Por ejemplo, si foo era público, fácilmente podría establecerlo en null y luego otra persona podría intentar llamar a un método en el objeto. ¡Pero ya no está allí! Con un método setFoo , podría asegurarme de que foo nunca se configuró en null .

Los accesores y los mutadores también permiten la encapsulación: si no se supone que se debe ver el valor una vez que se establece (tal vez se establezca en el constructor y luego se use por métodos, pero nunca se deba cambiar), nadie lo verá nunca. Pero si puede permitir que otras clases lo vean o lo cambien, puede proporcionar el acceso y / o mutador adecuado.


Getters y setters se utilizan para implementar dos de los aspectos fundamentales de la Programación Orientada a Objetos que son:

  1. Abstracción
  2. Encapsulacion

Supongamos que tenemos una clase de empleado:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

Aquí, los detalles de implementación de Nombre completo están ocultos para el usuario y no son accesibles directamente para el usuario, a diferencia de un atributo público.


Debes usar captadores y definidores cuando:

  • Estás tratando con algo que es conceptualmente un atributo, pero:
    • Su idioma no tiene propiedades (o algún mecanismo similar, como las trazas variables de Tcl), o
    • El soporte de propiedad de su idioma no es suficiente para este caso de uso, o
    • Las convenciones idiomáticas de su idioma (o, a veces, de su marco) alientan a los buscadores o configuradores para este caso de uso.

Así que esto rara vez es una pregunta general de OO; es una pregunta específica del idioma, con diferentes respuestas para diferentes idiomas (y diferentes casos de uso).

Desde el punto de vista de la teoría OO, los captadores y los instaladores son inútiles. La interfaz de su clase es lo que hace, no lo que es su estado. (Si no, ha escrito la clase incorrecta). En casos muy simples, lo que hace una clase es simplemente, por ejemplo, representar un punto en coordenadas rectangulares, * los atributos son parte de la interfaz; los captadores y los instaladores simplemente nublan eso. Pero en todos los casos, excepto en casos muy simples, ni los atributos ni los captadores y definidores forman parte de la interfaz.

Dicho de otra manera: si crees que los consumidores de tu clase ni siquiera deberían saber que tienes un spamatributo, mucho menos poder cambiarlo sin querer, entonces darles un set_spammétodo es lo último que quieres hacer.

* Incluso para esa clase simple, es posible que no necesariamente desee permitir la configuración de los valores xy y. Si esto es realmente una clase, ¿no debería tener métodos como translate, rotate, etc.? Si es solo una clase porque su idioma no tiene registros / estructuras / tuplas con nombre, entonces esto no es realmente una cuestión de OO ...

Pero nadie está haciendo diseño general de OO. Están haciendo diseño e implementación, en un lenguaje específico. Y en algunos idiomas, los captadores y los instaladores están lejos de ser inútiles.

Si su idioma no tiene propiedades, entonces la única manera de representar algo que es conceptualmente un atributo, pero en realidad se calcula, o valida, etc., es a través de captadores y definidores.

Incluso si su idioma tiene propiedades, puede haber casos en que sean insuficientes o inapropiados. Por ejemplo, si desea permitir que las subclases controlen la semántica de un atributo, en idiomas sin acceso dinámico, una subclase no puede sustituir una propiedad computada por un atributo.

En cuanto al "¿qué pasa si quiero cambiar mi implementación más tarde?" Pregunta (que se repite varias veces con palabras diferentes tanto en la pregunta del OP como en la respuesta aceptada): si realmente es un cambio de implementación puro, y comenzó con un atributo, puede cambiarlo a una propiedad sin afectar la interfaz. A menos que, por supuesto, su idioma no lo admita. Así que este es realmente el mismo caso de nuevo.

Además, es importante seguir las expresiones idiomáticas del lenguaje (o marco) que está utilizando. Si escribe un código hermoso de estilo Ruby en C #, cualquier desarrollador de C # con experiencia que no sea usted tendrá problemas para leerlo, y eso es malo. Algunos idiomas tienen culturas más fuertes en torno a sus convenciones que otros, y puede que no sea una coincidencia que Java y Python, que se encuentran en extremos opuestos del espectro de cómo son los captadores idiomáticos, tengan dos de las culturas más fuertes.

Más allá de los lectores humanos, habrá bibliotecas y herramientas que esperan que usted siga las convenciones y haga su vida más difícil si no lo hace. Conectar los widgets de Interface Builder a cualquier cosa que no sean las propiedades de ObjC, o usar ciertas bibliotecas de simulacros de Java sin interlocutores, solo hace que tu vida sea más difícil. Si las herramientas son importantes para usted, no las pelee.


Además, esto es para "a prueba de futuro" su clase. En particular, cambiar de un campo a una propiedad es una ruptura ABI, por lo que si decide más adelante que necesita más lógica que solo "establecer / obtener el campo", entonces necesita romper ABI, que por supuesto crea problemas para cualquier cosa otra cosa ya compilada contra tu clase.


Desde el punto de vista del diseño de orientación de objetos, ambas alternativas pueden dañar el mantenimiento del código al debilitar la encapsulación de las clases. Para una discusión puede consultar este excelente artículo: http://typicalprogrammer.com/?p=23


El código evoluciona . privatees ideal para cuando necesita protección de miembros de datos . Eventualmente, todas las clases deben ser una especie de "miniprogramas" que tienen una interfaz bien definida que no se puede limitar a los internos .

Dicho esto, el desarrollo de software no consiste en establecer esa versión final de la clase como si estuvieras presionando una estatua de hierro fundido en el primer intento. Mientras trabajas con él, el código es más como arcilla. Evoluciona a medida que lo desarrolla y aprende más sobre el dominio del problema que está resolviendo. Durante el desarrollo, las clases pueden interactuar entre sí de lo que deberían (dependencia que planea descartar), fusionarse o separarse. Así que creo que el debate se reduce a las personas que no quieren escribir religiosamente

int getVar() const { return var ; }

Así que tienes:

doSomething( obj->getVar() ) ;

En lugar de

doSomething( obj->var ) ;

No solo es getVar()visualmente ruidoso, sino que da a esta ilusión que gettingVar()es de alguna manera un proceso más complejo de lo que realmente es. La manera en que usted (como el escritor de la clase) considera que la santidad vares particularmente confusa para un usuario de su clase si tiene un activador de passthru - entonces parece que está poniendo estas puertas para "proteger" algo que insiste que es valioso, (la santidad de var), pero aun así, usted acepta que varla protección no vale mucho por la capacidad de cualquiera para entrar y set varpor el valor que desee, sin que usted incluso se asuste en lo que está haciendo.

Así que programo de la siguiente manera (asumiendo un enfoque de tipo "ágil", es decir, cuando escribo el código sin saber exactamente lo que hará / no tengo tiempo o experiencia para planificar un conjunto de interfaces de estilo de cascada elaborado):

1) Comience con todos los miembros públicos para objetos básicos con datos y comportamiento. Esta es la razón por la que en todo mi código de "ejemplo" de C ++ notará que lo uso en structlugar de en classtodas partes.

2) Cuando el comportamiento interno de un objeto para un miembro de datos se vuelve lo suficientemente complejo (por ejemplo, le gusta mantener un interno std::listen algún tipo de orden), se escriben las funciones de tipo de acceso. Debido a que estoy programando por mi cuenta, no siempre configuro al miembro de privateinmediato, pero en algún lugar de la evolución de la clase, el miembro será "promovido" a uno protectedu otro private.

3) Las clases que están completamente desarrolladas y tienen reglas estrictas sobre sus aspectos internos (es decir , saben exactamente lo que están haciendo, y usted no debe "joder" (término técnico) con sus classcomponentes internos) reciben la designación, miembros privados predeterminados, y solo unos pocos miembros seleccionados pueden ser public.

Encuentro que este enfoque me permite evitar sentarme allí y escribir religiosamente a los getters / setters cuando muchos miembros de los datos se migran, cambian de lugar, etc. durante las etapas iniciales de la evolución de una clase.


En idiomas que no admiten "propiedades" (C ++, Java) o que requieren la recompilación de clientes al cambiar campos a propiedades (C #), es más fácil modificar los métodos de obtención / configuración. Por ejemplo, agregar lógica de validación a un método setFoo no requerirá cambiar la interfaz pública de una clase.

En los idiomas que admiten propiedades "reales" (Python, Ruby, ¿quizás Smalltalk?) No hay ningún punto para obtener / establecer métodos.


Hay una buena razón para considerar el uso de accesores si no hay herencia de propiedad. Vea el siguiente ejemplo:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}

Salida:

0
0
4

Los métodos de getter y setter son métodos de acceso, lo que significa que generalmente son una interfaz pública para cambiar miembros de clase privada. Utiliza los métodos getter y setter para definir una propiedad. Puede acceder a los métodos de captador y definidor como propiedades fuera de la clase, aunque los defina dentro de la clase como métodos. Esas propiedades fuera de la clase pueden tener un nombre diferente al nombre de la propiedad en la clase.

Hay algunas ventajas de usar los métodos de obtención y establecimiento, como la capacidad de permitirle crear miembros con una funcionalidad sofisticada a la que puede acceder como propiedades. También te permiten crear propiedades de solo lectura y solo escritura.

Aunque los métodos de obtención y configuración son útiles, debe tener cuidado de no abusar de ellos porque, entre otras cuestiones, pueden dificultar el mantenimiento del código en ciertas situaciones. Además, brindan acceso a la implementación de su clase, como miembros públicos. La práctica de la programación orientada a objetos desalienta el acceso directo a las propiedades dentro de una clase.

Al escribir clases, siempre se recomienda que las variables de instancia sean privadas y agregue los métodos de obtención y configuración en consecuencia. Esto se debe a que en varias ocasiones es posible que no desee que los usuarios cambien ciertas variables dentro de sus clases. Por ejemplo, si tiene un método estático privado que rastrea el número de instancias creadas para una clase específica, no desea que un usuario modifique ese contador utilizando el código. Solo la declaración del constructor debe incrementar esa variable cada vez que se llama. En esta situación, puede crear una variable de instancia privada y permitir un método de obtención solo para la variable de contador, lo que significa que los usuarios pueden recuperar el valor actual solo mediante el método de obtención y no podrán establecer nuevos valores utilizando el método setter.Crear un captador sin un definidor es una forma simple de hacer que ciertas variables en su clase sean de solo lectura.


Solo me gustaría lanzar la idea de anotación: @getter y @setter. Con @getter, deberías poder obj = class.field pero no class.field = obj. Con @setter, viceversa. Con @getter y @setter deberías poder hacer ambas cosas. Esto preservaría la encapsulación y reduciría el tiempo al no llamar a métodos triviales en tiempo de ejecución.


Una ventaja relativamente moderna de los getters / setters es que facilita la búsqueda de códigos en los editores de códigos etiquetados (indexados). Por ejemplo, si desea ver quién establece un miembro, puede abrir la jerarquía de llamadas del configurador.

Por otro lado, si el miembro es público, las herramientas no permiten filtrar el acceso de lectura / escritura al miembro. Así que tienes que andar con todos los usos del miembro.







abstraction