Wir alle kennen das: Wir schreiben Code, der funktioniert, aber im Hinterkopf nagt das Gefühl, dass da noch Luft nach oben ist. Dass der Code sauberer, eleganter und wartbarer sein könnte. In diesem Artikel tauchen wir tief in die Welt des Code-Reviews ein und demonstrieren anhand eines konkreten C# Beispiels, wie man aus gutem Code noch besseren Code macht. Ziel ist es, Ihnen praktische Tipps und Techniken an die Hand zu geben, die Sie sofort in Ihren eigenen Projekten anwenden können.
Warum ist sauberer Code wichtig?
Bevor wir uns dem Beispielcode widmen, wollen wir kurz beleuchten, warum Clean Code so entscheidend ist. Hier sind einige der wichtigsten Vorteile:
- Wartbarkeit: Sauberer Code ist leichter zu verstehen, zu ändern und zu debuggen. Dies spart Zeit und Ressourcen, besonders in langfristigen Projekten.
- Lesbarkeit: Wenn Code klar und verständlich ist, können andere Entwickler (und Sie selbst in ein paar Monaten!) ihn schnell erfassen und daran arbeiten.
- Zusammenarbeit: Sauberer Code fördert eine bessere Zusammenarbeit im Team, da alle Beteiligten auf der gleichen Seite stehen.
- Weniger Fehler: Gut strukturierter Code ist weniger anfällig für Fehler und Bugs.
- Performance: Optimierter Code kann die Performance Ihrer Anwendung verbessern.
Unser Beispielcode: Eine einfache Bestellabwicklung
Nehmen wir an, wir haben folgenden C# Code, der eine einfache Bestellabwicklung simuliert:
„`csharp
public class OrderProcessor
{
public void ProcessOrder(string customerName, List
{
if (string.IsNullOrEmpty(customerName))
{
throw new ArgumentException(„Customer name cannot be empty.”);
}
if (items == null || items.Count == 0)
{
throw new ArgumentException(„Order must contain at least one item.”);
}
decimal totalAmount = 0;
foreach (string item in items)
{
// Simulate getting the price from a database or API
decimal itemPrice = GetItemPrice(item);
totalAmount += itemPrice;
}
totalAmount = totalAmount * (1 – discount);
Console.WriteLine($”Order for {customerName} processed successfully.”);
Console.WriteLine($”Total amount: {totalAmount:C}”);
}
private decimal GetItemPrice(string item)
{
// Simulate getting the price from a database or API
if (item == „ProductA”) return 10.00m;
if (item == „ProductB”) return 20.00m;
if (item == „ProductC”) return 30.00m;
return 5.00m; // Default price
}
}
„`
Dieser Code funktioniert, aber er ist nicht besonders sauber. Was können wir verbessern?
Schritt 1: Validierung verbessern
Die Validierung am Anfang der ProcessOrder
Methode ist gut, aber wir können sie noch lesbarer und wartbarer gestalten. Anstatt separate if
-Abfragen zu verwenden, können wir eine Hilfsmethode erstellen, die die Validierung durchführt. Dies verbessert die Lesbarkeit der Hauptmethode:
„`csharp
private void ValidateOrder(string customerName, List
{
if (string.IsNullOrEmpty(customerName))
{
throw new ArgumentException(„Customer name cannot be empty.”);
}
if (items == null || items.Count == 0)
{
throw new ArgumentException(„Order must contain at least one item.”);
}
}
„`
Die ProcessOrder
Methode sieht dann so aus:
„`csharp
public void ProcessOrder(string customerName, List
{
ValidateOrder(customerName, items);
decimal totalAmount = 0;
foreach (string item in items)
{
// Simulate getting the price from a database or API
decimal itemPrice = GetItemPrice(item);
totalAmount += itemPrice;
}
totalAmount = totalAmount * (1 – discount);
Console.WriteLine($”Order for {customerName} processed successfully.”);
Console.WriteLine($”Total amount: {totalAmount:C}”);
}
„`
Schritt 2: Die Preisermittlung auslagern
Die GetItemPrice
Methode ist momentan sehr einfach und hardcodiert. In einer realen Anwendung würden wir die Preise aus einer Datenbank oder einem API abrufen. Aber auch in diesem einfachen Beispiel können wir die Methode verbessern, indem wir eine Dictionary
verwenden, um die Preise zu speichern:
„`csharp
private static readonly Dictionary
{
{„ProductA”, 10.00m},
{„ProductB”, 20.00m},
{„ProductC”, 30.00m}
};
private decimal GetItemPrice(string item)
{
if (ItemPrices.TryGetValue(item, out decimal price))
{
return price;
}
return 5.00m; // Default price
}
„`
Dies macht die Methode lesbarer und leichter zu erweitern, wenn neue Produkte hinzugefügt werden müssen.
Schritt 3: Berechnung des Gesamtbetrags optimieren
Die Berechnung des Gesamtbetrags kann mit LINQ vereinfacht werden. Dies macht den Code kompakter und ausdrucksstärker:
„`csharp
decimal totalAmount = items.Sum(item => GetItemPrice(item));
„`
Dies ersetzt die gesamte foreach
-Schleife durch eine einzige Zeile Code.
Schritt 4: Discount-Logik verbessern
Die Berechnung des Discounts ist momentan sehr einfach. In einer realen Anwendung gäbe es wahrscheinlich komplexere Regeln für die Berechnung des Discounts. Wir könnten die Discount-Logik in eine separate Methode oder sogar eine separate Klasse auslagern. Um die Lesbarkeit zu erhöhen, können wir auch den Rabatt als Prozentsatz (z.B. 0.1 für 10%) übergeben:
„`csharp
private decimal CalculateDiscountedAmount(decimal totalAmount, decimal discountPercentage)
{
return totalAmount * (1 – discountPercentage);
}
„`
Die ProcessOrder
Methode wird dann wie folgt angepasst:
„`csharp
totalAmount = CalculateDiscountedAmount(totalAmount, discount);
„`
Schritt 5: Logging hinzufügen
In einer realen Anwendung würden wir Logging verwenden, um Informationen über die Bestellabwicklung zu protokollieren. Dies hilft uns, Fehler zu finden und die Performance der Anwendung zu überwachen. Wir können eine ILogger
Schnittstelle verwenden, um das Logging zu implementieren. Hier der Einfachheit halber eine Konsolenausgabe, aber in einer echten Anwendung würde man ein Logging-Framework wie Serilog oder NLog verwenden.
„`csharp
Console.WriteLine($”Calculating order for {customerName}…”);
„`
Der verbesserte Code
Hier ist der gesamte Code nach den Verbesserungen:
„`csharp
public class OrderProcessor
{
private static readonly Dictionary
{
{„ProductA”, 10.00m},
{„ProductB”, 20.00m},
{„ProductC”, 30.00m}
};
public void ProcessOrder(string customerName, List
{
ValidateOrder(customerName, items);
Console.WriteLine($”Calculating order for {customerName}…”);
decimal totalAmount = items.Sum(item => GetItemPrice(item));
totalAmount = CalculateDiscountedAmount(totalAmount, discountPercentage);
Console.WriteLine($”Order for {customerName} processed successfully.”);
Console.WriteLine($”Total amount: {totalAmount:C}”);
}
private void ValidateOrder(string customerName, List
{
if (string.IsNullOrEmpty(customerName))
{
throw new ArgumentException(„Customer name cannot be empty.”);
}
if (items == null || items.Count == 0)
{
throw new ArgumentException(„Order must contain at least one item.”);
}
}
private decimal GetItemPrice(string item)
{
if (ItemPrices.TryGetValue(item, out decimal price))
{
return price;
}
return 5.00m; // Default price
}
private decimal CalculateDiscountedAmount(decimal totalAmount, decimal discountPercentage)
{
return totalAmount * (1 – discountPercentage);
}
}
„`
Fazit
Durch die Anwendung einfacher Prinzipien wie Validierung, Datenstrukturen, LINQ und Logging konnten wir den Code deutlich verbessern. Der Code ist nun lesbarer, wartbarer und weniger anfällig für Fehler. Denken Sie daran, dass Clean Code ein fortlaufender Prozess ist. Nehmen Sie sich die Zeit, Ihren Code regelmäßig zu überprüfen und zu verbessern. Das Ergebnis wird sich auszahlen!
Code-Reviews sind ein integraler Bestandteil dieses Prozesses. Nutzen Sie sie, um von anderen zu lernen und Ihr eigenes Handwerk zu verfeinern. Und vergessen Sie nicht: Der beste Code ist der, der einfach zu verstehen ist und seine Aufgabe effizient erfüllt.