¿Cuándo se justifican los captadores y establecedores?

175

Getters y setters a menudo son criticados por no ser OO adecuados. Por otro lado, la mayoría del código OO que he visto tiene extensores captadores y establecedores.

¿Cuándo se justifican getters y setters? ¿Intentas evitar usarlos? ¿Se usan en exceso en general?

Si su idioma favorito tiene propiedades (el mío), entonces esas cosas también se consideran captadores y establecedores para esta pregunta. Son lo mismo desde una perspectiva de metodología OO. Simplemente tienen una sintaxis más agradable.

Fuentes de crítica de Getter / Setter (algunas tomadas de comentarios para darles una mejor visibilidad):

Para expresar la crítica simplemente: Getters y Setters le permiten manipular el estado interno de los objetos desde el exterior del objeto. Esto viola la encapsulación. Solo el objeto mismo debe preocuparse por su estado interno.

Y un ejemplo Versión procesal de código.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Versión del código del mutador:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

Los captadores y definidores hicieron el código mucho más complicado sin permitir una encapsulación adecuada. Debido a que el estado interno es accesible para otros objetos, no ganamos mucho al agregar estos captadores y establecedores.

La pregunta se ha discutido previamente en Stack Overflow:

Winston Ewert
fuente
21
Getters and setters are often criticized as being not proper OO- Cita, por favor.
Robert Harvey
45
@ Job, ¿por qué porque tiene código? Es una buena pregunta y apesta a guerra santa si se toma en serio.
Peter Turner el
2
@Winston Ewert: "... la pregunta es si debería acceder a los datos dentro de la clase": bueno, nadie lo obliga a implementar captadores y establecedores para todas las variables miembro, solo implementa aquellos que necesita. Es por eso que usa getters y setters en lugar de variables de miembros públicos.
Giorgio
2
@ Winston Ewert: No veo cómo la ausencia de captadores / establecedores resolvería este problema: debe haber una forma de acceder a cada pieza de datos, de lo contrario, es inútil; La tarea de un buen diseño es decidir qué parte de los datos es accesible a qué parte del código. Si uno es un mal diseñador, lo será con o sin captadores y colocadores. Solo mis 2 centavos.
Giorgio

Respuestas:

162

Tener captadores y establecedores no interrumpe en sí mismo la encapsulación. Lo que rompe la encapsulación es agregar automáticamente un getter y un setter para cada miembro de datos (cada campo , en la jerga de Java), sin pensarlo. Si bien esto es mejor que hacer públicos todos los miembros de datos, está a solo un paso de distancia.

El objetivo de la encapsulación no es que no pueda saber o cambiar el estado del objeto desde fuera del objeto, sino que debe tener una política razonable para hacerlo.

  • Algunos miembros de datos pueden ser completamente internos al objeto, y no deberían tener getters ni setters.

  • Algunos miembros de datos deben ser de solo lectura, por lo que pueden necesitar getters pero no setters.

  • Es posible que algunos miembros de datos deban mantenerse consistentes entre sí. En tal caso, no proporcionaría un setter para cada uno, sino un método único para configurarlos al mismo tiempo, de modo que pueda verificar la consistencia de los valores.

  • Es posible que algunos miembros de datos solo necesiten cambiarse de cierta manera, como aumentar o disminuir en una cantidad fija. En este caso, proporcionaría un método increment()y / o decrement()método, en lugar de un setter.

  • Sin embargo, otros pueden necesitar lectura y escritura, y tendrían un captador y un definidor.

Considere un ejemplo de a class Person. Digamos que una persona tiene un nombre, un número de seguro social y una edad. Digamos que no permitimos que las personas cambien sus nombres o números de seguridad social. Sin embargo, la edad de la persona debe incrementarse en 1 cada año. En este caso, proporcionaría un constructor que inicializaría el nombre y el SSN a los valores dados, y que inicializaría la edad a 0. También proporcionaría un método incrementAge(), que aumentaría la edad en 1. También proporcionaría captadores para los tres. No se requieren setters en este caso.

