¿Cambiar el nombre de un método puede preservar la encapsulación?

9

Estaba leyendo esta página , sobre cuándo se justifican los captadores / establecedores, y el OP dio el siguiente ejemplo de código:

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);        
}

La respuesta aceptada dice:

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.

¿Cómo se preserva la encapsulación al renombrarla de get / set a putCheese()/ takeCheese()Obviamente está obteniendo / estableciendo un valor, entonces, ¿por qué no simplemente dejarlo como get / set?

En la misma respuesta también dice:

Tener captadores y establecedores no rompe 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.

En este caso, solo tenemos una variable, cheesey es posible que desee tomar y devolver el queso a la nevera, por lo que se justifica un par get / set en este caso.

ReinaSvetlana
fuente
77
putCheeseagregaría queso al refrigerador y takeCheeselo eliminaría: estas son abstracciones orientadas al dominio (de nivel superior), en lugar de captadores y establecedores de campos de objetos (que son abstracciones de programación de computadora (de bajo nivel)).
Erik Eidt

Respuestas:

17

Creo que te estás perdiendo el punto. No significa que debas cambiar el nombre del setter y getter, sino tener métodos que agreguen y retiren elementos del refrigerador. es decir

public class Fridge
{
    private int numberOfCheeseSlices;

    public void AddCheeseSlices(int n)
    {
         if(n < 0) { 
             throw new Exception("you cant add negative cheese!");
         }
         if(numberOfCheeseSlices + n > this.capacityOfFridge) { 
             throw new Exception("TOO MUCH CHEESE!!");
         }
         ..etc
         this.numberOfCheeseSlices += n;
    }
}

Ahora la variable privada está encapsulada. No puedo configurarlo para lo que quiera, solo puedo agregar y quitar lonchas de queso de la nevera utilizando los métodos, lo que a su vez garantiza que se apliquen las reglas lógicas comerciales de queso que quiera.

Ewan
fuente
2
Correcto, pero aún puede dejarlo ya setCheese()que tiene la misma lógica en el setter. Para mí, de todos modos, estás tratando de ocultar el hecho de que estás usando un setter cambiando el nombre del método, pero obviamente obtienes / configura algo.
QueenSvetlana
21
@QueenSvetlana no es un setter. Es una operacion. Le estás diciendo al refrigerador "aquí hay otro queso", y lo está agregando a su recuento interno de queso encapsulado. Un setter diría "ahora tienes 5 quesos". Estas son operaciones funcionalmente diferentes, no solo nombres diferentes.
Ant P
1
podrías llamarlo setCheese, pero eso sería confuso ya que no establecería el valor del queso.
Ewan
66
Tenga en cuenta que hay un error de desbordamiento de enteros aquí. Si ya hay más de 0 quesos, AddCheese(Integer.MAX_VALUE)debería fallar, pero no lo hará.
user253751
3
eso estaría totalmente cubierto en la sección ..etc
Ewan
5

Getters y setters rompen la encapsulación cada vez , por definición. Lo que podría argumentarse es que a veces necesitamos hacer eso. Con eso fuera del camino, aquí están mis respuestas:

¿Cómo se preserva la encapsulación renombrándola de get / set a putCheese () / takeCheese () Obviamente está obteniendo / estableciendo un valor, entonces, ¿por qué no simplemente dejarlo como get / set?

La diferencia está en la semántica, es decir, el significado de lo que escribes. La encapsulación no se trata solo de proteger, sino de ocultar el estado interno. El estado interno ni siquiera debería conocerse en el exterior, sino que se espera que el objeto ofrezca métodos relevantes para el negocio (a veces denominado "comportamiento") para manipular / usar ese estado.

Por lo tanto get, y setson los términos técnicos y parecen no tener nada que ver con el dominio "nevera", mientras que puty takeen realidad podría tener algún sentido.

Sin embargo , si puto takehacer real sentido sigue dependiendo de los requisitos y no se puede juzgar objetivamente. Ver siguiente punto:

En este caso, solo tenemos una variable, queso, y es posible que desee tomar y devolver el queso a la nevera, por lo que se justifica un par get / set en este caso.

