¿Los getters protegidos triviales son excesivos?

8

Algo en lo que realmente no había pensado antes (sintaxis AS3):

private var m_obj:Object;
protected function get obj():Object
{
    return m_obj;
}

private var m_str:String;
protected function get str():String
{
    return m_str;
}

Al menos las subclases no podrán establecer m_obj o m_str (aunque aún podrían modificar m_obj).

Mi pregunta: ¿Es esto simplemente una exageración descarada?


La pregunta de este Programador: ¿ Cuándo o por qué debería uno usar getters / setters para propiedades de clase en lugar de simplemente hacerlas públicas? ha sido sugerido como un duplicado.

Esa pregunta es diferente porque solo aborda las propiedades públicas y privadas y si una propiedad debe envolverse con captadores y establecedores. Para mi pregunta, me estoy centrando en variables protegidas y cómo las clases heredadas interactuarían con esas variables.

Entonces la implementación alternativa sería:

protected var m_obj:Object; //more accessible than a private variable with a protected getter
protected var m_str:String; //more accessible than a private variable with a protected getter

Mi pregunta es similar porque estoy hablando sobre el uso de getters triviales protegidos para evitar que las subclases escriban en las variables, pero para permitirles el acceso a todos los demás.
En lenguajes como AS3, esto incluso les permitirá realizar cambios en las variables de objeto mutable, siempre que las referencias mismas no se modifiquen.

Panzercrisis
fuente
2
pregúntese; es peligroso que las subclases pueden ajustar a algo arbitrario si la respuesta es sí, entonces no es una exageración
trinquete monstruo
1
Edité la pregunta para dejar más claro lo que estoy hablando de no hacer en su lugar.
Panzercrisis

Respuestas:

6

¿Es esto simplemente una exageración descarada?

Muchas veces lo es, a veces no lo es.

Mantenerse m_objen un buen estado conocido ayuda a proteger el Principio de sustitución de Liskov, manteniendo su código más resistente y de mayor calidad. A veces puede confiar en los herederos para respetar ese comportamiento, a veces no puede hacerlo (ya sea debido al patrón de uso o la sutileza del contrato que implementa). Aunque este código / pregunta también tropieza con algunas de las razones de "¿Por qué Clean Code sugiere evitar las variables protegidas?"

Telastyn
fuente
¿A veces vale la pena cuando las subclases aún pueden hacer casi cualquier cosa que quieran m_obj excepto establecerlo en nulo u otra instancia?
Panzercrisis
@Panzercrisis: depende del idioma. En lenguajes no gestionados por memoria, establecerlo como nulo / algo más es un gran problema.
Telastyn
13

Los captadores protegidos triviales tienen al menos dos ventajas prácticas sobre una variable expuesta:

  • Para la depuración, son un lugar perfecto para poner un punto de interrupción condicional que se activa cuando "ese valor divertido entra en su programa". Es mucho más difícil obtener el mismo control si accede directamente al valor.

  • Para burlarse de un objeto, si está utilizando este método para probar descendientes.

El costo de usar getters es prácticamente cero, por lo que no hay una razón real para no usarlos. ¿Esta ahí?

Michael Le Barbier Grünewald
fuente
Gracias. Sin embargo, la pregunta ha sido editada para mayor claridad.
Panzercrisis
¡Mi respuesta no se refiere al atributo publico protecteddel getter! :-)
Michael Le Barbier Grünewald
Oh, perdon por eso.
Panzercrisis
44
El costo está en el código hinchado. El costo no es grande, pero es mayor que cero. Claro, los captadores son triviales, pero deben pasarse por alto cada vez que alguien trabaja en la clase. Y pueden estar equivocados: get x() ... { return _y; } nadie escribiría eso, pero podría suceder debido a un corte y pegado incorrecto.
Kevin Cline
3
Sin embargo, es una compensación entre diferentes costos. Y en mi experiencia, el costo de rastrear errores donde las subclases comprometen a su clase principal de manera sutil es enorme en comparación con el costo de mantener captadores y establecedores.
Julia Hayward
7

No importa si usa getters o acceso de campo, cualquier revisor experimentado descubrirá el problema en su diseño y se quejará de que sus objetos exponen datos en lugar de comportamiento.

