DRY — Don't Repeat Yourself
Præsenter som slidesLæringsmål
- Kan forklare DRY-princippet med egne ord
- Kan genkende gentagen kode og samle den i metoder
- Kan refaktorere simpel duplikeret logik uden at ændre adfærd
- Kan skelne mellem DRY og for tidlig abstraktion (KISS)
DRY — Don't Repeat Yourself
DRY betyder: gentag ikke den samme logik mange steder. Hvis du har skrevet det samme (eller næsten det samme) to gange, bør det typisk være én metode — så retter du fejl ét sted.
DRY handler om viden og logik — ikke om at to linjer tilfældigt ligner hinanden. Console.WriteLine("Hej"); og Console.WriteLine("Farvel"); er ikke et DRY-problem.
DRY betyder ikke "lav alt generisk med det samme". Én kopi er bedre end en dårlig abstraktion. Se KISS — forenkel først, derefter samle gentagelser når mønsteret er tydeligt.
Hvad skal samles?
| DRY — gentag logik | Ikke nødvendigvis DRY |
|---|---|
| Samme beregning tre steder | To Console.WriteLine med forskellig tekst |
Samme validering i to if |
To variabler med samme type |
| Samme udskriftsformat i en løkke og udenfor | Lignende men forskellig forretningsregel |
Eksempel 1 — Samme udskrift tre gange
Gentaget (DRY-brud):
Console.WriteLine("=== Rapport ===");
Console.WriteLine($"Navn: {name}");
Console.WriteLine($"Score: {score}");
Console.WriteLine("=== Rapport ===");
Console.WriteLine($"Navn: {teacher}");
Console.WriteLine($"Score: {examScore}");
Console.WriteLine("=== Rapport ===");
Console.WriteLine($"Navn: {guest}");
Console.WriteLine($"Score: {guestScore}");
DRY:
Eksempel 2 — Samme grænseværdi mange steder
Gentaget:
if (score >= 60)
{
Console.WriteLine("Bestået");
}
// ... 30 linjer senere ...
if (score >= 60)
{
bonusPoints = 5;
}
// ... igen ...
passed = score >= 60;
DRY:
Nu ændrer du ét sted, hvis grænsen bliver 50 eller 70.
Eksempel 3 — Læs input med samme validering
Gentaget:
DRY:
Senere kan du udvide én metode med TryParse og fejlbeskeder — ikke tre kopier.
Eksempel 4 — Samme loop-mønster
Gentaget:
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine($"{i + 1}. {names[i]}");
}
// ... senere med et andet array ...
for (int i = 0; i < cities.Length; i++)
{
Console.WriteLine($"{i + 1}. {cities[i]}");
}
DRY:
Eksempel 5 — Beregning kopieret i if og else
Gentaget:
if (hours > 40)
{
double pay = 40 * rate + (hours - 40) * rate * 1.5;
Console.WriteLine(pay);
}
else
{
double pay = hours * rate;
Console.WriteLine(pay);
}
DRY:
Reglen for løn findes ét sted — nemmere at teste og forklare.
Eksempel 6 — Validering kopieret til flere felter
Gentaget:
if (string.IsNullOrWhiteSpace(firstName))
{
Console.WriteLine("Fornavn mangler.");
return;
}
if (string.IsNullOrWhiteSpace(lastName))
{
Console.WriteLine("Efternavn mangler.");
return;
}
if (string.IsNullOrWhiteSpace(email))
{
Console.WriteLine("Email mangler.");
return;
}
DRY:
// gf2-setup: string firstName = "Ada";
// gf2-setup: string lastName = "Lovelace";
// gf2-setup: string email = "[email protected]";
if (!RequireText(firstName, "Fornavn")) return;
if (!RequireText(lastName, "Efternavn")) return;
if (!RequireText(email, "Email")) return;
Console.WriteLine("Alle felter er udfyldt.");
static bool RequireText(string value, string fieldLabel)
{
if (string.IsNullOrWhiteSpace(value))
{
Console.WriteLine($"{fieldLabel} mangler.");
return false;
}
return true;
}
Eksempel 7 — Formatér output ens
Gentaget:
Console.WriteLine($"Pris: {price:F2} kr.");
Console.WriteLine($"Rabat: {discount:F2} kr.");
Console.WriteLine($"Total: {total:F2} kr.");
DRY:
Eksempel 8 — Switch med duplikeret kode i cases
Gentaget:
switch (choice)
{
case "1":
Console.WriteLine("Starter spil...");
Console.WriteLine("Indlæser data...");
StartGame();
break;
case "2":
Console.WriteLine("Starter øvelse...");
Console.WriteLine("Indlæser data...");
StartPractice();
break;
}
DRY:
Eksempel 9 — Array + manuel gentagelse
Gentaget:
int sum = numbers[0] + numbers[1] + numbers[2] + numbers[3] + numbers[4];
double avg = sum / 5.0;
Console.WriteLine($"Sum: {sum}, Avg: {avg:F1}");
Hvis arrayet vokser, kopierer folk ofte mønsteret igen.
DRY:
Eksempel 10 — Konstanter frem for "magic numbers"
Gentaget:
if (attempts > 3) { /* ... */ }
// ...
if (attempts > 3) { /* ... */ }
// ...
maxAttempts = 3;
DRY:
Konstanten er ét navngivet sted for tallet 3.
DRY vs. KISS — hvornår venter du?
| Situation | Gør |
|---|---|
| Koden er gentaget 2+ gange med samme regel | Overvej metode |
| Du har kun set mønsteret én gang | Vent — lad være med at abstrahere for tidligt |
| Abstraktionen kræver 8 parametre | Forenkl først (KISS) |
Metodenavnet er uklart (DoThing) |
DRY hjælper ikke — navngiv bedre |
Rule of Three (tommelfingerregel): Først når noget gentager sig tre gange, er det ofte værd at samle det. To gange kan du leve med — især i små øvelsesprogrammer.
Refaktorering trin for trin
- Find to blokke der gør det samme (eller næsten)
- Uddrag til en metode med et beskrivende navn
- Erstat kopierne med metodekald
- Kør programmet — adfærden skal være uændret
- Formater med Ctrl+Shift+F i GF2 Learn
Hurtig tjekliste
- Er samme regel skrevet mere end ét sted?
- Har jeg magic numbers der burde være
const? - Kan jeg samle gentagen logik i en metode med et klart navn?
- Er min abstraktion enklere end kopierne — ikke sværere?
Test din viden
Spørgsmål 1
Hvad betyder DRY?
DRY handler om vedligeholdelse: én rettelse ét sted i stedet for at jage fejl i mange kopier.
Spørgsmål 2
Hvilket er et tydeligt DRY-brud?
Samme grænse/regel på flere steder bør typisk være en metode eller konstant.
Spørgsmål 3
Hvad er den bedste DRY-løsning til gentagen "=== Rapport ===" udskrift?
En navngiven metode med få parametre samler formatet uden at blive uforståelig.
Spørgsmål 4
Hvad er Rule of Three?
Tommelfingerregel mod for tidlig abstraktion — to gentagelser kan være OK i små opgaver.
Spørgsmål 5
Hvordan arbejder DRY og KISS sammen?
Først simpel kode — derefter genbrug når mønsteret er klart. Dårlig DRY (for generisk) bryder KISS.
Spørgsmål 6
Hvorfor er const int PassingScore = 60; et DRY-trick?
Magic numbers spredt i koden er et DRY-problem — konstanten er ét sted at ændre grænsen.