Argumentos de la función de escritura dinámica: ¿cómo mantener alta la legibilidad?

7

El novato de la escritura dinámica aquí, esperando algunas palabras sabias y sabias.

Tengo curiosidad por saber si hay un conjunto de mejores prácticas para tratar los argumentos de la función (y seamos honestos, las variables en general) en lenguajes de tipo dinámico, como Javascript. El problema con el que me topo a menudo es con respecto a la legibilidad del código: estoy viendo una función que escribí hace un tiempo y no tengo idea de cuál es realmente la estructura de las variables de argumento. Por lo general, está bien en el momento del desarrollo de un nuevo código: todo está nuevo en mi cabeza, cada variable y parámetro tiene sentido porque los escribí. ¿Una semana después? No tanto.

Por ejemplo, supongamos que estoy tratando de obtener una gran cantidad de datos sobre las sesiones de usuario en un sitio web y obtener algo útil:

var crunchSomeSessionData = function(sessionsMap, options) {
    [...]
}

Sin tener en cuenta el hecho de que el nombre de la función no es útil, eso obviamente es un gran problema, en realidad no sé nada sobre la estructura de las sesiones. Mapa u opciones. Ok ... tengo k / v empareja el objeto sessionMap, ya que se llama Mapa, pero ¿es el valor un primitivo, una matriz, otro hash de cosas? ¿Qué son las opciones? ¿Una matriz? ¿Una cadena separada de espacios en blanco?

Tengo algunas opciones:

  • aclare la estructura exactamente en el encabezado de comentario para la función. El problema es que ahora tengo que mantener el código en dos lugares.
  • tiene un nombre tan útil como sea posible. p.ej. userIdToArrayOfTimestampsMap o incluso tener algún tipo de dialecto pseudo-húngaro para nombrar variables que solo yo hablo que explica qué son los tipos y cómo están anidados. Esto lleva a un código realmente detallado, y soy un fanático de mantener las cosas por debajo de 80 col.
  • descomponer las funciones hasta que solo estoy transmitiendo primitivas o colecciones de primitivas. Me imagino que podría funcionar, pero es probable que termine con micro-funciones que tienen una o dos líneas a lo sumo, funciones que existen solo con el propósito de facilitar la lectura. Ahora tengo que saltar todo el archivo y recomponer la función en mi cabeza, lo que acaba de empeorar la legibilidad.
  • algunos lenguajes ofrecen desestructuración, que en cierta medida se puede considerar como documentación adicional de lo que contendrá el tipo de argumento.
  • podría crear una "clase" para el tipo específico de objeto, aunque no haría una gran diferencia en un lenguaje prototípico como JS, y probablemente agregaría más gastos generales de mantenimiento de lo necesario. Alternativamente, si está disponible, uno puede intentar usar protocolos, tal vez algo parecido a deftype / defrecord de Clojure, etc.

En el mundo estáticamente tipado, esto no es un problema tan importante. En C #, por ejemplo, obtienes un:

public void DoStuff(Dictionary<string, string> foo) {[...]};

Ok, fácil peasy, sé exactamente lo que estoy recibiendo, no hay necesidad de leer el encabezado de la función, o volver a la persona que llama y averiguar qué está inventando, etc.

¿Cuál es la solución aquí? ¿Todas las personas que se desarrollan en lenguajes de tipos dinámicos están continuamente aturdidas por los tipos que obtienen sus subrutinas? ¿Existen estrategias de mitigación?

    
pregunta Alexandr Kurilin 25.04.2013 - 07:01

3 respuestas

1

Creo que muchos de los problemas que tiene pueden resolverse con la denominación adecuada de las variables y el contenido del método. En su mayor parte, debería ser obvio que los tipos de parámetros se basan en los nombres y los contenidos del método. La documentación también ayuda.

Por ejemplo:

function getSum(arr) {
    var sum = 0;
    arr.forEach(arr, function(item) {
        sum += item;
    });

    return sum;
}

