2012-07-08 21 views
5

Una de las ventajas de Scala es que le da un gran control sobre el alcance. Puede anidar funciones como este:Estilo Scala: ¿qué tan lejos anidar las funciones?

def fn1 = { 
    def fn11 = { 
    ... 
    } 
    def fn12 = { 
    ... 
    def fn121 = { 
     ... 
    } 
    } 
    ... 
    def fn13 = { 
    ... 
    } 
} 

El problema aquí es que na1 puede empezar a parecer un poco intimidante. Procedentes de un fondo de Java, se nos aconseja mantener las funciones suficientemente pequeñas para ser vistas en una sola "página" en el IDE.

¿Qué pensaría usted acerca de tomar FN12 de na1 basado en el razonamiento: "Sólo se utiliza en na1 en este momento, pero podría resultar muy útil en otro lugar en la clase más adelante ..."

Además, ¿le gustaría saber dónde colocar las funciones anidadas, antes o después del código que las llama?

Respuesta

5

Hay algunos patrones que aprovechan una capa de anidación.

recursividad, donde se utiliza para ocultar los detalles de implementación (y es más limpio que separa en dos métodos de nivel superior separadas):

def callsRecursive(p: Param): Result = { 
    def recursive(p: Param, o: OtherParam, pr: PartialResult = default): Result = { 
    ... 
    } 
} 

Ámbito de fallos No te-repetir-yourself:

def doesGraphics(p: Point) { 
    def up(p: Point): Point = // ... 
    def dn(p: Point): Point = // ... 
    def lf(p: Point): Point = // ... 
    def rt(p: Point): Point = // ... 
    /* Lots of turtle-style drawing */ 
} 

Y más trucos esotéricos como sombrear conversiones implícitas para un bloque local.

Si necesita estas dos cosas, podría imaginar anidar dos veces. Más que eso es probable que sea demasiado, sobre todo porque probablemente estás haciendo que un método haga demasiado. Debería pensar en cómo subdividir el problema con interfaces limpias que luego pueden convertirse en sus propios métodos, en lugar de tener un revoltijo desordenado de cierres alrededor de todo tipo de variables definidas dentro del método. Los grandes métodos son como variables globales: todo se vuelve demasiado dependiente de los detalles de la implementación y demasiado difícil de seguir. Si está listo para pensar lo suficiente para que algo tenga una interfaz decente, incluso si solo lo necesita una vez, considere sacarlo al nivel superior. Si no vas a pensar tan duro, mi inclinación es dejarlo adentro para evitar contaminar la interfaz.

En cualquier caso, no tenga miedo de crear un método en cualquier lugar que lo necesite. Por ejemplo, supongamos que se encuentra en lo profundo de algún método con dos colecciones, cada una de las cuales debe tener la misma operación realizada en puntos específicos de la lógica. ¡No te preocupes si tienes uno o dos o tres métodos de profundidad! Simplemente cree el método donde sea necesario y llámelo en lugar de repetirlo. (Solo tenga en cuenta que crear una lista y mapear es una alternativa si simplemente necesita procesar varias cosas en el mismo lugar).

2

Considero que es una buena práctica usar siempre la visibilidad más baja. Si se necesita una función anidada para una función diferente, se podría mover de todos modos.

0

¡Eso parece bastante aterrador! Si desea controlar-fina del alcance de los métodos privados, se puede declarar como

private[scope] def fn12 = { ... } 

donde scope es un paquete. Puede leer más en The busy Java developer's guide to Scala.

Personalmente evito anidar métodos con nombre (def), mientras que no me importa anidar funciones anónimas (por ejemplo, cierres en la programación de estilo de paso de continuación).

2

Si tiene una función de nivel superior como la que describe, probablemente le esté haciendo demasiado.

TDD también ayuda en la decisión si este es el caso: Todavía todo es fácilmente comprobable.

Si llego a la conclusión de que este es realmente el caso, refactorizo ​​para obtener las funciones internas como dependencias, con sus propias pruebas.

En el resultado final hago un uso muy limitado de funciones definidas en funciones definidas ... También puse un límite mucho más estricto sobre el tamaño del método: alrededor de 10-15 líneas en java, incluso menos en scala, ya que es menos detallado .

Pongo las funciones internas en su mayoría en la parte superior del método externo, pero apenas importa ya que es muy corto de todos modos.

6

En general, no veo tantas funciones anidadas en el código real. Funciona en contra del espíritu de mantener los métodos simples y concisos. Dicha anidación es principalmente útil para cierres en los que utilizará algunos de los parámetros del alcance externo (por ejemplo, el bucle interno de una función recursiva), por lo que es más limpio que declararlo fuera y tener que volver a pasar dichos argumentos explícitamente.

Debe colocar las funciones anidadas antes del código que las llama o es una referencia hacia delante y no se compilará. (En objetos/clases, puede colocarlos después, pero no en métodos.)

0

Las funciones anidadas son útiles (por ejemplo, los ayudantes en recursividad). Pero si se vuelven demasiado numerosos, no hay nada que te impida extraerlos a un nuevo tipo y delegar en eso.

Cuestiones relacionadas