En este diseño, permite que el estado del objeto se inspeccione desde fuera de la clase, y permite que se cambie desde fuera de la clase. Sin embargo, no permite que el estado se cambie arbitrariamente. Existe una política que establece efectivamente que el nombre y el SSN no se pueden cambiar en absoluto, y que la edad se puede aumentar en 1 año a la vez.

Ahora digamos que una persona también tiene un salario. Y las personas pueden cambiar de trabajo a voluntad, lo que significa que su salario también cambiará. ¡Para modelar esta situación no tenemos otra manera que proporcionar un setSalary()método! Permitir que el salario se cambie a voluntad es una política perfectamente razonable en este caso.

Por cierto, en su ejemplo, le daría a la clase Fridgelos métodos putCheese()y takeCheese(), en lugar de get_cheese()y set_cheese(). Entonces todavía tendrías encapsulación.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}
Dave Jarvis
fuente
44
Por favor, no tomes mi ejemplo de Fridge demasiado en serio. :) Un refrigerador real debería ser un objeto contenedor, esperaría que supiera cómo sostener objetos sin preocuparse de qué eran exactamente. Es decir, sería como una ArrayList. En cuanto a lo que lo convierte en un refrigerador, digamos que serializa objetos en el disco cuando se almacena para que puedan sobrevivir a la falla del sistema. Okay. Basta de tomar en serio mi ejemplo de nevera.
Winston Ewert
66
Pero, ¿no debería saber el refrigerador las fechas de vencimiento, de modo que pueda decirle que el queso se echó a perder y se debe tirar? :) Ok, tenemos que detener esto! :)
Dima
10
Una discusión bastante interesante de Fridge Logic que tienes aquí ...
Mason Wheeler
35
Jaja, me gustaría ver el anuncio para eso: "Es un tipo de refrigerador completamente nuevo: ¡Te arroja cosas cuando tratas de agarrar algo que no está allí! ¡De esta manera solo lo intentarás una vez! ¡llenando el refrigerador y deje que el refrigerador se preocupe por el comportamiento ilegal! "
gablin
42
El ejemplo lo tiene todo mal. No debe haber un campo Edad ni un método setAge (). La edad es una función de la fecha de nacimiento de la persona en comparación con algún punto en el tiempo. Si bien parece trivial, esto es exactamente lo que está mal con la práctica moderna de la mutabilidad total de los objetos y otros diseños malos, como se ve por los métodos get / set para campos privados en lugar de considerar cuidadosamente qué es realmente mutable, qué es un campo, cuál es el objeto debe conocer sus comportamientos y qué cambios de estado son realmente válidos (qué métodos setX destruyen por completo).
Darrell Teague
41

La razón básica para obtener y establecer en Java es muy simple:

  • Solo puede especificar métodos , no campos, en una interfaz.

Por lo tanto, si desea permitir que un campo pase a través de la interfaz, necesitará un método de lector y escritor. Estos se llaman tradicionalmente getX y setX para el campo x.