Solo por el nombre de la función y la forma en que está escrita, debe implicarse que getSum toma una matriz de números y devuelve un número.

Veamos otro ejemplo que no tiene ningún sentido, pero debería tener sentido lo que está haciendo.

var crunchSomeSessionData = function(sessionsMap) {
    var browsers = {};

    Object.keys(sessionsMap).forEach(function(key) {
        var session = sessionsMap[key],
            browser = session.getBrowser();

        if(!browsers[browser]) {
            browsers[browser] = [];
        }

        browsers[browser].push(session.getUserId());
    });

    return browsers;
}

Sin la documentación adecuada (si esto estuviera documentado), aún puede ver que crunchSessionData toma un objeto de las instancias de Session, esas instancias tienen un método getBrowser y getUserId. Devuelve un objeto desactivado por el navegador que tiene una serie de ID de usuario que se encuentran actualmente en ese navegador.

crunchSomeSessionData({
    213j123j123j123j13: {browser: 'chrome', userId: 'pllee'},
    fawefjioawejfwoeiw: {browser: 'ie', userId: 'grandpa'}
})
>> {chrome: ['pllee'], ie: ['grandpa']}

Le sugeriría que para saber cómo se puede leer fácilmente la escritura dinámica, lea un código de fuente abierta que no esté muy documentado y vea si puede seguir lo que está sucediendo. Si es así empieza a utilizar sus patrones de diseño. Puede consultar la sencilla biblioteca de tiempo de rendimiento que escribí enlace . Es muy pequeño y la mayoría de los métodos no están documentados. El código no es perfecto pero podría ser un buen ejemplo. Después de no buscarlo o usarlo durante meses, el código aún tiene sentido para mí, pero lo escribí :)

    
respondido por el pllee 26.04.2013 - 08:40
1

Tal vez intente escribirlo más en estilo OOP. Por ejemplo, en lugar de:

var crunchSomeSessionData = function(sessionsMap, options) {
    [...]
}

¿por qué no puedes hacerlo?

session.crunchData(options)

donde sesión es el objeto que has creado. Podrá echar un vistazo al constructor para saber exactamente cómo se crea la sesión y todos sus problemas deberían desaparecer

    
respondido por el mkk 26.04.2013 - 12:20
1

Cualquier código debe estar documentado con suficiente información para entenderlo. Eso incluye información sobre los tipos de variables si es necesario (e incluso en idiomas tipificados estáticamente, es posible que necesite más información de la que proporciona la palabra clave de tipo).

Cuando te acostumbras a trabajar con un lenguaje de tipo dinámico, encontrarás que el tipo de una variable a menudo se explica por sí mismo en función de su uso. Pero si no, agrega comentarios para aclararlo.

En cuanto a su preocupación de que tales comentarios agreguen duplicación (y posiblemente rompan un principio como DRY), esa preocupación se aplicaría a cualquier comentario . Cualquier documentación agrega inherentemente una cierta cantidad de duplicación, y debe mantenerse para que coincida con el código. Pero cuando ayuda a que el código sea comprensible, el beneficio supera el costo.

También, tenga en cuenta que la mayoría de sus otras soluciones involucran la creación de tantas cosas adicionales (o más). Una clase necesitaría una definición de clase que sea tan pequeña como los comentarios. (Por supuesto, hacer una clase puede ser beneficioso por otras razones. Pero no evite resolver esto con comentarios simplemente porque agregan líneas adicionales a su código).

Sin embargo, los comentarios solo deben hacerse cuando sea necesario entender el código. Odio ver código como este, lo que realmente es una duplicación inútil:

//crunchSomeSessionData - A function that crunches some session data.
//
//sessionsMap - A map containing the sessions and their data that we want to crunch.
//user - The user for whose sessions we want to crunch data
function crunchSomeSessionData(sessionsMap, user) {
    [...]
}
    
respondido por el user82096 26.04.2013 - 12:41

Lea otras preguntas en las etiquetas