terminology - ejemplos - codigo espagueti youtube
¿Qué es el código de spaghetti? (13)
Código de espagueti: Originado a principios de los años 60 en Italia como una receta alternativa para ciertos platos de pasta, el código de espagueti fue cocinado por un empresario de restaurantes que intentó automatizar la creación de un entrante infalible. Presionado por la falta de tiempo para completar el diseño, el ingeniero / chef cortó las esquinas, lo que introdujo problemas en la receta desde el principio. En un intento frenético por remediar una buena idea que salió mal, varias especias se agregaron rápidamente al brebaje a medida que la receta crecía fuera de control. El resultado fue una pila de textos fibrosos, sinuosos, pero potencialmente sabrosos que más tarde llegarían a ser una práctica apreciada por los desarrolladores de todo el mundo.
¿Puedes publicar un breve ejemplo de código de spaghetti real y exagerado, posiblemente diciendo lo que hace? ¿Puedes mostrarme la pesadilla de un pequeño depurador?
No me refiero al código IOCCC , eso es ciencia ficción. Me refiero a ejemplos de la vida real que te sucedieron ...
Actualizar
El enfoque ha cambiado de "publicar un código de espagueti" a "¿qué es exactamente el código de spaghetti?". Desde una perspectiva histórica, las elecciones actuales parecen ser:
- viejo código de Fortran usando gotos computarizados masivamente
- código antiguo de Cobol usando la instrucción ALTER
Desde un controlador SCSI de Linux (que permanecerá anónimo para proteger a los culpables):
wait_nomsg:
if ((inb(tmport) & 0x04) != 0) {
goto wait_nomsg;
}
outb(1, 0x80);
udelay(100);
for (n = 0; n < 0x30000; n++) {
if ((inb(tmport) & 0x80) != 0) { /* bsy ? */
goto wait_io;
}
}
goto TCM_SYNC;
wait_io:
for (n = 0; n < 0x30000; n++) {
if ((inb(tmport) & 0x81) == 0x0081) {
goto wait_io1;
}
}
goto TCM_SYNC;
wait_io1:
inb(0x80);
val |= 0x8003; /* io,cd,db7 */
outw(val, tmport);
inb(0x80);
val &= 0x00bf; /* no sel */
outw(val, tmport);
outb(2, 0x80);
TCM_SYNC:
/* ... */
small_id:
m = 1;
m <<= k;
if ((m & assignid_map) == 0) {
goto G2Q_QUIN;
}
if (k > 0) {
k--;
goto small_id;
}
G2Q5: /* srch from max acceptable ID# */
k = i; /* max acceptable ID# */
G2Q_LP:
m = 1;
m <<= k;
if ((m & assignid_map) == 0) {
goto G2Q_QUIN;
}
if (k > 0) {
k--;
goto G2Q_LP;
}
G2Q_QUIN: /* k=binID#, */
¿Cómo localicé esta joya?
find /usr/src/linux -type f -name /*.c |
while read f
do
echo -n "$f "
sed -n ''s/^.*goto */([^;]*/);.*//1/p'' $f | sort -u | wc -l
done |
sort +1rn |
head
El resultado es una serie de líneas que enumeran los archivos ordenados por el número de gotos en etiquetas distintas, como las siguientes:
kernel/fork.c 31
fs/namei.c 35
drivers/infiniband/hw/mthca/mthca_main.c 36
fs/cifs/cifssmb.c 45
fs/ntfs/super.c 47
El verdadero código de spaghetti requiere una multitud de gotos no locales. Lamentablemente, esto no es posible con la mayoría de los idiomas modernos.
Editar: Algunos sugieren excepciones y longjmp como sustitutos de GOTO. Pero estos son muy limitados y estructurados, ya que solo te permiten devolver el callstack. Real GOTO te permite saltar a cualquier línea en cualquier parte del programa, lo cual es necesario para crear verdaderos espaguetis.
Esto es de un analizador MIDI que escribí hace un tiempo. Fue una prueba rápida y sucia de concepto, pero, no obstante, me echaré la culpa de su fealdad: 4 niveles de condicionales anidados más los temidos retornos múltiples. Este código tenía la intención de comparar 2 eventos MIDI para ordenarlos por prioridad al escribir en un archivo. Aunque feo como era, hizo el trabajo decentemente.
internal class EventContainerComparer : IComparer {
int IComparer.Compare(object a, object b) {
MIDIEventContainer evt1 = (MIDIEventContainer) a;
MIDIEventContainer evt2 = (MIDIEventContainer) b;
ChannelEvent chanEvt1;
ChannelEvent chanEvt2;
if (evt1.AbsoluteTime < evt2.AbsoluteTime) {
return -1;
} else if (evt1.AbsoluteTime > evt2.AbsoluteTime) {
return 1;
} else {
// a iguar valor de AbsoluteTime, los channelEvent tienen prioridad
if(evt1.MidiEvent is ChannelEvent && evt2.MidiEvent is MetaEvent) {
return -1;
} else if(evt1.MidiEvent is MetaEvent && evt2.MidiEvent is ChannelEvent){
return 1;
// si ambos son channelEvent, dar prioridad a NoteOn == 0 sobre NoteOn > 0
} else if(evt1.MidiEvent is ChannelEvent && evt2.MidiEvent is ChannelEvent) {
chanEvt1 = (ChannelEvent) evt1.MidiEvent;
chanEvt2 = (ChannelEvent) evt2.MidiEvent;
// si ambos son NoteOn
if( chanEvt1.EventType == ChannelEventType.NoteOn
&& chanEvt2.EventType == ChannelEventType.NoteOn){
// chanEvt1 en NoteOn(0) y el 2 es NoteOn(>0)
if(chanEvt1.Arg1 == 0 && chanEvt2.Arg1 > 0) {
return -1;
// chanEvt1 en NoteOn(0) y el 2 es NoteOn(>0)
} else if(chanEvt2.Arg1 == 0 && chanEvt1.Arg1 > 0) {
return 1;
} else {
return 0;
}
// son 2 ChannelEvent, pero no son los 2 NoteOn, el orden es indistinto
} else {
return 0;
}
// son 2 MetaEvent, el orden es indistinto
} else {
return 0;
}
}
}
}
Para mí, un ejemplo más moderno de código spaghetti es cuando tienes 20 dlls y cada DLL se refiere entre sí de una forma u otra. Su gráfico de dependencia parece un bloque grande, y su código salta por todas partes sin un orden real. Todo es interdependiente.
También está el Código Ravioli , que es lo opuesto. Pequeños trozos de funcionalidad, una interfaz limpia cuidadosamente envuelta en la bondad de carne, todo se sentó en una agradable salsa de marco.
No te olvides de mencionar los espaguetis orientados a objetos. Esto es cuando intenta usar todos los patrones de diseño en el libro, incluso cuando no tienen sentido. Esto lleva al código de spaghetti a nivel conceptual, que es mucho más perjudicial para la calidad que el código de spaghetti clásico basado en goto.
Lo has pedido, lo obtendrás:
Esta es la fuente de un archivo DOS .com que reproduce el vals del Danubio Azul. El ejecutable tiene solo 176 bytes de tamaño. El código se reutiliza como datos y viceversa.
.286
.model tiny
g4 equ 55-48 ; removed note-decoding !
a4 equ 57-48 ; now: storing midi-notes for octaves 0..2 and convert
h4 equ 59-48 ; to 4..6 with a simple add 48.
c5 equ 60-48
d5 equ 62-48
e5 equ 64-48
g5 equ 67-48
h5 equ 71-48
c6 equ 72-48
d6 equ 74-48
e6 equ 76-48
g6 equ 79-48 ; = 00011111b
pp equ 0 ; c4 is not used in the walz, using it as play-pause.
EOM equ 1 ; c#4 is also available... End Of Music
; warning: experts only beyond this point !
pau1 equ 00100000b ; bitfield definitions for note-compression
pau2 equ 01000000b ; you can or a pau to each note!
pau3 equ 01100000b
;rep1 equ 01000000b ; rep1 is history (only used once).
;rep3 equ 11000000b ; rep3 was never used.
rep2 equ 10000000b ; or a rep2 to a note to play it 3 times.
drumsize equ 5
.code
org 100h
start:
mov ah,9
mov dx,offset msg
int 21h ; print our headerstring
mov dx,0330h ; gus midi megaem -port
mov si,offset music_code ; start of music data
mainloop:
; get new note (melody)
xor bp,bp ; bp= repeat-counter
lodsb ; get a new note
cmp al, EOM ; check for end
jne continue
ret
continue:
jns no_rep2 ; check for rep2-Bit
inc bp
inc bp ; "build" repeat-counter
no_rep2:
push ax ; save the note for pause
; "convert" to midi-note
and al,00011111b
jz skip_pp ; check pp, keep it 0
add al,48 ; fix-up oktave
skip_pp:
xchg ax,bx ; bl= midi-note
play_again:
mov cl,3
push cx ; patch program (3= piano)
push 0c8h ; program change, channel 9
; wait (cx:dx) times
mov ah,86h ; wait a little bit
int 15h
; prepare drums
dec di ; get the current drum
jns no_drum_underflow
mov di,drumsize
no_drum_underflow:
; play drum
push dx ; volume drum
push [word ptr drumtrk+di] ; note drum
mov al,99h
push ax ; play channel 10
; play melody
push dx ; volume melody
push bx ; note melody
dec ax ; replaces dec al :)
push ax ; play channel 9
; send data to midi-port
mov cl,8 ; we have to send 8 bytes
play_loop:
pop ax ; get the midi event
out dx,al ; and send it
loop play_loop
; repeat "bp" times
dec bp ; repeat the note
jns play_again
; check and "play" pause
xor bx,bx ; clear the note, so we can hear
; a pause
; decode pause value
pop ax
test al,01100000b
jz mainloop ; no pause, get next note
; decrement pause value and save on stack
sub al,20h
push ax
jmp play_again ; and play next drum
; don''t change the order of the following data, it is heavily crosslinked !
music_code db pp or rep2
db g4 or rep2 or pau1
db h4 or pau1, d5 or pau1, d5 or pau3
db d6 or pau1, d6 or pau3, h5 or pau1, h5 or pau3
db g4 or rep2 or pau1
db h4 or pau1, d5 or pau1, d5 or pau3
db d6 or pau1, d6 or pau3, c6 or pau1, c6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3
db e6 or pau1, e6 or pau3, c6 or pau1, c6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3
db e6 or pau1, e6 or pau3, h5 or pau1, h5 or pau3
db g4 or rep2 or pau1
db h4 or pau1, g5 or pau1, g5 or pau3
db g6 or pau1, g6 or pau3, d6 or pau1, d6 or pau3
db g4 or rep2 or pau1
db h4 or pau1, g5 or pau1, g5 or pau3
db g6 or pau1, g6 or pau3, e6 or pau1, e6 or pau3
db a4 or rep2 or pau1
db c5 or pau1, e5 or pau1, e5 or pau3, pp or pau3
db c5 or pau1, e5 or pau1, h5 or pau3, pp or pau3, d5 or pau1
db h4 or pau1, h4 or pau3
db a4 or pau1, e5 or pau3
db d5 or pau1, g4 or pau2
; db g4 or rep1 or pau1
; replace this last "rep1"-note with two (equal-sounding) notes
db g4
db g4 or pau1
msg db EOM, ''Docking Station'',10,''doj&sub''
drumtrk db 36, 42, 38, 42, 38, 59 ; reversed order to save some bytes !
end start
¿Alguna vez ha visto el código generado por el escáner y el generador Flex / Bison? Una plétora de etiquetas y directivas de preprocesador.
Es absolutamente imposible entender qué hay dentro ... y absolutamente imposible seguir el flujo del programa.
Eso es definitivamente el código de spaghetti.
No voy a sacar esto de mi cabeza. Esto es con lo que he tenido que trabajar, aunque simplificado. Digamos que básicamente tienes un programa que necesita una enumeración:
enum {
a, b, c;
} myenum;
Pero en cambio lo que tenemos es
HashTable t;
t["a"] = 0;
t["b"] = 1;
t["c"] = 2;
Pero, por supuesto, ninguna implementación de una tabla hash es lo suficientemente buena para que haya una implementación local de tablas hash, que contiene aproximadamente 10 veces más código que una implementación promedio de código abierto con la mitad de las características y el doble de errores. El HashTable en realidad se define virtual, y hay una fábrica HashTableFactory para crear instancias de HashTables, pero fiel al patrón HashTableFactory también es virtual. Para evitar una cascada de infite de clases virtuales, hay una función
HashTableFactory *makeHashTableFactor();
Por lo tanto, donde quiera que el código necesite myenum , lleva una referencia a la instancia de HashTable y HashTableFactory, en caso de que desee crear más HashTable. Pero espera, ¡eso no es todo! No es así como se inicializa la tabla hash, pero se hace escribiendo un código que lea XML:
<enum>
<item name="a" value="0"/>
<item name="b" value="1"/>
<item name="c" value="2"/>
</enum>
y se inserta en una tabla hash. Pero el código está "optimizado" para que no lea un archivo ascii myenum.xml, pero en su lugar hay un script de tiempo de compilación que genera:
const char* myenumXML = [13, 32, 53 ....];
desde myenum.xml y la tabla hash se inicializa mediante una función:
void xmlToHashTable(char *xml, HashTable *h, HashTableFactory *f);
Lo que es llamado:
HashTableFactory *factory = makeHashTableFactory();
HashTable *t = facotry.make();
xmlToHashTable(myenumXML, t, f);
Ok, entonces tenemos un montón de código para obtener una estructura enum. Básicamente se usa en una función:
void printStuff(int c) {
switch (c) {
case a: print("a");
case b: print("b");
case c: print("c");
}
}
y esto se llama en un contexto donde:
void stuff(char* str) {
int c = charToEnum(str);
printStuff(c);
}
Entonces, lo que realmente tenemos es en lugar de
void stuff(char *str) {
printf(str);
}
hemos gestionado generar miles de líneas de código (privado nuevo, con errores, complejo, implementación de hashtables y lectores xml y escritor) en lugar de los 3 anteriores.
El código real de espagueti se realizó en COBOL y utilizó la instrucción ALTER.
Aquí hay un ejemplo , mientras mencioné un "humor", he visto este tipo de cosas. Casi fue despedido una vez por notar que cualquier programa con una declaración Alter obviamente estaba en un estado de pecado. Me negué a "mantener" ese programa, fue más rápido reemplazarlo que entenderlo.
En términos simples, el código de espagueti es cualquier código en cualquier lenguaje de programación en el que no es posible rastrear la siguiente publicación de ejecución, o al menos difícil determinar dónde va el siguiente punto en respuesta a una acción.
Aquí está el Dispositivo de Duff , de la respuesta de Matt a esta pregunta :
int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}