usuario1249
fuente
44
Esa es una limitación de idioma. La verdadera pregunta es si deberíamos permitir que otros objetos manipulen ese estado.
Winston Ewert
3
@ Peter Turner, claramente nada impide que un lenguaje tenga interfaces que incluyen propiedades. Debajo de las cubiertas que bien pueden implementarse como getter / setter, pero sería bastante fácil agregar soporte a las definiciones de interfaz para las propiedades.
Winston Ewert
2
@Winston, eventualmente tendrá que permitir que las clases pasen información de un lado a otro para realmente hacer el trabajo. ¿Qué sugerirías en su lugar?
66
Se supone que un objeto proporciona una interfaz de nivel superior a su interior. Getters y Setters tienden a ser una interfaz de bajo nivel. Por ejemplo, suponga que tiene una clase que implementa un árbol binario. Podría tener funciones como GetLeft (), GetRight (), GetValue () y GetKey () para navegar por el árbol. Pero ese es el enfoque completamente equivocado. Su clase de árbol binario debe proporcionar operaciones como Buscar, Insertar, Eliminar.
Winston Ewert
66
Para tomar otro ejemplo, considere una pieza de tetris. La pieza tetris tiene algún estado interno como block_type, rotación, posición. Podría tener getters como GetBlockType (), GetRotation (), GetPosition (). Pero realmente no deberías. Lo que debe tener es un método GetSquares () que devuelve una lista de todos los cuadrados ocupados por esta pieza. Tampoco debería tener cosas como SetPosition () o SetRotation (). En su lugar, debe tener operaciones como MoveLeft (), MoveRight (), Rotate (), Fall ().
Winston Ewert
20

De http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

Estilo JavaBean:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

Estilo OO:

connection.connect("dukie","duke");

Bueno, claramente prefiero el último enfoque; no sangra los detalles de implementación, es más simple y conciso, y toda la información necesaria se incluye con la llamada al método, por lo que es más fácil hacerlo bien. También prefiero configurar miembros privados utilizando parámetros en el constructor, siempre que sea posible.

Su pregunta es, ¿cuándo se justifica un getter / setter? Quizás cuando se necesita un cambio de modo, o si necesita interrogar un objeto para obtener información.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Al pensar en ello, cuando comencé a codificar en C #, escribí mucho código en el estilo Javabeans ilustrado anteriormente. Pero a medida que adquirí experiencia en el lenguaje, comencé a configurar más miembros en el constructor y a utilizar métodos que se parecían más al estilo OO anterior.

Robert Harvey
fuente
66
¿Por qué es el estilo OO "dar todos los argumentos como parámetros"?
55
Si bien su "estilo OO" está bien para 2 o 3 opciones, preferiría el estilo JavaBean una vez que supere eso. Tener 20 métodos sobrecargados para cada combinación posible de parámetros simplemente se ve terrible
TheLQ
1
@TheLQ: C # 4.0 hace esto más fácil al permitir parámetros opcionales y con nombre en llamadas de constructor y método.
Robert Harvey
77
@TheLQ Para eso es el Generador con sintaxis fluida, ver, por ejemplo , MapMaker de Guava .
maaartinus
55
@ Thorbjørn Ravn Andersen, yo diría que "dar todos los argumentos como parámetros" es mejor estilo OO que "llamar a los creadores de JavaBean" porque un creador implica que se está configurando un atributo subyacente, que por definición expone detalles internos. El uso del método connect (..) en el ejemplo no hace tal suposición; tal vez establezca los atributos de usuario y contraseña, tal vez no. Lo importante es que se concentre en el concepto correcto de OO: el mensaje "conectar".
Andres F.
13

¿Cuándo se justifican getters y setters?

Cuando el comportamiento "get" y "set" realmente coinciden con el comportamiento en su modelo, que prácticamente nunca es .

Cualquier otro uso es solo una trampa porque el comportamiento del dominio comercial no está claro.

Editar

Es posible que esa respuesta parezca frívola, así que déjame expandirme. Las respuestas anteriores son en su mayoría correctas, pero se centran en los paradigmas de programación del diseño OO y algunos de los que pierden el panorama general. En mi experiencia, esto lleva a las personas a pensar que evitar los gettings y setters es una regla académica para los lenguajes de programación OO (por ejemplo, la gente piensa que reemplazar getters con propiedades es grandioso)

De hecho, es una consecuencia del proceso de diseño. No tiene que entrar en interfaces y encapsulación y patrones, discutiendo si rompe o no estos paradigmas y qué es o no es buena programación OO. El único punto que en última instancia importa es que si no hay nada en su dominio que funcione de esta manera al ponerlos, no está modelando su dominio.

