Antipatterns

Ett antipattern är motsatsen till ett designmönster — en lösning som är vanlig, kanske till och med “fungerar”, men som i praktiken skapar fler problem än den löser. Se Anti-pattern i ordlistan.

Katten och fönstret — en illustration

Det finns en klassisk anekdot om ett kontor där fönstren ständigt stod öppna och drog in kyla på vintern. Lösningen som växte fram: en katt satte sig vid fönstret när det var kallt, och människor som gick förbi öppnade och stängde fönstret åt katten efter behov. Det “fungerade” — kylan minskade. Men det var en hemsk lösning: katten visste om problemet, löste det indirekt, och alla i kontoret anpassade sig runt den utan att någonsin fråga varför fönstret behövde stängas manuellt varje gång. Ingen byggde faktiskt en fungerande stängningsmekanism.

Det är precis vad ett antipattern är — en lösning som “råkar funka” genom att alla anpassar sig runt ett underliggande problem, istället för att lösa det. Designmönster är motsatsen: strukturerade, namngivna, begripliga lösningar — inte “det bara funkar om vi gör så här”.

God Object (God Class)

En enda klass som vet och gör alldeles för mycket.

public class App
{
public void ConnectDb() { /* ... */ }
public void SaveUser(User user) { /* ... */ }
public void SendEmail(string to, string subject, string body) { /* ... */ }
public void GenerateReport() { /* ... */ }
public void CalculateTax(decimal amount) { /* ... */ }
public void RenderHomepage() { /* ... */ }
}

En ändring i e-postleverantören rör samma klass som en ändring i skatteberäkningen. Sammanslagningskonflikter i git blir vanliga eftersom alla ändrar samma fil. Går inte att testa isolerat — vill du testa CalculateTax drar du med dig hela klassens övriga beroenden. Klassen växer och växer, ingen vågar röra den.

Fix: Bryt isär enligt Single Responsibility-principen (se Interfaces, Repository) — en klass, ett ansvar.

Spaghetti Code

Ingen tydlig struktur — allt anropar allt, inga lager.

  • UI-kod anropar databasfunktioner direkt, utan lager emellan.
  • Affärslogik ligger inbäddad i event-hanterare istället för i egna klasser.
  • Globala variabler delas mellan helt orelaterade delar av applikationen.

Symptomet märks vid felsökning: en bugg i ett UI-element kräver att du förstår databaslogik, e-postutskick och rapportgenerering samtidigt, eftersom allt är hoptrasslat i samma kodväg.

Fix: Inför lager (UI → affärslogik → datalager) med tydliga gränser — se Facade och Repository för konkreta sätt att dra de gränserna.

Copy-Paste Inheritance (Copy-Paste-programmering)

Att kopiera en klass och ändra kopian, istället för att faktiskt återanvända kod via arv eller komposition.

// Kopierad och lätt ändrad — istället för återanvändning
public class EmailValidator { public bool Validera(string s) => s.Contains("@"); }
public class UsernameValidator { public bool Validera(string s) => s.Length > 3; }
// ... och nu finns samma struktur duplicerad fem gånger, med fem separata buggar att fixa

Hittar du en bugg i en av kopiorna måste du komma ihåg att fixa den i alla kopior — och det är precis det som glöms bort.

Fix: Extrahera ett gemensamt interface eller basklass, använd komposition eller arv på riktigt istället för att duplicera.

Golden Hammer

Redan täckt i detalj på Generics-sidan — att använda samma verktyg eller mönster på allt, oavsett om det passar. Värt att nämna här igen: det gäller inte bara generics. Att tvinga in Singleton, Factory eller Observer överallt “för att det är ett mönster” är samma antipattern, bara med ett annat verktyg i handen.

Magic Numbers och Magic Strings

Hårdkodade värden utan förklaring, utspridda i koden:

if (user.Role == 3) { /* ... */ } // Vad är 3?
if (status == "PROC_CMPLT") { /* ... */ } // Vad betyder detta?

Sex månader senare vet ingen — inte ens den som skrev det — vad 3 eller "PROC_CMPLT" faktiskt representerar.

Fix: Namnge värdet — en konstant eller ett enum:

if (user.Role == UserRole.Admin) { /* ... */ }

Lava Flow

Död kod som ingen vågar ta bort, eftersom ingen längre vet om den faktiskt används eller varför den finns. Namnet kommer av att den “stelnat” som lava — svår att röra utan att riskera att något går sönder.

Fix: Testtäckning gör det säkert att ta bort — om testerna fortfarande är gröna efter borttagning, var koden faktiskt död. Se Testa din kod.

TL;DR

Ett antipattern är en lösning som “fungerar” ytligt men bygger på att alla anpassar sig runt ett olöst problem, istället för att lösa det. De vanligaste: God Object (en klass gör allt), Spaghetti Code (inga lager), Copy-Paste Inheritance (duplicering istället för återanvändning), Golden Hammer (fel verktyg används på allt), Magic Numbers (oförklarade värden) och Lava Flow (död kod ingen vågar röra).