Meer informatie over code smells

Opdracht 1 Te lange methode

Pak de code smell aan. Refactor de te lange methode.

Specificaties

  • Verdeel de te lange methode in kortere methoden.
  • Controleer of elke methode één verantwoordelijkheid heeft (Single Responsibility).
  • Controleer of de implementatie correct werkt.

Verwachte output

Order processing started...
Verzonden orderbevestiging naar: jan@example.com
Betaling verwerken van €1250...
Grote betaling vereist handmatige controle.
Order succesvol verwerkt!

Nu jij

using System;
using System.Collections.Generic;
 
class Order
{
    public string CustomerEmail { get; set; }
    public List<OrderItem> Items { get; set; } = new List<OrderItem>();
}
 
class OrderItem
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int Quantity { get; set; }
}
 
class OrderProcessor
{
    public void ProcessOrder(Order order)
    {
        Console.WriteLine("Order processing started...");
 
        // 1. Orderbevestiging versturen
        Console.WriteLine($"Verzonden orderbevestiging naar: {order.CustomerEmail}");
 
        // 2. Totale prijs berekenen
        decimal totalPrice = 0;
        foreach (var item in order.Items)
        {
            totalPrice += item.Price * item.Quantity;
        }
        Console.WriteLine($"Betaling verwerken van €{totalPrice}...");
 
        // 3. Grote betalingen extra controleren
        if (totalPrice > 1000)
        {
            Console.WriteLine("Grote betaling vereist handmatige controle.");
        }
 
        // 4. Order afronden
        Console.WriteLine("Order succesvol verwerkt!");
    }
}
 
class Program
{
    static void Main()
    {
        Order order = new Order
        {
            CustomerEmail = "jan@example.com",
            Items = new List<OrderItem>
            {
                new OrderItem { Name = "Laptop", Price = 1200, Quantity = 1 },
                new OrderItem { Name = "Muis", Price = 25, Quantity = 2 }
            }
        };
 
        OrderProcessor processor = new OrderProcessor();
        processor.ProcessOrder(order);
    }
}

Opdracht 2 Te grote klasse

Een te grote klasse heeft teveel verantwoordelijkheden, is slecht onderhoudbaar, schendt het Single Responsibility Principle.

Specificaties

  • Refactor de klasse naar meerdere klassen.
  • Controleer of de verantwoordelijkheden verdeeld zijn.
  • Controleer de werking van de implementatie.

Verwachte output

Order aangemaakt.
Bevestiging verzonden naar jan@example.com
Betaling van €1250 verwerkt.
Bestelling verzonden naar jan@example.com

Nu jij

class OrderManager
{
    public void CreateOrder(string customerEmail, List<OrderItem> items)
    {
        Console.WriteLine("Order aangemaakt.");
    }
 
    public void SendOrderConfirmation(string email)
    {
        Console.WriteLine($"Bevestiging verzonden naar {email}");
    }
 
    public decimal CalculateTotalPrice(List<OrderItem> items)
    {
        decimal total = 0;
        foreach (var item in items)
        {
            total += item.Price * item.Quantity;
        }
        return total;
    }
 
    public void ProcessPayment(decimal amount)
    {
        Console.WriteLine($"Betaling van €{amount} verwerkt.");
    }
 
    public void ShipOrder(string customerEmail)
    {
        Console.WriteLine($"Bestelling verzonden naar {customerEmail}");
    }
 
    public void HandleRefund(string customerEmail, decimal amount)
    {
        Console.WriteLine($"Terugbetaling van €{amount} verwerkt voor {customerEmail}.");
    }
}

Opdracht 3 Inconsistente naamgeving

Verbeter de naamgeving van de methoden.

Specificaties

  • Controleer de consistentie van de gebruikte naamgeving van klassen, methoden, parameters en variabelen.
  • Pas inconsistenties aan.
  • Controleer de werking van de implementatie.

Verwachte output

Bob
Aantal gebruikers: 1

Nu jij

using System;
using System.Collections.Generic;
 
class UserManager
{
    private List<string> userList = new List<string>();
 
    public void AddUser(string userName)
    {
        userList.Add(userName);
    }
 