La realidad es que es muy poco probable que cualquier sistema en su espacio de dominio tenga captadores y establecedores. No puede acercarse al hombre o la mujer responsable de la nómina y simplemente decir "Establezca este salario en X" o "Consígueme este salario" . Tal comportamiento simplemente no existe

Si está poniendo esto en su código, no está diseñando su sistema para que coincida con el modelo de su dominio. Sí, eso rompe las interfaces y la encapsulación, pero ese no es el punto. El punto es que estás modelando algo que no existe.

Además, probablemente se esté perdiendo un paso o proceso importante, porque probablemente haya una razón por la que no puedo simplemente acercarme a pagar y decir que establezca este salario en X.

Cuando las personas usan getters y setters, tienden a llevar las reglas de este proceso al lugar equivocado. Esto se está alejando de su dominio aún más. Usando el ejemplo del mundo real, es como un salario asumiendo que la persona aleatoria que entró tiene el permiso para obtener estos valores, de lo contrario no los pediría. No solo no es así como es el dominio, sino que de hecho miente sobre cómo es el dominio.

Cormac Mulhall
fuente
Esto no parece ofrecer nada sustancial sobre los puntos hechos y explicados en las 17 respuestas anteriores
mosquito
1
Las otras respuestas hablan sobre objetos, interfaces y paradigmas de otros lenguajes. Lo cual está bien, pero no es el tema central. Los captadores y establecedores no solo son malos porque rompen algunas convenciones de codificación, sino principalmente porque casi nunca encuentras este comportamiento en el modelo real que estás representando. Este punto tiende a perderse en la discusión sobre las mejores prácticas de codificación.
Cormac Mulhall
¿Cuál crees que es la interfaz para la nómina si no es el set/getsalario?
Winston Ewert
1
Si están envueltos alrededor de capas de autorización y restringidos a ciertas entidades externas, no son captadores y establecedores. No digo que no tenga ningún método en el departamento de nómina que cambie el salario, pero ese método debe alinearse con los procesos internos establecidos en la propia empresa. Por ejemplo, la mayoría de las empresas establecen el salario en función del contrato de los empleados, que está autorizado por un gerente. Si el salario del empleado cambia, se redacta un nuevo contrato y un gerente debe firmarlo. Por lo tanto, la nómina debe esperar un objeto de contrato y algún objeto de autorización para cambiar la nómina
Cormac Mulhall
3
O para decirlo de otra manera, hay una gran diferencia entre set_salary (new_salary, employee_id) y Authorized_salary_update (employee_contract, authorising_manager). El proceso debe modelar el proceso interno del negocio
Cormac Mulhall
11

Como regla general, getters y setters son una mala idea. Si un campo no es lógicamente parte de la interfaz y lo hace privado, está bien. Si es lógicamente parte de la interfaz y la haces pública, está bien. Pero si lo hace privado y luego se da la vuelta y lo vuelve público de manera efectiva al proporcionar un captador y configurador, volverá a donde comenzó, excepto que su código ahora es más detallado y ofuscado.

Obviamente, hay excepciones. En Java, es posible que deba usar interfaces. La biblioteca estándar de Java tiene requisitos de compatibilidad con versiones anteriores tan extremas que superan las medidas normales de calidad del código. Incluso es posible que en realidad estés lidiando con el legendario pero raro caso en el que hay una buena posibilidad de que luego puedas reemplazar un campo almacenado con un cálculo sobre la marcha sin romper la interfaz. Pero estas son excepciones. Getters y setters son un antipatrón que necesita una justificación especial.

