2008-08-28 13 views
18

Ya he propuesto esto en my blog, pero este es el lugar más adecuado.Diseño: Java y autorreferencia regresiva en métodos setter

Para las clases que tienen una larga lista de adaptadores que se utilizan con frecuencia, me pareció muy útil (aunque recientemente he leído sobre el Builder pattern en Java efectivo que es prácticamente el mismo). Básicamente, todos los métodos setter devuelven el objeto mismo modo, puede utilizar código como el siguiente:

MyClass 
    .setInt(1) 
    .setString("test") 
    .setBoolean(true) 
    ; 

Setter simplemente devuelva esto al final:

public MyClass setInt(int anInt) { 
    [snip] 
    return this; 
    } 

¿Cuál es su opinión? ¿Cuáles son los pros y los contras? ¿Esto tiene algún impacto en el rendimiento?

+1

+ 1 idea realmente genial. – helpermethod

Respuesta

13

@pek
La invocación encadenada es una de las propuestas para Java 7. Dice que si un tipo de retorno de método es nulo, debe devolver implícitamente este. Si está interesado en este tema, hay un grupo de enlaces y un ejemplo simple en Alex Miller's Java 7 page.

+0

Encuentro que este modismo puede ser una buena idea (aunque no lo usaría yo mismo), pero ¿por qué extender el lenguaje si se puede hacer declarando explícitamente que el tipo de retorno de los incubadores no es nulo? No me gusta la idea de extensiones ad-hoc a un idioma para admitir modismos específicos. – Giorgio

+1

Estoy de acuerdo con @Giorgio. Este es el comportamiento "mágico", y toda la magia es mala. Es mágico en el sentido de que no puedes verificar la fuente para saber qué devuelve, debes saber que ese vacío devuelve una referencia automática ... Prefiero que introduzcan un nuevo tipo de devolución para esto ... llámalo 'en lugar de' vacío '. O algo. –

+0

@Tor Valamo: estoy de acuerdo contigo. Además de esto, creo que un lenguaje de programación debe proporcionar ** conceptos ** no ** modismos **. Si uno realmente quiere usar muchos modismos, uno debería usar un lenguaje extensible como Lisp, pero no se puede esperar que un idioma se extienda continuamente para admitir todos los modismos posibles: hay tantos que un lenguaje puede convertirse fácilmente (innecesariamente) en complejo . – Giorgio

2

No lo haría yo mismo, porque para mí es un poco lo que hace un método en particular, y el método de encadenamiento es de uso limitado para mí hacerlo a mano. Sin embargo, no me va a enviar a una bola temblorosa de rabia y psicosis, que siempre es algo bueno. : ')

No me preocupa el rendimiento; solo pregúntale a Knuth.

8

Esto se llama Fluent Interface, como referencia.

Personalmente, creo que es una idea bastante clara, pero realmente una cuestión de gusto. Creo que jQuery funciona de esta manera.

-2

Estoy de acuerdo con @Bernard en que el método de encadenamiento como este confunde el propósito de los incubadores. En lugar de ello sugeriría que si siempre está creando cadenas de emisores de este tipo que cree un constructor personalizado para su clase así que en vez de

MyClass 
    .setInt(1) 
    .setString("test") 
    .setBoolean(true) 
    ; 

Usted hacer

new MyClass(1,"test",true); 

Esto hace que sea más fácil de leer y puede usar esto para hacer que su clase sea inmutable si así lo desea.

+6

En realidad, esto lo hace mucho, MUCHO menos legible si tiene una gran cantidad de incubadores y hay muchas variaciones a las que debe llamar. Tratar de manejar eso con constructores sobrecargados será una pesadilla, confía en mí. –

+1

No estoy de acuerdo. Los constructores solo deberían tener parámetros para lo que SE REQUIERE en el momento de la creación. Los constructores por conveniencia deben compensar la falta de una construcción adecuada para manejar la misma semántica. –

+0

Constructores contaminantes es una mala idea, a menos que tenga, digamos dos o tres parámetros, como máximo. Envía algo final a los constructores, y usa setters para los campos mutables. – Ajax

1

me parece que sea en un estilo pobre cuando se utiliza en los emisores. clases inmutables son por lo general un mejor ajuste para el encadenamiento, tales como:

aWithB = myObject.withA(someA).withB(someB); 

donde myObject es de esta clase:

class MyClass { 
    withA(TypeA a) { 
     this.a.equals(a) ? this : new MyClass(this, a); 
    } 