    public void remove_user(string username)
    {
        userList.Remove(username);
    }
 
    public int CountUsers()
    {
        return userList.Count;
    }
 
    public void printAllUsers()
    {
        foreach (var user in userList)
        {
            Console.WriteLine(user);
        }
    }
}
 
class Program
{
    static void Main()
    {
        UserManager userManager = new UserManager();
        userManager.AddUser("Alice");
        userManager.AddUser("Bob");
        userManager.remove_user("Alice"); 
        userManager.printAllUsers();
        Console.WriteLine("Aantal gebruikers: " + userManager.CountUsers());
    }
}

Opdracht 4 Te complexe statements

Vereenvoudig het statement.

Specificaties

  • Vereenvoudig het complexe statement.
  • Geef de logica waar mogelijk een aparte methode.
  • Controleer de werking van de implementatie.

Verwachte output

Grade: B

Nu jij

> class Program
> {
>     static void Main()
>     {
>         int score = 85;
>         string result = (score >= 90) ? "A" : (score >= 80) ? "B" : (score >= 70) ? "C" : (score >= 60) ? "D" : "F";
>         Console.WriteLine($"Grade: {result}");
>     }
> }

Opdracht 5 Duplicate code

De implementatie bevat code die grotendeels op elkaar lijkt.

Specificaties

  • Elimineer de duplicate code.
  • Controleer of je project na de wijziging gebuild kan worden.

Verwachte output

Geen verwachte output.

Nu jij

class DiscountCalculator
{
    public decimal CalculateDiscountForRegularCustomer(decimal amount)
    {
        if (amount > 100)
        {
            return amount * 0.10m;
        }
        return amount * 0.05m;
    }
 
    public decimal CalculateDiscountForPremiumCustomer(decimal amount)
    {
        if (amount > 100)
        {
            return amount * 0.15m;
        }
        return amount * 0.10m;
    }
}

Opdracht 6 Overbodig commentaar en slecht commentaar

De implementatie bevat zowel overbodig commentaar als commentaar dat opgenomen had moeten worden als logica.

Specificaties

  • Verwijder overbodig commentaar.
  • Implementeer logica waar deze ontbreekt en wel in code is opgenomen.
  • Controleer of het project gebuild kan worden.

Verwachte output

Geen output in de console.

Nu jij

class Calculator
{
// Dit is een klasse genaamd Calculator
 
    // Deze methode berekent de som van twee getallen  
    public int Add(int a, int b)  
    {  
        return a + b; // Hier tellen we a en b op en retourneren het resultaat  
    }  
 
    // Deze methode controleert of een getal even is  
    public bool IsEven(int number)  
    {  
        return number % 2 == 0; // Als de rest 0 is, is het even  
    }  
 
    /* Deze methode haalt het maximum uit een lijst.  
    LET OP: Deze methode gaat ervan uit dat de lijst niet leeg is! */  
    public int GetMax(List<int> numbers)  
    {  
        return numbers.Max(); // Haalt het maximum  
    }
}  

Opdracht 7 Mutable state

De klasse houdt onnodig de state bij. Ook kan de waarde onbedoeld negatief worden.

Specificaties

  • Wijzig de locatie van de state.
  • Voeg validatie toe.
  • Controleer de output.

Verwachte output

Count: 0

Nu jij

using System;
 
class Counter
{
    private int _count = 0; // Veranderlijke toestand die willekeurig via de methoden aangepast kan worden.
 
    public void Increment()
    {
        _count++;
    }
 
    public void Decrement()
    {
        _count--;
    }
 
    public int GetCount()
    {
        return _count;
    }
}
 
class Program
{
    static void Main()
    {
        Counter counter = new Counter();
        counter.Increment();
        counter.Decrement();
        counter.Decrement(); // Oeps! Nu kan de teller een negatieve waarde krijgen.
        Console.WriteLine($"Count: {counter.GetCount()}");
    }
}

Opdracht 8 Inefficiënte String-Concatenering

String-concatenering met de +-operator in een lus kan prestatieproblemen veroorzaken, omdat elke concatenatie een nieuw stringobject aanmaakt en de vorige strings kopieert. Gebruik in plaats daarvan de StringBuilder-klasse om strings efficiënt samen te voegen. Dit bespaart geheugen en verhoogt de snelheid van je code.

