¿La forma más amigable para los humanos de ordenar definiciones de métodos de clase?

37

En cualquier definición de clase dada, he visto las definiciones de métodos ordenadas de varias maneras: alfabético, cronológico basado en el uso más común, alfabético agrupado por visibilidad, alfabético con captadores y establecedores agrupados, etc. Cuando empiezo a escribir una nueva clase, Tiendo a escribir todo, luego reordenar cuando termine de escribir toda la clase. En esa nota, tengo tres preguntas:

  1. ¿Importa el orden?
  2. ¿Hay un "mejor" pedido?
  3. Supongo que no, así que ¿cuáles son los pros y los contras de las diferentes estrategias de pedido?
Johntron
fuente
1
Realmente no espera que la gente corte / pegue código simplemente para reordenar los métodos. Si el IDE lo hace automáticamente, entonces está bien. De lo contrario, es difícil de hacer cumplir.
Reactgular
Para aclarar, mi pregunta es sobre legibilidad / usabilidad, no sobre la sintaxis.
Johntron

Respuestas:

51

En algunos lenguajes de programación, el orden es importante porque no puede utilizar las cosas hasta después de que se hayan declarado. Pero salvo eso, para la mayoría de los idiomas no le importa al compilador. Entonces, te queda importando para los humanos.

Mi cita favorita de Martin Fowler es: Any fool can write code that a computer can understand. Good programmers write code that humans can understand.Entonces, diría que el orden de su clase dependerá de lo que facilite la comprensión de los humanos.

Personalmente prefiero el tratamiento gradual que Bob Martin da en su Clean Codelibro. Variables miembro en la parte superior de la clase, luego constructores, luego todos los demás métodos. Y usted ordena que los métodos se relacionen estrechamente con la forma en que se usan dentro de la clase (en lugar de poner arbitrariamente todo público, luego privado y luego protegido). Él lo llama minimizar la "distancia vertical" o algo así (no tengo el libro sobre mí en este momento).

Editar:

La idea básica de la "distancia vertical" es que desea evitar que la gente salte todo su código fuente solo para entenderlo. Si las cosas están relacionadas, deberían estar más juntas. Las cosas no relacionadas pueden estar más separadas.

El Capítulo 5 de Clean Code (gran libro, por cierto) entra en muchos detalles sobre cómo el Sr. Martin sugiere ordenar el código. Sugiere que leer el código debería funcionar como leer un artículo de periódico: los detalles de alto nivel son lo primero (en la parte superior) y obtienes más detalles a medida que lees. Él dice: "Si una función llama a otra, deben estar cerca verticalmente, y la persona que llama debe estar por encima de la persona que llama, si es posible". Además, los conceptos relacionados deben estar muy juntos.

Así que aquí hay un ejemplo artificial que es malo en muchos sentidos (diseño pobre de OO; nunca se usa doublepor dinero) pero ilustra la idea:

public class Employee {
  ...
  public String getEmployeeId() { return employeeId; }
  public String getFirstName() { return firstName; }
  public String getLastName() { return lastName; }

  public double calculatePaycheck() {
    double pay = getSalary() / PAY_PERIODS_PER_YEAR;
    if (isEligibleForBonus()) {
      pay += calculateBonus();
    }
    return pay;
  }

  private double getSalary() { ... }

  private boolean isEligibleForBonus() {
    return (isFullTimeEmployee() && didCompleteBonusObjectives());
  }

  public boolean isFullTimeEmployee() { ... }
  private boolean didCompleteBonusObjectives() { ... }
  private double calculateBonus() { ... }
}

Los métodos están ordenados para que estén cerca de los que los llaman, trabajando desde la parte superior. Si hubiéramos puesto todos los privatemétodos debajo de publiclos, entonces tendrías que hacer más saltos para seguir el flujo del programa.

getFirstNamey getLastNameestán relacionados conceptualmente (y getEmployeeIdprobablemente también lo estén), por lo que están muy juntos. Podríamos moverlos todos hacia abajo, pero no nos gustaría ver getFirstNameen la parte superior e getLastNameinferior.

Esperemos que esto te de la idea básica. Si está interesado en este tipo de cosas, le recomiendo leer Clean Code.

Alano
fuente
Necesito saber cómo se deben colocar los establecedores y captadores de variables de instancia. ¿Debería venir justo después del constructor de la clase o al final de la clase?
srinivas
Personalmente me gustan en la parte superior después del constructor. Pero en realidad no importa; Recomiendo consistencia en su proyecto y con su equipo como una buena forma de decidir.
Allan
No debería calculateBonus()venir antes isFullTimeEmployee()y didCompleteBonusObjectives()?
winklerrr
@winklerrr Puedo ver un argumento para eso. Los coloqué donde hice porque isFullTimeEmployeey didCompleteBonusObjectivesson utilizados por isEligibleForBonuslo que debe ser verticalmente cerca de ella. Pero podría moverse calculateBonushacia arriba para colocarlo más cerca de donde se llama. Desafortunadamente, como tiene funciones que llaman funciones, eventualmente termina con problemas (como una función compartida llamada por varias otras) donde no hay un pedido perfecto. Luego se deja a su mejor juicio. Recomiendo mantener clases y funciones pequeñas para mitigar esos problemas.
Allan
2