Tenga en cuenta que apelar a "protegido" no ayudará, ya que el propósito de la herencia es permitir que las subclases ajusten / sintonicen el comportamiento del padre ( LSP ), no justificar el desorden indiscriminado con los datos del padre. Como Bertrand Meyer lo dice,

Ni el principio de Open-Closed ni la redefinición en la herencia es una forma de abordar los defectos de diseño ... (La única excepción potencial para esta regla es el caso de un software defectuoso que no puede modificar).

El razonamiento de "los captores son malvados" se aplica independientemente de si lo deletreas getexplícitamente o lo enmascaras detrás del acceso al campo.

ver este artículo o este artículo de Alan Holub ...

No debe usar métodos de acceso (captadores y establecedores) a menos que sea absolutamente necesario porque estos métodos exponen información sobre cómo se implementa una clase y, como consecuencia, hacen que su código sea más difícil de mantener. A veces, los métodos get / set son inevitables, pero un diseñador experimentado de OO probablemente podría eliminar el 99 por ciento de los accesos actualmente en su código sin mucha dificultad.

Los métodos getter / setter a menudo se abren paso en el código porque el codificador estaba pensando en el procedimiento. La mejor manera de salir de esa mentalidad procesal es pensar en términos de una conversación entre objetos que tienen responsabilidades bien definidas ...

Si está seriamente preocupado por el problema, lo primero que debe considerar es el rediseño del código para deshacerse de los captadores y el acceso de campo no privado , en lugar de jugar juegos mentales infructuosos para elegir cuál es la forma menos inapropiada .

Vale la pena enfatizar una vez más, esconder un diseño problemático detrás de una cortina de humo "protegida" no hará que el problema desaparezca, la herencia no justifica trucos como ese. Parafraseando su pregunta, esfuércese por diseñar las cosas de una manera que evite que las clases heredadas interactúen con esas variables , ni a través de getters ni a través del acceso de campo. Dile, no preguntes :

El código de procedimiento obtiene información y luego toma decisiones. El código orientado a objetos le dice a los objetos que hagan cosas.

Dedicar tiempo y esfuerzo a decidir qué forma preferiría obtener información, contra el principio clásico que recomienda evitar esto por completo, ahora eso me parece una exageración descarada .

mosquito
fuente
Somos de la misma escuela. La pregunta no debería ser, "oh, la clase base no funciona bien, entonces, ¿cómo me meto con sus datos", debería ser, "cómo hago que la clase base funcione para que no tenga que hacerlo? con su estado interno? Raramente implemento getters. Casi nunca implemento setters. Los métodos en mis clases son comandos en la línea de "Tell Don't Ask". A veces siento que una de mis principales responsabilidades laborales es corregir errores ocultando detalles de implementación expuestos por programadores perezosos o irreflexivos.
dash-tom-bang
1

En el caso de una implementación de clase inmutable, proteger sus variables internas rompería el contrato que ha establecido, en cuyo caso los captadores protegidos no serían excesivos y necesarios (aunque tendría que devolver copias de su variable, no un puntero) )

En las clases mutables, sigo prefiriendo getters / setters a otorgar acceso directo a mis variables internas. Esto asegura que pueda cambiar la forma en que maneja sus variables internas como lo desee, sin temor a romper ningún código de cliente. Por ejemplo, imagine que tiene una clase Java de la siguiente manera:

public abstract class SomeClass {
    protected int[] someVariable;

    // Rest of implementation
}

La extensión de la clase del cliente SomeClassaccedería directamente someVariable, manipulándola como mejor les parezca. Ahora, esto puede crear todo tipo de problemas si estas clases no salen someVariableen un estado aceptable. Además, si decide en una versión posterior cambiar la definición de SomeClassusar un en List<Integers>lugar de una matriz, romperá la compatibilidad con la base de código existente, que espera SomeClasstener una matriz interna de ints, no una lista de ints.

El uso de captadores protegidos en este caso le permitiría realizar este cambio sin romper ningún contrato preestablecido.

public abstract class SomeClass {
    List<Integer> someVariable;

    protected int[] getSomeVariable () {
        // Return int[] version of someVariable
    }

    // Rest of implementation
}
Laf
fuente