rwallace
fuente
66
Un captador y un definidor significan que el atributo, no el campo, es parte de la interfaz. Supongamos que getBirthDate () y SetBirthDate () toman "aaaa / mm / dd" pero el campo almacenado es el número de días desde 01/01/1000. Puede cambiar eso a 01/01/0001 y el getter y setter mantendrán la interfaz igual. Especialmente porque 'público' significa públicamente trasharable, las variables nunca deberían ser públicas; siempre use un configurador para validar el valor entrante, actualice los campos relacionados, convierta según sea necesario, etc. Intente usar un captador en caso de que cambie el almacenamiento interno.
Andy Canfield
@AndyCanfield, eso es un poco fuerte para decir que público significa públicamente traspasable ... Eso solo es así si lo programa mal. Si tiene un setter y se le envía un valor incorrecto, entonces podría lanzar una excepción a la que podría llamar destruir el programa, pero se cierra con un error También si el usuario del objeto puede darle un valor a una variable y puede darle un error entonces puede tener eso en cuenta y probarlo. Puede decir "ah pero solo tengo que hacer 1 prueba si uso un setter", pero tal vez su propio código le da a la variable un valor incorrecto ... por lo que el setter podría no reduzca sus pruebas. n con las pruebas, su programa no está "destrozado"
barlop
3

si el campo es accesible directamente o mediante el método no es realmente importante.

Los invariantes de clase (útiles) son importantes. Y para preservarlos, a veces necesitamos no poder cambiar algo desde afuera. P.ej. Si tenemos clase Cuadrado con ancho y alto separados, cambiar uno de ellos hace que se convierta en algo más que cuadrado. Por lo tanto, necesitamos un cambio de método en el lado Si fuera un rectángulo, podríamos tener setters / public field. Pero establecer más de lo que probaría si es mayor que cero sería mejor.

Y en lenguajes concretos (por ejemplo, Java) hay razones por las cuales necesitamos esos métodos (interfaces). Y otra razón para el método es la compatibilidad (fuente y binario). Entonces es más fácil agregarlos y luego pensar si el campo público sería suficiente.

por cierto. Me gusta usar clases simples de valor inmutable con campos finales públicos.

user470365
fuente
Esto es prácticamente lo único que debe evitarse. Ir del campo a la propiedad / getter / setter es un cambio radical en la mayoría de los idiomas.
MrDosu
2

Es posible que desee cambiar sus componentes internos a lo que sea mientras mantiene las interfaces iguales. Si sus interfaces no varían, las que codifique no se romperán. Todavía puede cambiar sus componentes internos como lo desee.

George Silva
fuente
1
Me sorprende que esta respuesta esté tan lejos. Getters y setters le permiten hacer más en el futuro (por ejemplo, disparar un evento, hacer más validación de entrada, hacer contabilidad interna) sin romper su API existente. Incluso con código no público, cuantas menos API tenga que cambiar, menos dependencias para actualizar y menos errores introducidos al realizar esas actualizaciones.
AmadeusDrZaius
0

Mi enfoque es este:

Cuando espero jugar con los datos más adelante, se justifica un getter / setter. Además, si se produce un cambio, a menudo inserto los datos en un captador / configurador.

Si es una estructura POD, dejo las ranuras disponibles.

En un nivel más abstracto, la pregunta es "quién administra los datos", y eso depende del proyecto.

Paul Nathan
fuente
0

Si usar getters y setters se siente complicado, el problema podría ser el lenguaje, no el concepto en sí.

Aquí está el código del segundo ejemplo escrito en Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

¿Te das cuenta de que se parece mucho al primer ejemplo en Java? Cuando los captadores y los setters son tratados como ciudadanos de primera clase, no son una tarea difícil de usar, y la flexibilidad adicional a veces puede ser una verdadera bendición; por ejemplo, podríamos decidir devolver un valor predeterminado para el queso en un refrigerador nuevo:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Por supuesto, habrá muchas variables que no deberían exponerse públicamente. Exponer innecesariamente las variables internas empeorará su código, pero difícilmente puede culpar a los captadores y establecedores.