Eso no puede determinarse objetivamente sin más contexto. Su ejemplo contiene un go_shopping()método en otro lugar. Si eso es para lo que Fridgesirve, entonces no necesita geto set, lo que necesita es Fridge.go_shopping(). De esta manera, tiene un método que se deriva de los requisitos, todos los "datos" que necesita son locales y tiene un comportamiento real en lugar de una estructura de datos apenas velada.

Recuerde, no está creando un genérico, reutilizable Fridge. Está construyendo un solo Fridgepara sus requisitos. Cualquier esfuerzo dedicado a hacerlo más de lo que debe ser es realmente un desperdicio.

Robert Bräutigam
fuente
10
Getters and setters break encapsulation every single time- Eso está demasiado redactado para mi gusto. Los métodos (incluidos getters y setters) tienen el mismo propósito que las puertas en un concierto: permiten la entrada y salida ordenadas del lugar.
Robert Harvey
8
"Getters y setters rompen la encapsulación cada vez, por definición" - ¿por qué definición? Yo también tengo un problema con esto, porque getters y setters son solo parte de la interfaz pública, como cualquier otro miembro público; solo rompen la encapsulación si exponen detalles de implementación que se consideran internos por diseño (es decir, por decisión del programador (o de un equipo)). El hecho de que los captadores y los establecedores a menudo se agreguen al azar, con el control del acoplamiento como una ocurrencia tardía, es otra cuestión completamente diferente.
Filip Milovanović
2
@ FilipMilovanović Punto justo. Como mencioné en la respuesta, "encapsulación" se utiliza para ocultar elementos internos del objeto (== estado del objeto == variables de instancia). Getters y setters publican objetos internos. Por lo tanto, según estas definiciones, están en conflicto perpetuo. Ahora, si a veces necesitamos romper la encapsulación es un asunto diferente, que debe manejarse por separado. Para ser claros, al decir que los setters / getters siempre rompen la encapsulación, no digo que siempre sea "incorrecto", si eso ayuda.
Robert Bräutigam
55
getters y setters no "rompen la encapsulación literalmente siempre". Los captadores y establecedores a menudo ni siquiera se refieren a miembros debajo directamente, realizan algún tipo de operación, o solo están definidos exclusivamente, es posible que solo tenga un captador o que solo tenga un establecedor. Rompe la encapsulación cuando getters y setters no hacen nada más que el acceso de miembros y ambos están definidos para los mismos campos. Incluso esto puede ser necesario para los administradores de bibliotecas que no desean romper las ABI / API con cambios internos y evitar la recompilación del cliente.
cuando el
44
Ok, getters y setter solo rompen la encapsulación el 99% del tiempo. ¿Contento? Y, al exponer el tipo de objeto encapsulado, definitivamente rompen la encapsulación. Una vez que tienes una public int getFoo(), es casi imposible, en la práctica, cambiar a otro tipo.
user949300
3

Casi todo esto muestra un malentendido fundamental de la encapsulación y cómo se aplica.

La respuesta inicial de que estaba rompiendo la encapsulación es incorrecta. Es posible que su aplicación necesite simplemente establecer el valor del queso en el refrigerador en lugar de aumentar / disminuir o agregar / eliminar. Además, no es semántica, no importa cómo lo llame, si necesita acceder y / o cambiar atributos, no interrumpe la encapsulación al proporcionarlos. Finalmente, la encapsulación no se trata realmente de "esconderse", se trata de controlar el acceso al estado y los valores que no deberían ser públicos o manipulados fuera de la clase, al tiempo que se los otorga a aquellos que deberían hacerlo y realizar la tarea disponible internamente.

Un getter o setter no rompe la encapsulación cuando existe una necesidad legítima de obtener o establecer un valor. Es por eso que los métodos pueden hacerse públicos.

La encapsulación se trata de mantener los datos y los métodos que modifican esos datos directamente juntos en un lugar lógico, la clase.

En este caso particular, existe claramente la necesidad de cambiar el valor del queso en la aplicación. Independientemente de cómo se haga esto, a través de get / set o add / remove, siempre que los métodos estén encapsulados en la clase, está siguiendo el estilo orientado a objetos.

