Kun je een kort voorbeeld posten van echte, overdreven spaghetti-code, misschien wat het doet? Kun je me de nachtmerrie van een kleine debugger laten zien?
Ik bedoel niet IOCCC-code, dat is sciencefiction. Ik bedoel voorbeelden uit het echte leven die je zijn overkomen…
Bijwerken
De focus is veranderd van “post wat spaghetti-code” naar “wat is preciesspaghetti-code?”. Vanuit historisch perspectief lijken de huidige keuzes te zijn:
- oude Fortran-code die massaal berekende gotos gebruikt
- oude Cobol-code met de instructie ALTER
Antwoord 1, autoriteit 100%
Voor mij is een meer modernvoorbeeld van spaghetticode wanneer je 20 dll’s hebt en elke DLL op de een of andere manier naar elkaar verwijst. Je afhankelijkheidsgrafiek ziet eruit als een enorme klodder en je code springt overal heen zonder echte volgorde. Alles is onderling afhankelijk.
Antwoord 2, autoriteit 74%
Ik trek dit niet uit mijn hoofd. Dit is waar ik mee heb moeten werken, zij het vereenvoudigd. Laten we zeggen dat je in principe een programma hebt dat een opsomming nodig heeft:
enum {
a, b, c;
} myenum;
Maar in plaats daarvan hebben we
HashTable t;
t["a"] = 0;
t["b"] = 1;
t["c"] = 2;
Maar natuurlijk is geen enkele implementatie van een hash-tabel goed genoeg, dus er is een lokale implementatie van hash-tabellen, die ongeveer 10 keer meer code bevat dan een gemiddelde open source-implementatie met de helft van de functies en het dubbele aantal bugs. De HashTable is feitelijk virtueel gedefinieerd en er is een fabriek HashTableFactory om instanties van HashTables te maken, maar trouw aan het patroon is HashTableFactory ook virtueel. Om een oneindige cascade van virtuele klassen te voorkomen is er een functie
HashTableFactory *makeHashTableFactor();
Dus overal waar de code myenum‘s nodig heeft, staat er een verwijzing naar de instantie van een HashTable en HashTableFactory, voor het geval je meer HashTables wilt maken. Maar wacht, dat is niet alles! Dit is niet hoe de hash-tabel wordt geïnitialiseerd, maar het wordt gedaan door een code te schrijven die XML leest:
<enum>
<item name="a" value="0"/>
<item name="b" value="1"/>
<item name="c" value="2"/>
</enum>
en wordt ingevoegd in een hashtabel. Maar de code is “geoptimaliseerd” zodat het geen ascii-bestand myenum.xml leest, maar in plaats daarvan is er een script voor compileren dat het volgende genereert:
const char* myenumXML = [13, 32, 53 ....];
van myenum.xml en de hashtabel wordt geïnitialiseerd door een functie:
void xmlToHashTable(char *xml, HashTable *h, HashTableFactory *f);
die heet:
HashTableFactory *factory = makeHashTableFactory();
HashTable *t = facotry.make();
xmlToHashTable(myenumXML, t, f);
Ok, dus we hebben veel code om een enum-structuur te krijgen. Het wordt eigenlijk gebruikt in een functie:
void printStuff(int c) {
switch (c) {
case a: print("a");
case b: print("b");
case c: print("c");
}
}
en dit wordt aangeroepen in een context waarin:
void stuff(char* str) {
int c = charToEnum(str);
printStuff(c);
}
Dus wat we eigenlijk hebben is in plaats van
void stuff(char *str) {
printf(str);
}
we zijn erin geslaagd om duizenden regels code te genereren (privé nieuw, buggy, complex, implementatie van hashtabellen en xml-lezers en schrijver) in plaats van de bovenstaande 3.
Antwoord 3, autoriteit 67%
Er is ook Ravioli-code, wat het tegenovergestelde is. Leuke stukjes functionaliteit, een schone interface netjes gewikkeld rond vlezige goedheid, alles zat in een mooie saus van kader.
Antwoord 4, autoriteit 37%
Van een Linux SCSI-stuurprogramma (dat naamloos blijft om de schuldigen te beschermen):
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#, */
Hoe heb ik dit juweeltje gevonden?
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
De uitgang is een reeks lijnen die bestanden vermeldt die zijn besteld door het aantal GOTOS tot verschillende labels, zoals het volgende:
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
Antwoord 5, Autoriteit 37%
Echte spaghetti-code is gedaan in COBOL en gebruikte de wijziging van het alter.
Hier is een voorbeeld , terwijl ik een “humor” heeft vermeld, ik heb dit soort gezien van ding. Bijna werd eenmaal ontslagen om op te merken dat een programma met een alter-verklaring duidelijk in een staat van zonde was. Ik weigerde om dat programma ‘te handhaven “, het was sneller om het te vervangen dan het begrijpen.
Antwoord 6, Autoriteit 33%
Vergeet niet om het object-georiënteerde spaghetti te vermelden.
Dit is wanneer je alle ontwerppatronen in het boek probeert te gebruiken, zelfs als ze niet logisch zijn. Dit leidt tot spaghetti-code op conceptueel niveau, dat veel schadelijker is voor kwaliteit dan de klassieke goto-gebaseerde spaghetti-code.
Antwoord 7, Autoriteit 30%
U hebt er om gevraagd, u krijgt het:
Dit is de bron van een DOS .COM-bestand dat de blauwe Donau Waltz speelt. Het uitvoerbare bestand is slechts 176 bytes in grootte. Code wordt hergebruikt als gegevens en vice versa.
.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
Antwoord 8, autoriteit 26%
Echtespaghetti-code vereist een groot aantal niet-lokale goto’s. Helaas is dit niet mogelijk met de meeste moderne talen.
Bewerken: sommigen suggereren uitzonderingen en longjmp als vervanging voor GOTO. Maar deze zijn veelte beperkt en gestructureerd, aangezien ze je alleen toestaan om terug te keren in de callstack. Met Real GOTO kun je naar elkeregel overalin het programma springen, wat nodig is om echte spaghetti te maken.
Antwoord 9, autoriteit 26%
In eenvoudige bewoordingen is spaghetticode elke code in welke programmeertaal dan ook waarin het niet mogelijk is om de volgende post van uitvoering te traceren, of op zijn minst moeilijk te bepalen waar het volgende punt naartoe gaat als reactie op één actie.
Antwoord 10, autoriteit 15%
Dit is van een MIDI-parser die ik enige tijd geleden heb geschreven. Het was een snelle en vuile proof of concept, maar toch zal ik de schuld op zich nemen voor zijn lelijkheid: 4 niveaus van geneste conditionals plus de gevreesde meervoudige returns. Deze code was bedoeld om 2 MIDI-events te vergelijken om ze op prioriteit te sorteren bij het schrijven naar een bestand. Hoe lelijk het ook was, het deed het werk behoorlijk.
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;
}
}
}
}
Antwoord 11, autoriteit 11%
Hier is het Duff’s Device, van Matts antwoord op deze vraag:
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);
}
Antwoord 12
Spaghetticode: ontstaan in de vroege jaren 60 in Italië als alternatief recept voor bepaalde pastagerechten, werd spaghetticode bedacht door een restaurantondernemer die probeerde de creatie van een onfeilbaar voorgerecht te automatiseren. Gedrukt door het gebrek aan tijd om het ontwerp te voltooien, sneed de ingenieur / chef-kok de bochten af die al vroeg problemen in het recept introduceerden. In een verwoede poging om een slecht idee te verhelpen, werden er snel verschillende kruiden aan het brouwsel toegevoegd toen het recept uit de hand liep. Het resultaat was een draderige, kronkelige, maar potentieel smakelijke stapel tekst die later zou uitgroeien tot een praktijk die wordt gekoesterd door ontwikkelaars over de hele wereld.
Antwoord 13
Heb je ooit gekeken naar code gegenereerd door Flex/Bison scanner en generator? Een overvloed aan labels en preprocessor-richtlijnen.
Het is absoluut onmogelijk om te begrijpen wat erin zit.. en absoluut onmogelijk om de stroom van het programma te volgen.
Dat is beslist spaghetti-code.