Specificaties

  • Gebruik de Stringbuilder voor efficiente concatenering
  • Controleer de output

Verwachte output

0, 1, 2, 3, 4, 5, ... , 998, 999, 

Nu jij

using System;
 
class Program
{
    static void Main()
    {
        string result = "";
        for (int i = 0; i < 1000; i++)
        {
            result += i + ", "; // Inefficiënt: maakt telkens een nieuwe string aan
        }
 
        Console.WriteLine(result);
    }
}

Opdracht 9 Overbodige Boolean-Literals

Overbodige boolean-literals ontstaan wanneer je een true of false expliciet vergelijkt met een boolean-variabele (isReady == true). Dit is onnodig, omdat een boolean-variabele al zelf true of false oplevert. Laat de vergelijking weg en gebruik de variabele direct.

Specificaties

  • Refactor de implementatie zodat de onnodige booleans zijn weggewerkt.
  • Controleer of het project build.

Verwachte output

Geen output.

Nu jij

class Example
{
    bool IsValid(int number)
    {
        if (number > 0)
            return true;  // Overbodige Boolean-literal
        else
            return false; // Overbodige Boolean-literal
    }
}

Opdracht 10 Magic numbers

Magic numbers zijn letterlijke getallen zonder context of betekenis in je code (zoals 3.14, 42 of 1000). Dit kan ambiguïteit en inconsistentie veroorzaken. Vervang magic numbers door constanten of enums met betekenisvolle namen om de leesbaarheid en onderhoudbaarheid te verbeteren.

Specificaties

  • Ontmasker het magic number.
  • Controleer of het project build.

Verwachte output

Geen output.

Nu jij

class Circle
{
    public double CalculateCircumference(double radius)
    {
        return 2 * 3.14159 * radius; // Magic number
    }
}

Opdracht 11 Closed switches

Switch-statements kunnen de Open-Closed Principle schenden, omdat je bestaande code moet wijzigen wanneer je een nieuwe case toevoegt. Dit kan complexiteit en duplicatie veroorzaken. Een betere aanpak is polymorfisme, waarbij je overerving en virtuele methoden gebruikt om verschillend gedrag te verwerken.

Specificaties

  • Maak een abstracte klasse PaymentMethod met een abstrcte methode Process.
  • De PaymentProcessor ontvangt een type PaymentMethod.
  • Voer de code uit en controleer de output.

Verwachte output

Processing credit card payment...
Processing PayPal payment...

Nu jij

class PaymentProcessor
{
    public void ProcessPayment(string paymentType)
    {
        switch (paymentType)
        {
            case "CreditCard":
                Console.WriteLine("Processing credit card payment...");
                break;
            case "PayPal":
                Console.WriteLine("Processing PayPal payment...");
                break;
            default:
                Console.WriteLine("Unknown payment method.");
                break;
        }
    }
}

Opdracht 12 Data clumps

Data clumps ontstaan wanneer groepen variabelen of parameters altijd samen worden gebruikt. Bijvoorbeeld firstName, lastName en age die overal samen worden doorgegeven. Een goede praktijk is om deze gegevens te bundelen in een klasse of struct, zodat ze als één entiteit worden behandeld.

Specificaties

  • Refactor de parameters naar een klasse.
  • Voer het programma en controleer de output.

Verwachte output

User added: John Doe, Age: 30
User: John Doe, Age: 30

Nu jij

using System;
 
class UserManager
{
    public void AddUser(string firstName, string lastName, int age)
    {
        Console.WriteLine($"User added: {firstName} {lastName}, Age: {age}");
    }
 
    public void DisplayUser(string firstName, string lastName, int age)
    {
        Console.WriteLine($"User: {firstName} {lastName}, Age: {age}");
    }
}
 
class Program
{
    static void Main()
    {
        UserManager userManager = new UserManager();
 
        string firstName = "John";
        string lastName = "Doe";
        int age = 30;
 
        userManager.AddUser(firstName, lastName, age);
        userManager.DisplayUser(firstName, lastName, age);
    }
}