    private MyClass(MyClass copy, TypeA a) { 
     this(copy); 
     this.a = a; 
    } 
} 

El Builder es también útil, ya que permite que el objeto final sea inmutable mientras que previene las instancias intermedias que normalmente debería crear al usar esta técnica.

0

Yo solía ser un fan de la práctica de Java (y peor C#) de hacer getters y setters (obtener propiedades de conjunto) a través de un objeto. Este uso es lo que consideré orientado a objetos, pero realmente esto nos lleva a exponer las agallas y la implementación del objeto y no aprovechar realmente la encapsulación. Hay momentos en los que no puede salirse de esto (me viene a la mente OR/M), pero en general, el objeto debe configurarse y luego realizar su función. Los objetos de mis sueños tienden a tener uno o dos constructores, y quizás media docena de funciones que sí funcionan.

La razón de esto es que una vez que comencé a desarrollar API, existe una necesidad real de mantener las cosas simples. En realidad, solo desea agregar tanta complejidad como sea necesario para realizar el trabajo, y los getters y setters, aunque simples en sí mismos, agregan complejidad en montones cuando se agregan en masa. ¿Qué sucede cuando cargo setters en un orden diferente? Anythign diferente? ¿Estás seguro?

0

@Dan nuevamente, para situaciones más complejas (la inmutabilidad viene en mente), el patrón de construcción es una gran solución.

Además, estoy de acuerdo con usted principalmente en getters. Creo que lo que dices es seguir mayoritariamente el paradigma "Tell don't ask" y estoy totalmente de acuerdo. Pero eso está orientado principalmente a getters.

Por último, todo lo anterior es para las clases que tienen una gran cantidad de atributos. No veo un motivo para ninguno si solo tiene menos de, por ejemplo, 7.

0

Terminé haciendo esto mucho cuando trabajo con la biblioteca de Excel de POI de Apache; Terminé escribiendo métodos de ayuda que se encadenaron para poder aplicar formato, tipos de datos, datos de celda internos, fórmulas y posicionamiento de celda.

Para cosas con muchos pequeños elementos de peso mosca que necesitan pequeños retoques meticulosos aplicados, funciona bastante bien.

1

Tiene sentido para los constructores, donde todo lo que se va a hacer es preparar un montón de cosas, crear el objeto real y tirar el constructor de distancia. Para los constructores, también podría deshacerse de la parte "establecer" del nombre del método. Del mismo modo, los tipos inmutables realmente no necesitan el "obtener".

Something thing = new SomethingBuilder() 
    .aProperty(wotsit) 
    .anotherProperty(somethingElse) 
    .create(); 

Un truco útil (si no te importa un tiempo de ejecución de arriba ~ 2K por clase) es utilizar el doble lenguaje corsé:

JFrame frame = new JFrame("My frame") {{ 
    setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
    setLocation(frameTopLeft); 
    add(createContents()); 
    pack(); 
    setVisible(true); 
}}; 
+0

Eso no es un "modismo". Es un abuso horrible de clases anónimas que no se vuelve aceptable solo porque le das un nombre bonito. –

+1

Es una expresión idiomática. Hace que el código sea menos desordenado. Me servirá hasta que use un lenguaje que pueda expresarlo más claramente. –

+0

+1: dos puntos buenos. ¿por qué downvote? él ha enumerado la advertencia sobre el tiempo de ejecución en lo alto. –

0

Cómo utilizar

/** 
* 
* @author sanjay 
*/ 
public class NewClass { 
private int left ; 
private int top; 
public void set(int x,int y) 
    { 
    left=x; 
    top=y; 
} 
public NewClass UP(int x) 
    { 
    top+=x; 
    return this; 
} 
public NewClass DOWN(int x) 
    { 
    top-=x; 
    return this; 
} 
public NewClass RIGHT(int x) 
    { 
    left+=x; 
    return this; 
} 
public NewClass LEFT(int x) 
    { 
    left-=x; 
    return this; 
} 
public void Display() 
    { 
    System.out.println("TOP:"+top); 
    System.out.println("\nLEFT\n:"+left); 
} 
} 
public static void main(String[] args) { 
    // TODO code application logic here 
    NewClass test = new NewClass(); 
    test.set(0,0); 
    test.Display(); 
    test.UP(20).UP(45).DOWN(12).RIGHT(32).LEFT(20); 
    test.Display(); 
+0

Esta respuesta no parece abordar las preguntas originales del OP. –

Cuestiones relacionadas