Tobias Cohen
fuente
Eso está muy bien dicho. Se agregaron funciones de construcción porque queremos crear objetos inmutables mientras se construye gradualmente la receta: tal vez si agrega edulcorante artificial, no necesita agregar azúcar. Usando parámetros con nombre, e incluso con sobrecarga, es muy difícil construir un método que haga todo. Y, por supuesto, Java no tiene parámetros con nombre, por lo tanto, una razón más por la que las personas están usando el patrón Builder.
YoYo
0

Considere una Sizeclase que encapsula ancho y alto. Puedo eliminar los colocadores usando el constructor, pero ¿cómo me ayuda eso a dibujar un rectángulo Size? Ancho y alto no son datos internos de la clase; son datos compartidos que deben estar disponibles para los consumidores de Size.

Los objetos consisten en comportamientos y estados, o atributos. Si no hay un estado expuesto, solo los comportamientos son públicos.

Sin estado, ¿cómo ordenarías una colección de objetos? ¿Cómo buscarías una instancia específica de un objeto? Si solo usa constructores, ¿qué sucede cuando su objeto tiene una larga lista de atributos?

Ningún parámetro de método debe consumirse sin validación. Por lo tanto, es pereza escribir:

setMyField(int myField){
    this.myField = myField;
}

Si lo escribe de esa manera, entonces al menos se ha preparado para usar el configurador para la validación; es mejor que un campo público, pero apenas. Pero al menos tiene una interfaz pública consistente que puede volver y poner en reglas de validación sin romper el código de sus clientes.

Getters, setters, propiedades, mutadores, llámalos como quieras, pero son necesarios.

Dale
fuente
Pero al menos tiene una interfaz pública consistente que puede volver y poner en reglas de validación sin romper el código de sus clientes. Eso no es cierto. Agregar reglas de validación más adelante podría "romper el código de sus clientes". Tome una intvariable de instancia, por ejemplo, e imagine que decide agregar una regla de validación para aceptar solo valores no negativos. El problema es que el código de su cliente ya puede contar con la posibilidad de establecer en un valor negativo ...
jub0bs
0

Si getters y setters violan la encapsulación y el verdadero OO, entonces estoy en serios problemas.

Siempre sentí que un objeto representa lo que se te viene a la mente que necesitas que haga.

Acabo de terminar de escribir un programa que genera laberintos en Java, y tengo una clase que representa "Cuadrados de laberintos". Tengo datos en esta clase que representan coordenadas, muros y booleanos, etc.

¡Tengo que tener alguna forma de cambiar / manipular / acceder a estos datos! ¿Qué hago sin captadores y colocadores? Java no usa propiedades y configurar todos mis datos que son locales para esta clase en público es DEFINITIVAMENTE una violación de encapsulación y OO.

Bryan Harrington
fuente
66
Aquí está la pregunta: si hiciera públicos todos esos campos y dejara de usar getters y setters, ¿cómo sería diferente su código?
Winston Ewert
En teoría, no lo es. ¿Por qué incluso hacer una clase en ese punto? El mismo argumento podría hacerse con cualquier cosa. Pero la publicación debajo de la mía parece haberlo dicho con más elegancia. (Los captadores y establecedores son una forma de entregar un valor de forma segura a un objeto, o recuperar un valor de ese objeto.) La publicación continúa ...
Bryan Harrington
3
Señalaría: ese cartel finalmente estuvo de acuerdo conmigo y publicó otra respuesta al respecto. Esencialmente, usar getters y setters es lo mismo que manipular los datos directamente. Realmente no obtienes OOness haciendo eso. Para ser OO, debe proporcionar una interfaz de nivel superior a los datos.
Winston Ewert
44
Creo que la mejor manera de describirlo sería decir que debe diseñar sus interfaces desde afuera hacia adentro, no desde adentro hacia afuera. La interfaz que proporciona su objeto debe estar dictada por cómo se usa el objeto y no cómo se implementa. Por lo tanto, no escriba getters y permita que un objeto externo interprete los datos. Descubra qué pregunta necesitan responder y escriba un método que responda esa pregunta. No permita que un setter modifique el estado de los objetos externos, descubra qué tipo de manipulaciones necesitan realizar y escriba métodos que lo hagan.
Winston Ewert
2
En algunos casos, Getters y Setters son el mejor método. A veces son la mejor interfaz que puede proporcionar. Sin embargo, en muchos casos no lo son. En muchos casos, solo está filtrando detalles de implementación en otras clases.
Winston Ewert
-1

