oop - property - Como evitar los getters y setters.
getters setters python (13)
He leído en muchos lugares que "los captadores y los setters son malos". Y entendí por qué así. Pero no sé cómo evitarlos por completo. Say Item es una clase que tiene información sobre el nombre del artículo, cantidad, precio, etc ... y ItemList es una clase, que tiene una lista de artículos. Para encontrar el total general:
int grandTotal() { int total = 0; for (Item item: itemList) total += item.getPrice(); return total; }
En el caso anterior, ¿cómo se evita getPrice ()? La clase Item proporciona getName, setName, etc ....
¿Cómo los evito?
He leído en muchos lugares que "los captadores y los setters son malos".
De Verdad? Eso me parece una locura. ¿Muchos? Nos muestran uno Lo haremos pedazos.
Y entendí por qué así.
Yo no. Me parece una locura. O bien lo has entendido mal pero piensas que lo entendiste, o la fuente original es una locura.
Pero no sé cómo evitarlos por completo.
No deberias
¿Cómo evitar
getPrice
?
Mira, ¿por qué querrías evitar eso? ¿De qué otra manera se supone que debes sacar datos de tus objetos?
¿Cómo evitarlos?
No lo hagas Deja de leer loca charla.
¿Cuándo deberías usar getters y setters?
Los captadores y definidores son excelentes para configurar o determinar la configuración de una clase, o para recuperar datos de un modelo
Obtener el precio de un artículo es un uso totalmente razonable de un captador. Esos son los datos que deben estar disponibles y pueden implicar consideraciones especiales para protegerlos agregando validación o desinfección al configurador.
También puede proporcionar getters sin setters. No tienen que venir en parejas.
¿Cuándo no deberías usar getters y setters?
A veces los objetos se basan en propiedades internas que nunca serán expuestas. Por ejemplo, iteradores y colecciones internas. Exponer la colección interna podría tener consecuencias dramáticamente negativas e inesperadas.
También, por ejemplo, supongamos que se está comunicando a través de algún HttpURLConnection. Exponer el configurador para su HttpURLConnection significa que podría terminar con un estado muy extraño si la conexión se cambia mientras espera recibir datos. Esta conexión es algo que debe crearse en la creación de instancias o administrarse por completo internamente.
Resumen
Si tiene datos que son para todos los fines públicos, pero necesita ser administrado: use los métodos de obtención y configuración.
Si tiene datos que deben recuperarse, pero en ningún caso deben cambiarse: utilice un captador pero no un configurador.
Si tiene datos que deben configurarse para fines internos y nunca deben ser expuestos públicamente (y no pueden configurarse en la instanciación): use un configurador pero no un captador (el establecedor probablemente previene una segunda llamada que afecte la propiedad interna)
Si tiene algo que es completamente interno y ninguna otra clase necesita acceder o cambiarlo directamente, entonces no use ninguno.
No olvide que los configuradores y captadores pueden ser privados e incluso para propiedades administradas internamente, tener un configurador que administre la propiedad puede ser deseable. Por ejemplo, tomar una cadena de conexión y pasarla al configurador para HttpURLConnection.
También tenga en cuenta:
El artículo de Allen Holub Por qué los métodos de captador y colocador son malos parece ser la fuente del razonamiento de OP pero, en mi opinión, el artículo hace un mal trabajo al explicar su punto.
Edición: Resumen añadido
Edición 2: correcciones ortográficas
''Evil'' as .getAttention ()
Esto se ha discutido a menudo, e incluso quizás se volvió un poco viral, como resultado del término peyorativo "Evil" usado en el diálogo. Hay momentos en que los necesitas, por supuesto. Pero el problema es usarlos correctamente. Verás, la perorata del profesor Holub no se trata de lo que tu código está haciendo ahora , sino de encasillarte para que el cambio en el futuro sea doloroso y propenso a errores.
De hecho, todo lo que he leído de él lleva esto como tema.
¿Cómo se aplica ese tema al elemento de clase?
Una mirada al futuro del artículo.
Aquí está la clase de artículo de ficciones:
class Item{
private double price;
public void setPrice(final double price){
if(isValidPrice(price))
this.price = price;
else throw new IllegalArgumentException(price+" is not valid!");
}
public double getPrice(){
return this.price;
}
}
Todo esto está muy bien, pero sigue siendo "malo" en el sentido de que podría causarle mucho dolor en el futuro.
El duelo puede provenir del hecho de que un día el "precio" puede tener que tener en cuenta diferentes monedas (y quizás incluso esquemas de trueque más complejos). Al establecer que el precio sea doble, cualquier código que se escriba entre ahora y el ''apocalipsis'' (estamos hablando de maldad, después de todo) será el precio del cableado al doble.
Es mucho mejor (incluso Bueno, quizás) pasar un objeto de Precio en lugar de un doble. Al hacerlo, puede implementar fácilmente cambios en lo que quiere decir con "precio" sin romper las interfaces existentes.
La comida para llevar en getters y setters.
Si se encuentra usando getters y setters en tipos simples, asegúrese de considerar posibles cambios futuros en la interfaz. Hay una muy buena posibilidad de que no debas. ¿Está utilizando setName(String name)
? Debería considerar setName(IdentityObject id)
o incluso setIdentity(IdentityObject id)
en caso de que aparezcan otros modelos de identificación (avatares, claves, lo que sea). Seguro que siempre puedes ir y setAvatar
y setKey
en todo, pero al usar un objeto en tu firma de método, te setKey
más fácil extenderlo en el futuro a los objetos que pueden usar las nuevas propiedades de identidad y no romper los objetos heredados.
¿Cómo evitar getters y setters en Java? Usa el Proyecto Lombok
¿Cómo evitar getters y setters? Clases de diseño que realmente actúan sobre los datos que poseen.
Getters mienten sobre los datos de todos modos. En el ejemplo Item.getPrice()
, puedo ver que estoy recibiendo un int
. ¿Pero es el precio en dólares o centavos? ¿Incluye impuestos? ¿Qué pasa si quiero saber el precio en un país o estado diferente, puedo seguir usando getPrice()
?
Sí, esto podría estar más allá del alcance de lo que el sistema está diseñado para hacer, y sí, podría terminar devolviendo el valor de una variable de su método, pero la publicidad de ese detalle de implementación mediante el uso de un getter debilita su API.
Cuando alguien te diga que los hechiceros y los setters son malos, piensa en por qué están diciendo eso.
Getters
¿Son malvados? No hay tal cosa como el mal en el código. El código es código y no es ni bueno ni malo. Es solo una cuestión de lo difícil que es leer y depurar.
En su caso, creo que está perfectamente bien usar un captador para calcular el precio final.
El mal"
Caso de uso: crees que quieres el precio de un artículo al comprar algo.
La gente a veces usa captadores de esta manera:
if(item.getPrice() <= my_balance) {
myBank.buyItem(item);
}
No hay nada malo con este código, pero no es tan sencillo como podría ser. Mira esto (enfoque más pragmático):
myBank.buyItem(item); //throws NotEnoughBalanceException
No es el trabajo de los compradores o de los cajeros verificar el precio de un artículo al comprar algo. Es en realidad el trabajo del banco. Imagina que el cliente A
tiene un SimpleBank.java
public class SimpleBank implements Transaction {
public void buyItem(Item item){
if(getCustomer().getBalance() >= item.getPrice()){
transactionId = doTransaction(item.getPrice());
sendTransactionOK(transactionId);
}
}
}
El primer enfoque parece estar bien aquí. ¿Pero qué NewAndImprovedBank.java
si el cliente B
tiene un NewAndImprovedBank.java
?
public class NewAndImprovedBank implements Transaction {
public void buyItem(Item item){
int difference = getCustomer().getBalance() - item.getPrice();
if (difference >= 0) {
transactionId = doTransaction(item.getPrice());
sendTransactionOK(transactionId);
} else if (difference <= getCustomer().getCreditLimit()){
transactionId = doTransactionWithCredit(item.getPrice());
sendTransactionOK(transactionId);
}
}
}
Podría pensar que está siendo defensivo cuando utiliza el primer enfoque, pero en realidad está limitando las capacidades de su sistema.
Conclusión
No solicite permiso, también item.getPrice()
como item.getPrice()
, pida perdón, también NotEnoughBalanceException
como NotEnoughBalanceException
.
De Verdad? Yo no creo eso. por el contrario, los captadores y establecedores te ayudan a proteger la consistencia de las variables.
La importancia de los captadores y definidores es proporcionar protección a los atributos privados para que no se pueda acceder directamente a ellos; es mejor crear una clase con el item
atributo en el que se incluyen los elementos get y set correspondientes.
El siguiente ejemplo es un brillante ejemplo de instaladores y preparadores de calderas.
class Item{
private double price;
public void setPrice(final double price){
this.price = price;
}
public double getPrice(){
return this.price;
}
}
Algunos codificadores piensan que esto se llama encapsulación, pero en realidad este código es exactamente equivalente a
class Item{
public double price;
}
En ambas clases, el price
no está protegido ni encapsulado, pero la segunda clase es más fácil de leer.
class Item{
private double price;
public void setPrice(final double price){
if(isValidPrice(price))
this.price = price;
else throw new IllegalArgumentException(price+" is not valid!");
}
public double getPrice(){
return this.price;
}
}
Esta es una encapsulación real, el invariante de la clase está protegido por setPrice
. Mi consejo: no escribas getters y setters ficticios, usa getters y setters solo si guardan el invariante de tu clase
Es una pena ver a una pequeña minoría vocal que critica a todos los " Captadores y Colocadores " que son malos debates. En primer lugar, el título del artículo es intencionalmente provocativo para atraerlo, al igual que cualquier publicación de blog. A su vez, he blogeado sobre esto antes y varios años después actualicé mis opiniones e ideas sobre esta pregunta . Resumiré lo mejor que pueda aquí.
- Getters y setters (accessors) no son malos
- Son "malvados" (innecesarios) la mayoría de las veces, sin embargo
- La encapsulación no es solo agregar accesores alrededor de campos privados para controlar el cambio, después de todo, no hay ningún beneficio en agregar / establecer métodos que solo modifican un campo privado
- Debe escribir la mayor cantidad de código posible con el principio de " Diga, no pregunte "
- Debe utilizar los accesores para el código del marco, los DTO, la serialización, etc. No trates de luchar contra esto.
- Sin embargo, desea que su lógica de dominio central (objetos de negocio) sea tan libre de propiedades como sea posible. Debes decirle a los objetos que hagan cosas, no verificar su estado interno a voluntad.
Si tiene una carga de accesores, esencialmente violará la encapsulación. Por ejemplo:
class Employee
{
public decimal Salary { get; set; }
// Methods with behaviour...
}
Este es un objeto de dominio de basura, porque puedo hacer esto:
me.Salary = 100000000.00;
Este puede ser un ejemplo simple, pero como cualquier persona que trabaje en un entorno profesional puede dar fe de ello, si existe algún código que sea público, la gente lo usará. No sería incorrecto que un desarrollador vea esto y comience a agregar cargas de cheques alrededor del código base utilizando el Salario para decidir qué hacer con el Empleado.
Un mejor objeto sería:
class Employee
{
private decimal salary;
public void GivePayRise()
{
// Should this employee get a pay rise.
// Apply business logic - get value etc...
// Give raise
}
// More methods with behaviour
}
Ahora no podemos confiar en que Salary sea de conocimiento público. Cualquier persona que desee dar un aumento salarial a los empleados debe hacerlo a través de este método. Esto es genial porque la lógica de negocios para esto está contenida en un solo lugar. Podemos cambiar este lugar y efecto en cualquier lugar donde se utilice el Empleado.
Getters y setters son malvados porque rompen la encapsulación y pueden exponer innecesariamente el estado interno de un objeto y permitir que se modifique de la manera que no debería ser. El siguiente artículo explica este problema:
http://programmer.97things.oreilly.com/wiki/index.php/Encapsulate_Behavior,_not_Just_State
La respuesta de Cloudanger es una, pero también debe darse cuenta de que la lista de elementos probablemente contendrá muchos objetos con la cantidad ordenada.
Solución: cree otra clase entre ellos que almacene su artículo en la lista de artículos y la cantidad ordenada para ese artículo (digamos que la clase se llama OrderLine).
OrderLine tendrá artículo y cantidad como campos.
Después de eso, codifique algo como calcular Total (int qty) en el Artículo que devuelve el precio * qty. Cree un método en OrderLine que llame a calculatotal (cantidad ordenada)
Pase el valor de retorno a la lista de elementos.
De esta manera, evitas getters. La Lista de Artículos solo sabrá el precio total. Su código debe vivir con sus datos. Pregunte al objeto quién tiene los datos para calcular el precio total en lugar de pedirle a ese objeto datos sin procesar para calcular su precio total.
Use una clase auxiliar de ShoppingCart
. El método item.addTo(ShoppingCart cart)
agregaría el precio a la totalSum
del carrito usando shoppingCart.addItem(Item item, int price)
La dependencia del Item
a ShoppingCart
no es desventajosa si los Item
están destinados a ser artículos de ShoppingCart
s.
En el caso de que los Item
vivan únicamente para ShoppingCart
y la clase de Item
sea pequeña, es más probable que tenga el Item
como una clase interna de ShoppingCart
, de modo que ShoppingCart
tenga acceso a las variables privadas de los artículos.
Otros pensamientos
También sería posible, aunque con un diseño bastante poco intuitivo, hacer que la clase de Item
cuente la suma ( item.calculateSum(List<Item> items)
( item.calculateSum(List<Item> items)
), ya que puede acceder a las partes privadas de otros artículos sin romper la encapsulación.
A otros preguntándose por qué los getters son malos. Considere el ejemplo dado donde el getPrice()
devuelve un entero. Si desea cambiarlo a algo mejor como BigDecimal
al menos o un tipo de dinero personalizado con moneda, entonces no sería posible ya que el tipo de retorno int
expone el tipo interno.
getPrice () está accediendo a una variable privada que estoy asumiendo.
Para responder a su pregunta directamente, haga pública la variable del precio y codifique algo parecido (la sintaxis puede variar según el idioma, el uso de punteros, etc.):
total += item.price;
Sin embargo, esto generalmente se considera mal estilo . Las variables de clase generalmente deben permanecer privadas.
Por favor, vea mi comentario sobre la pregunta.