Para aclarar, daré un ejemplo de cómo se rompe la encapsulación al proporcionar acceso independientemente del nombre del método o la ejecución lógica.

Digamos que su refrigerador tiene una "vida útil", solo una serie de tics antes de que el refrigerador deje de estar operativo (por el argumento, el refrigerador no puede repararse). Lógicamente, no hay forma de que un usuario (o el resto de su aplicación) pueda cambiar este valor. Debería ser privado. Solo sería visible a través de decir un atributo público diferente conocido como "isWorking". Cuando expira la vida útil, internamente el refrigerador se pone a trabajar en falso.

La ejecución de la cuenta regresiva de por vida y su activación del interruptor isWorking es todo interno en el refrigerador, nada afuera podría / debería ser capaz de afectar el proceso. isWorking solo debe ser visible, por lo tanto, un captador no rompe la encapsulación. Sin embargo, agregar accesores para elementos del proceso de por vida rompería su encapsulación.

Como la mayoría de las cosas, la definición de encapsulación no es literal, es relativa. ¿Deberías poder ver X fuera de la clase? ¿Deberías poder cambiar Y? ¿Es todo lo que se aplica a su objeto aquí en esta clase o la funcionalidad se extiende a través de múltiples clases?

usuario343330
fuente
Miremos pragmáticamente. Está diciendo que la encapsulación no está rota si el código está destinado de esa manera, o si hay una razón "legítima". Básicamente, eso significa que nunca podemos decir que la encapsulación está rota, porque el desarrollador solo tiene que decir que "pretendió" de esa manera, o que hubo una razón "legítima". Entonces, esta definición es básicamente inútil, ¿no?
Robert Bräutigam
No, digo que la encapsulación no se rompe simplemente al proporcionar acceso. Y no tiene nada que ver con lo que dice un programador, sino cuáles son las necesidades de la aplicación. Una aplicación necesita tener acceso para manipular los objetos, la encapsulación se trata de controlar el acceso. Una aplicación puede necesitar "establecer" un atributo de un objeto, pero la lógica y / o los cálculos requeridos para realizar esa operación "establecer" deben encapsularse en la clase de objeto.
user343330
Creo que aquí es donde diferimos fundamentalmente, usted dice: "Una aplicación puede necesitar" establecer "un atributo de un objeto ...". No lo creo. Elegir un diseño que implique establecer u obtener un atributo depende del desarrollador. Es una eleccion! Hay muchas formas de codificar algo, no todas implican obtener o establecer valores de variables de instancia.
Robert Bräutigam
Podría ser ... Por lo general, el diseño se basa en satisfacer una necesidad, ya sea que el programador elija si se basa en el entorno empresarial. Sin embargo, en cualquier caso, si la aplicación tiene una necesidad fundamental de manipular un valor que es parte de un objeto, entonces debe hacer que esa funcionalidad sea accesible de alguna manera. Proporcionar un punto de entrada para hacer el trabajo no es romper la encapsulación. Toma una aplicación de ventas. En algún momento de su transacción debe calcular el impuesto, haciendo que en el objeto de transacción lo mantendría encapsulado, pero la aplicación debe ser capaz de estado transaction.CalcTax
user343330
Los establecedores son malos ejemplos debido a su simplicidad, piense en términos de una función que hace mucho más trabajo que resulta en la actualización de un atributo de objeto frente a algo fuera de la clase que hace el trabajo y luego dice object.attribute = x. La primera es una buena encapsulación mientras proporciona el acceso adecuado, la segunda no. En cuanto a los captadores, ¿la aplicación necesita saber exactamente esa parte de un objeto para hacer algo más que no pueda estar contenido en la clase de objeto? en caso afirmativo, exponerlo no interrumpe su encapsulación. si no, entonces
encapsúlelo
1

No se trata solo de renombrar un método. Los dos métodos funcionan de manera diferente.

(Imagina esto en tu mente)

get_cheese y set_cheese expone el queso. putCheese () y takeCheese () mantienen el queso oculto y se encarga de administrarlo y le dan al usuario una forma de manejarlo. El observador no ve el queso, solo ve dos métodos para manipularlo.

Daneesha
fuente