En primer lugar, hay dos tipos de objetos que comentaré, los tipos de valor y servicio.

Los tipos de servicio nunca deberían tener establecedores, las dependencias que requieran no deberían ser obtenibles. La mejor manera de pasar dependencias es a través de un constructor o una fábrica de esa manera, todas las instancias están completamente formadas desde el principio, así de simple.

Los tipos de valor también deben ser inmutables, por otro lado, hay momentos en que esto no es práctico, como un ORM, o alguna otra asignación, como de un widget a un objeto. Todos los demás tipos de valores que se pasan por el sistema de una capa o parte a otra deben ser inmutables y no deben tener configuradores.

mP01
fuente
Sugeriría un tercer tipo: un contenedor mutable, cuyo propósito es contener uno o más valores; en muchos casos, no se debe esperar que un contenedor tenga mucho en el camino de la lógica o la validación. El código que necesita un método para calcular seis enteros positivos puede pasar un contenedor de seis enteros al método que almacenará los resultados allí. La responsabilidad de garantizar que los números sean positivos generalmente debe recaer en la persona que llama o en el método llamado, no con el contenedor.
supercat
-1

Un Getter / Setter está justificado como cualquier otro método público está justificado. La justificación se debe principalmente a que queremos proporcionar una interfaz con el mundo exterior que defina cómo nuestra clase interactúa con otras clases. Hacemos esto principalmente porque queremos reducir el acoplamiento entre entidades separadas. Reducir el acoplamiento es bueno por muchas razones:

  • Puedo usar desde mi código actual la misma interfaz de clase aunque, mientras tanto, esa clase agregó nuevos métodos (pero conservó la interfaz anterior)
  • Puede cambiar la representación interna de una clase sin afectar a sus consumidores.
  • Reduzca la posibilidad de errores: si hace que sus datos internos sean privados, puede estar seguro de que el código externo no interferirá con sus datos internos
Ghita
fuente
-1

Sí, getters and setters es un antipatrón en la programación orientada a objetos y nunca debe usarse: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . En pocas palabras, no encajan en el paradigma orientado a objetos porque lo alientan a tratar un objeto como una estructura de datos. Lo cual es un gran error.

yegor256
fuente
2
Esto no parece ofrecer nada sustancial sobre los puntos hechos y explicados en las 18 respuestas anteriores
mosquito
44
También es un poco fuerte decir que nunca .
Winston Ewert
-4

Espero que cualquier cosa que el IDE desarrollado por la compañía que desarrolla su lenguaje de programación genere automáticamente no viole los "Principios de Orientación de Objetos".

Dicho esto, cada vez que tenga una variable de ámbito público, use un getter y un setter y estará dorado. Me parece ser el elemento necesario del principio de encapsulación extremadamente orientado a objetos, incluso si solo parece un montón de código basura.

La razón para usarlos es que la encapsulación adecuada aún puede tener lugar y siempre y cuando haya abstraído la capa que le permite a la persona que hace el mono sentir su objeto, puede quitarlo sin que él lo sepa.

Baste decir que una casa dividida no puede sostenerse, uno de los inquilinos de OO no se volverá contra sí mismo y no puede servir tanto la encapsulación como la optimización prematura.

