2008-09-09 20 views
9

Disfruté mucho Jeff's post en Spartan Programming. Estoy de acuerdo en que un código como ese es un placer de leer. Desafortunadamente, no estoy tan seguro de que sea necesariamente un placer trabajar con él.Spartan Programming

Durante años he leído y adherido a la práctica de "una expresión por línea". He peleado la buena batalla y se mantiene mi tierra cuando muchos libros de programación contrarrestados este consejo con el ejemplo de código como:

while (bytes = read(...)) 
{ 
    ... 
} 

while (GetMessage(...)) 
{ 
    ... 
} 

Recientemente, me he defendido una expresión por línea por razones más prácticas - depuración y apoyo a la producción. Conseguir un archivo de registro de producción que reclama una excepción NullPointer en "línea 65", que dice lo siguiente:

ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber()); 

es frustrante y totalmente evitables. A menos que agarre a un experto con el código que puede elegir el objeto "más probable" que fue nulo ... este es un verdadero dolor práctico.

Una expresión por línea también ayuda bastante al pasar por el código. Practico esto con la suposición de que la mayoría de los compiladores modernos pueden optimizar todos los objetos temporales superfluos que acabo de crear ...

Intento ser ordenado, pero abarrotar mi código con objetos explícitos me parece a veces laborioso. Por lo general, no hace que el código sea más fácil de navegar, pero realmente me ha sido útil para rastrear cosas en la producción o recorrer el código de mi persona o de otra persona.

¿Qué estilo hace usted abogado y puede racionalizarlo en un sentido práctico?

Respuesta

7

En El programador pragmático Hunt y Thomas hablan sobre un estudio que denominan la Ley de Demeter y se centra en el acoplamiento de funciones a otros módulos que no sean los suyos. Al permitir que una función nunca llegue a un tercer nivel en su acoplamiento, se reduce significativamente el número de errores y se aumenta la capacidad de mantenimiento del código.

Así:

Objecta a = getTheUser (.. Session.getState() getAccount() getAccountNumber());

Está cerca de un delito grave porque somos 4 objetos por el agujero de la rata.Eso significa cambiar algo en uno de esos objetos. Tengo que saber que llamaste a toda esta pila aquí en este mismo método. Que dolor.

Mejor:

Account.getUser();

Nota esto va en contra de las formas expresivas de programación que ahora son realmente populares con el software de burla. La desventaja es que tienes una interfaz estrechamente acoplada de todos modos, y la sintaxis expresiva simplemente hace que sea más fácil de usar.

+0

Las API fluidas de las que habla generalmente no recorren un árbol de objetos con estado (como su primer ejemplo), sino que devuelven el mismo objeto o el nuevo estado de encapsulación de objetos internos. Entonces, mientras violan la letra del LoD, no violan su espíritu. – kyoryu

+0

@Kyoryu, estoy de acuerdo con su declaración, pero la pregunta se centró en 1 afirmación por línea. Las API fluidas aún hacen que sea un poco más difícil de depurar porque estás ejecutando múltiples operaciones en una sola línea. –

+1

Entiendo tu punto por completo. Las buenas interfaces fluidas usan las 'declaraciones múltiples' como simplemente una mejor manera de lograr una sola declaración lógica, en lugar de ser llamadas atómicas múltiples. Para su ejemplo, incluso diría que Account.GetUser() no es un ejemplo particularmente bueno, ya que normalmente uno actuaría sobre el usuario. Prefiero ver Account.ActualOperation().(Todavía votando, ya que creo que estamos de acuerdo en general, estamos objetando el 2%) – kyoryu

4

Una expresión por línea.

No hay razón para ofuscar su código. El tiempo adicional que toma escribiendo los términos adicionales, ahorra tiempo de depuración.

5

Creo que la solución ideal es encontrar un equilibrio entre los extremos. No hay forma de escribir una regla que se ajuste a todas las situaciones; viene con experiencia Declarar cada variable intermedia en su propia línea hará que leer el código sea más difícil, lo que también contribuirá a la dificultad en el mantenimiento. Por la misma razón, la depuración es mucho más difícil si alinea los valores intermedios.

El "punto ideal" está en el medio.

3

Tiendo a errar en el lado de la legibilidad, no necesariamente la depuración. Los ejemplos que dices definitivamente deben evitarse, pero creo que el uso juicioso de expresiones múltiples puede hacer que el código sea más conciso y comprensible.

2

Normalmente estoy en el campamento "más corto es mejor". Su ejemplo es buena:

ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber()); 

Me encogerse si vi que más de cuatro líneas en lugar de uno - no creo que haría más fácil de leer o comprender. Tal como lo presentaste aquí, está claro que estás buscando un solo objeto. Esto no es mejor:

obja State = session.getState(); 
objb Account = State.getAccount(); 
objc AccountNumber = Account.getAccountNumber(); 
ObjectA a = getTheUser(AccountNumber); 

Este es un compromiso:

objb Account = session.getState().getAccount(); 
ObjectA a = getTheUser(Account.getAccountNumber()); 

pero todavía prefieren la expresión de una sola línea. Aquí hay una razón anecdótica: es difícil para mí releer y verificar errores en el 4-line en este momento para tontos tontos; la única línea no tiene este problema porque simplemente hay menos caracteres.

+1

Pero si se genera una NullPointerException con ObjectA a = getTheUser (session.getState(). getAccount(). getAccountNumber()); ¿Qué objeto en el tren es nulo? ¿Session, State, Account, AccountNumber o el Usuario que se devuelve desde la función original? – Ian

0

La capacidad de mantenimiento, y con ella, la legibilidad, es el rey. Por suerte, más corto muy a menudo significa más legible.

Éstos son algunos consejos Me gusta utilizar para cortar y dados código:

  • Los nombres de variables: ¿cómo describiría esta variable a otra persona en su equipo? Debería no decir "el entero numberOfLinesSoFar". Diría "numLines" o algo similar, comprensible y breve. No pretenda que el mantenedor no conoce el código en absoluto, pero asegúrese de que usted mismo pueda descubrir cuál es la variable, incluso si olvidó su propio acto de escribirlo. Sí, esto es algo obvio, pero vale más el esfuerzo que veo que muchos codificadores lo incluyan, así que lo incluyo primero.
  • Flujo de control: Evite muchas cláusulas de cierre a la vez (una serie de} en C++). Por lo general, cuando ves esto, hay una manera de evitarlo. Un caso común es algo así como

:

if (things_are_ok) { 
    // Do a lot of stuff. 
    return true; 
} else { 
    ExpressDismay(error_str); 
    return false; 
} 

pueden ser reemplazados por

if (!things_are_ok) return ExpressDismay(error_str); 
// Do a lot of stuff. 
return true; 

si podemos conseguir ExpressDismay (o una envoltura de los mismos) para volver falsa.

Otro caso es:

  • Loop iteraciones: la más estándar, mejor. Para bucles más cortos, es bueno usar iteradores de un carácter cuando la variable nunca se usa, excepto como un índice en un solo objeto.

El caso particular, yo diría que aquí está en contra de la forma "correcta" de usar un contenedor STL:

for (vector<string>::iterator a_str = my_vec.begin(); a_str != my_vec.end(); ++a_str) 

es mucho más prolijo, y exige a los operadores sobrecargados puntero * a_str o a_str-> Tamaño() en el lazo. Para recipientes que tienen acceso aleatorio rápido, el siguiente es mucho más fácil de leer:

for (int i = 0; i < my_vec.size(); ++i) 

con referencias a my_vec [i] en el cuerpo del bucle, que no confundir a nadie.

Finalmente, a menudo veo que los codificadores se enorgullecen de los números de sus líneas. ¡Pero no son los números de línea los que cuentan! No estoy seguro de la mejor manera de implementar esto, pero si tiene alguna influencia sobre su cultura de codificación, trataría de cambiar la recompensa hacia aquellos con clases compactas :)

2
ObjectA a = getTheUser(session.getState().getAccount().getAccountNumber()); 

Este es un mal ejemplo, probablemente porque acabas de escribir algo desde la parte superior de tu cabeza. Está asignando, a la variable llamada a de tipo ObjectA, el valor de retorno de una función denominada getTheUser.
Así que vamos a suponer que usted escribió esto en su lugar:

User u = getTheUser(session.getState().getAccount().getAccountNumber()); 

me rompería esta expresión, así:

Account acc = session.getState().getAccount(); 
User user = getTheUser(acc.getAccountNumber()); 

Mi razonamiento es: ¿cómo iba a pensar en lo que estoy haciendo con este código?
Probablemente pensaría: "primero necesito obtener la cuenta de la sesión y luego obtengo el usuario usando el número de esa cuenta".

El código debe leer su forma de pensar. Las variables deben referirse a las principales entidades involucradas; no tanto a sus propiedades (así que no almacenaría el número de cuenta en una variable).

Un segundo factor a tener en cuenta es: ¿alguna vez tendré que volver a referirme a esta entidad en este contexto?
Si, por ejemplo, estoy sacando más cosas del estado de la sesión, introduciría SessionState state = session.getState().

Todo esto parece obvio, pero me temo que tengo algunas dificultades para poner en palabras por qué tiene sentido, no ser un hablante nativo de inglés y todo eso.