Opdracht 13 Primitive Obsession

Primitive obsession treedt op wanneer je primitieve types (int, string, bool) gebruikt om complexe concepten of gedragingen te representeren. Bijvoorbeeld een string voor een telefoonnummer of een valuta. Een betere aanpak is om aangepaste klassen of structs te gebruiken en hierin validatie en logica te kapselen.

Specificaties

  • Maak een klasse Email.
  • Valideer de string email in de klasse en gebruik daarvoor onderstaande code:
if (!Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$"))
    throw new ArgumentException("Invalid email format.");
  • Voer het programma uit en controleer de output.

Verwachte output

Invalid email format.

Nu jij

using System;
 
class User
{
    public string Email { get; set; }
 
    public User(string email)
    {
        Email = email;
    }
 
    public void Display()
    {
        Console.WriteLine($"User Email: {Email}");
    }
}
 
class Program
{
    static void Main()
    {
        User user = new User("invalid-email"); // Geen validatie!
        user.Display();
    }
}

Opdracht 14 Feature envy

Feature envy betekent dat een methode meer geïnteresseerd is in de data of functionaliteit van een andere klasse dan in zijn eigen klasse. Als een methode uit klasse A vaak velden of methoden uit klasse B aanroept, kan dit een teken zijn dat de methode beter in klasse B geplaatst kan worden. Dit verhoogt de cohesie en vermindert de koppeling.

Specificaties

  • Maak een methode GetFullAddress in klasse Address.
  • Maak in de klasse User gebruik van deze methode.
  • Voer de code uit en controleer de output.

Verwachte output

John Doe woont op Main St, New York, 10001

Nu jij

using System;
 
class Address
{
    public string Street { get; }
    public string City { get; }
    public string PostalCode { get; }
 
    public Address(string street, string city, string postalCode)
    {
        Street = street;
        City = city;
        PostalCode = postalCode;
    }
}
 
class User
{
    public string Name { get; }
    public Address Address { get; }
 
    public User(string name, Address address)
    {
        Name = name;
        Address = address;
    }
}
 
class UserService
{
    public void PrintUserAddress(User user)
    {
        // Feature Envy: deze methode gebruikt vooral de details van Address i.p.v. User
        Console.WriteLine($"{user.Name} woont op {user.Address.Street}, {user.Address.City}, {user.Address.PostalCode}");
    }
}
 
class Program
{
    static void Main()
    {
        User user = new User("John Doe", new Address("Main St", "New York", "10001"));
        UserService service = new UserService();
        service.PrintUserAddress(user);
    }
}

Opdracht 15 Tijdelijke velden

Een tijdelijk veld is een klasseveld dat slechts in enkele methoden wordt gebruikt, maar niet in de rest van de klasse. Dit kan verwarrend en inconsistent zijn. Een oplossing is om het veld te verwijderen en in plaats daarvan een lokale variabele of parameter te gebruiken.

Specificaties

  • Elimineer tijdelijke velden door ze te verplaatsen naar de methode die ze daadwerkelijk gebruikt.
  • Voer het programma uit en controleer de output.

Verwachte output

Klant: Jan, Bedrag: 150
Korting toegepast: 15

Nu jij

class Order
{
    public string CustomerName { get; set; }
    public decimal Amount { get; set; }
 
    // Tijdelijke velden, alleen gebruikt bij kortingsberekening
    public decimal DiscountAmount { get; set; }
    public bool IsDiscountApplied { get; set; }
 
    public void ApplyDiscount()
    {
        if (Amount > 100)
        {
            DiscountAmount = Amount * 0.1m;
            IsDiscountApplied = true;
        }
    }
 
    public void PrintOrder()
    {
        Console.WriteLine($"Klant: {CustomerName}, Bedrag: {Amount}");
        if (IsDiscountApplied)
        {
            Console.WriteLine($"Korting toegepast: {DiscountAmount}");
        }
    }
}
 
class Program
{
    static void Main()
    {
        Order order = new Order { CustomerName = "Jan", Amount = 150 };
        order.ApplyDiscount();
        order.PrintOrder();
    }
}