Peter Turner
fuente
44
¿Y qué gané exactamente al agregar mucho código para llamar a setFoo () y getFoo () cada vez que trato con la variable?
Winston Ewert
77
Aquí está la cosa, no creo que consigas encapsulación. Todavía está manipulando el estado del objeto desde otros lugares. Simplemente lo estás haciendo de manera más vergonzosa. Para obtener la encapsulación, debe centralizar la manipulación de valores en la clase que posee ese valor. Cualquier otra cosa es solo código de procedimiento en ropa OO. (No tiene nada de malo, pero es lo que es).
Winston Ewert
2
La ventaja de la encapsulación es que toda la lógica relacionada con valores particulares está en un solo lugar (más o menos). No entiendo eso con getters y setters. Por lo tanto, no creo que obtenga muchos beneficios para ellos.
Winston Ewert
1
Sin embargo, estaré de acuerdo en que prefiero tener un código con getters y setters que uno que manipule datos libremente a través del acceso público. De esa manera, al menos puedo adjuntar la validación de los datos entrantes.
Winston Ewert
3
Ciertamente no soy un fanático de la verbosidad. Pero mi objeción es en realidad la gente que hace que sus variables sean privadas y luego llaman a los captadores y establecedores libremente y terminan prácticamente sin diferencias. Si quieres usar getters / setters dentro de tu clase, está bien. La pregunta es ¿por qué? El principal beneficio que conozco es que puede hacer alguna validación en los datos para detectar errores antes. Esto es menos útil cuando todo el código que manipula los datos está en el mismo lugar. Pero aún puede ser útil. Idealmente, tiene un lenguaje que admite propiedades y puede evitar la verbosidad.
Winston Ewert
-4

Pensé que tendríamos más respuestas habituales aquí?

Getters y setters son en general extremadamente OOP (cualquiera que diga algo más está mal, así de simple).

Aunque debe recordar no ser demasiado ingeniero, nunca haga un getter o setter que no sea necesario. Cualquier información que no vaya a utilizar en otra clase para la que no debería tener ningún captador o definidor.

Sin embargo, la razón por la que no hace públicos los campos y en su lugar tiene captadores y establecedores es principalmente:

  • No es posible realizar pruebas adecuadas mediante el uso de campos. Getters / setters son mucho mejores en ese sentido.

  • Es imposible usar correctamente los campos en una configuración de programación en tiempo real. Getters / setters sincronizados es el camino a seguir.

  • El tema de la interfaz (ya explicado, así que no lo haré).

  • Es más fácil de usar cuando se reutiliza correctamente el sistema.

  • Es mucho más difícil saber qué clases están usando su clase y qué se romperá si cambia un campo que un captador / establecedor.

Por lo tanto, la única vez que usa un campo público en lugar de un captador / definidor es cuando no realiza un proyecto oficial, sino que lo hace por diversión y no puede molestarse con el captador / definidor.

Skarion
fuente
2
+1 para verificabilidad. Sorprendido de que nadie lo haya mencionado antes. Otra característica útil de las propiedades es depurarlas. (mucho más fácil insertar un punto de interrupción en el código que requerir puntos de interrupción de hardware / memoria para los campos).
Mark H
99
La objeción a los captadores y establecedores es que a menudo se utilizan para seguir la letra de la ley OOP mientras ignoran el espíritu y la intención. OOP dice que no debes permitir que otros objetos se contaminen con tus elementos internos. Si define getters y setters que le permiten hacer eso de todos modos, terminará violando OOP.
Winston Ewert
66
Estoy de acuerdo en que hay muchos beneficios de captadores y establecedores sobre campos públicos simples. Sin embargo, no estoy claro cómo las pruebas son mejores con ellos. ¿Podrías explicar?
Winston Ewert
44
@Skarion: No estoy de acuerdo con tu respuesta. Parece que presenta dos alternativas: ya sea usando getters / setters o haciendo públicos los campos. Pero hay una tercera opción: ¡no usar getters / setters ni exponer campos! ¿Por qué necesitaría probar el estado de un campo interno? ¿No deberías estar probando la API pública de tu objeto?
Andres F.