Generalmente ordeno mis métodos por relación y orden de uso.

Tome una clase de red, voy a agrupar todos los métodos TCP juntos, luego todos los métodos UDP juntos. El TCP interno tendría el método de configuración como el primero, tal vez envíe un mensaje dado en segundo lugar y cierre el socket tcp como tercero.

Obviamente no todas las clases encajarán en ese patrón, pero ese es mi flujo de trabajo general.

Lo hago así para depurar más que cualquier otra cosa, cuando tengo un problema y quiero saltar al método, no creo cómo se deletrea, pienso de qué es responsable y voy a esa sección.

De esta manera, en particular, tiene sentido que un tercero vea / use su código como está agrupado y seguirá el orden en que se usa, por lo que el código que escribirá con su clase seguirá la misma estructura que la clase.

En cuanto a lo que importa?

para facilitar la lectura, definitivamente.

aparte de eso, en realidad no, solo en los casos en que ciertos idiomas no se pueden llamar al método a menos que se defina arriba donde se llama, etc.

Simon McLoughlin
fuente
0

Idealmente, sus clases son tan pequeñas que no importa. Si solo tiene una docena de métodos, y su editor o IDE admite el plegado, no tiene ningún problema, porque la lista completa de métodos cabe en 12 líneas.

De lo contrario, la distinción de nivel superior debe ser pública versus privada. Primero, enumere los métodos públicos: esto es lo que la gente buscará más, porque definen la forma en que su clase interactúa con el resto de la base de código.

Luego, dentro de cada uno de estos, tiene más sentido agrupar métodos por funcionalidad: constructores y destructores en un bloque, getters / setters en otro, sobrecargas de operadores, métodos estáticos y el resto del grupo. Sin operator=embargo, en C ++, me gusta mantenerme cerca de los constructores, porque está estrechamente relacionado con el constructor de copia, y también porque quiero poder detectar rápidamente si todos (o ninguno) de ctor, copy ctor, operator = y dtor predeterminados existe.

tdammers
fuente
-1

tl; dr

Solo si el idioma en el que está trabajando requiere un pedido específico. Aparte de eso, el pedido depende de usted. Elija un sistema que sea consistente y tenga sentido, y trate de mantenerlo lo más posible.


1) ¿Importa el orden?

Solo si el idioma en el que trabaja necesita tener una función definida antes en el archivo que donde se llama, como en este ejemplo:

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

obtendrá un error porque llama funcB()antes de usarlo. Creo que este es un problema en PL / SQL y posiblemente también en C, pero puede tener declaraciones de reenvío como:

void funcB();

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

Esta es la única situación en la que puedo pensar, si el orden es "incorrecto", ni siquiera podrá compilar.

De lo contrario, siempre puede volver a ordenarlos como desee. Probablemente incluso podría escribir una herramienta para hacer eso por usted (si no puede encontrar una por ahí).

2) ¿Hay un "mejor" pedido?

Si el idioma / entorno no tiene requisitos de pedido, entonces el "mejor" orden es el que mejor funciona para usted . Para mí, me gusta tener todos los captadores / establecedores juntos, generalmente al comienzo de la clase (pero después de los constructores / inicializadores estáticos), y luego los métodos privados, luego protegidos, luego públicos. En cada grupo basado en el alcance generalmente no hay ordenación, aunque los métodos sobrecargados que trato de mantener juntos, en orden de número de parámetros. También trato de mantener juntos los métodos con funcionalidad relacionada, aunque a veces tengo que romper mi orden basada en el alcance para hacerlo; y, a veces, tratar de mantener el orden basado en el alcance divide el grupo por funcionalidad. Y mi IDE me puede dar una vista de esquema alfabético, así que eso también es bueno. ;)

Algunos lenguajes, como C #, tienen la capacidad de agrupar códigos en "regiones" que no tienen ningún efecto en la compilación, pero podrían facilitar la tarea de mantener juntas las funciones relacionadas, y luego ocultarlas / mostrarlas con el IDE. Como señaló MainMa , hay quienes consideran que esta es una mala práctica. He visto buenos y malos ejemplos de regiones que se utilizan de esta manera, así que si vas a hacerlo, ten cuidado.

FrustratedWithFormsDesigner
fuente