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); }}
Mogelijke uitwerking
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..."); SendOrderConfirmation(order.CustomerEmail); decimal totalPrice = CalculateTotalPrice(order.Items); ProcessPayment(totalPrice); Console.WriteLine("Order succesvol verwerkt!"); } private void SendOrderConfirmation(string email) { Console.WriteLine($"Verzonden orderbevestiging naar: {email}"); } private decimal CalculateTotalPrice(List<OrderItem> items) { decimal totalPrice = 0; foreach (var item in items) { totalPrice += item.Price * item.Quantity; } return totalPrice; } private void ProcessPayment(decimal totalPrice) { Console.WriteLine($"Betaling verwerken van €{totalPrice}..."); if (totalPrice > 1000) { Console.WriteLine("Grote betaling vereist handmatige controle."); } }}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}."); }}
Mogelijke uitwerking
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 OrderService{ public Order CreateOrder(string customerEmail, List<OrderItem> items) { Console.WriteLine("Order aangemaakt."); return new Order { CustomerEmail = customerEmail, Items = items }; }}class PaymentService{ public void ProcessPayment(decimal amount) { Console.WriteLine($"Betaling van €{amount} verwerkt."); }}class ShippingService{ public void ShipOrder(Order order) { Console.WriteLine($"Bestelling verzonden naar {order.CustomerEmail}"); }}class NotificationService{ public void SendOrderConfirmation(string email) { Console.WriteLine($"Bevestiging verzonden naar {email}"); }}class OrderProcessor{ private readonly OrderService _orderService = new(); private readonly PaymentService _paymentService = new(); private readonly ShippingService _shippingService = new(); private readonly NotificationService _notificationService = new(); public void ProcessOrder(string customerEmail, List<OrderItem> items) { Order order = _orderService.CreateOrder(customerEmail, items); _notificationService.SendOrderConfirmation(order.CustomerEmail); decimal totalPrice = CalculateTotalPrice(order.Items); _paymentService.ProcessPayment(totalPrice); _shippingService.ShipOrder(order); } private decimal CalculateTotalPrice(List<OrderItem> items) { decimal total = 0; foreach (var item in items) { total += item.Price * item.Quantity; } return total; }}class Program{ static void Main() { List<OrderItem> items = new() { new OrderItem { Name = "Laptop", Price = 1200, Quantity = 1 }, new OrderItem { Name = "Muis", Price = 25, Quantity = 2 } }; OrderProcessor processor = new(); processor.ProcessOrder("jan@example.com", items); }}
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()); }}
Mogelijke uitwerking
class UserManager{ private List<string> _users = new List<string>(); public void AddUser(string userName) => _users.Add(userName); public void RemoveUser(string userName) => _users.Remove(userName); public int UserCount => _users.Count; public void DisplayAllUsers() { foreach (var user in _users) { Console.WriteLine(user); } }}
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 }}
Mogelijke uitwerking
using System;using System.Collections.Generic;using System.Linq;class Calculator{ public int Add(int a, int b) { return a + b; } public bool IsEven(int number) { return number % 2 == 0; } public int GetMax(List<int> numbers) { if (numbers == null || numbers.Count == 0) { throw new ArgumentException("De lijst mag niet leeg zijn."); } return numbers.Max(); } }class Program{ static void Main() { Calculator calculator = new Calculator(); Console.WriteLine(calculator.Add(5, 3)); Console.WriteLine(calculator.IsEven(10)); List<int> numbers = new List<int> { 4, 7, 1, 9 }; Console.WriteLine(calculator.GetMax(numbers)); } }
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()}"); }}
Mogelijke uitwerking
class Counter{ public int Increment(int count) => count + 1; public int Decrement(int count) => count > 0 ? count - 1 : 0; // Voorkomt negatieve waarden}class Program{ static void Main() { Counter counter = new Counter(); int count = 0; count = counter.Increment(count); count = counter.Decrement(count); count = counter.Decrement(count); Console.WriteLine($"Count: {count}"); // Altijd >= 0 }}
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); }}
Mogelijke uitwerking
using System;using System.Text;class Program{ static void Main() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.Append(i).Append(", "); // Efficiënt: werkt in-memory zonder nieuwe objecten aan te maken } Console.WriteLine(sb.ToString()); }}
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.
class Example{ bool IsValid(int number) => number > 0; // Korter en duidelijker}
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 }}
Mogelijke uitwerking
class Circle{ private const double Pi = 3.14159; // Gebruik een constante public double CalculateCircumference(double radius) { return 2 * Pi * radius; }}
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.
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; } }}
Mogelijke uitwerking
abstract class PaymentMethod{ public abstract void Process();}class CreditCardPayment : PaymentMethod{ public override void Process() => Console.WriteLine("Processing credit card payment...");}class PayPalPayment : PaymentMethod{ public override void Process() => Console.WriteLine("Processing PayPal payment...");}class PaymentProcessor{ public void ProcessPayment(PaymentMethod payment) { payment.Process(); }}class Program{ static void Main() { PaymentProcessor processor = new PaymentProcessor(); PaymentMethod payment = new CreditCardPayment(); processor.ProcessPayment(payment); payment = new PayPalPayment(); processor.ProcessPayment(payment); }}
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); }}
Mogelijke uitwerking
using System;class Person{ public string FirstName { get; } public string LastName { get; } public int Age { get; } public Person(string firstName, string lastName, int age) { FirstName = firstName; LastName = lastName; Age = age; } public override string ToString() => $"{FirstName} {LastName}, Age: {Age}";}class UserManager{ public void AddUser(Person person) { Console.WriteLine($"User added: {person}"); } public void DisplayUser(Person person) { Console.WriteLine($"User: {person}"); }}class Program{ static void Main() { Person person = new Person("John", "Doe", 30); UserManager userManager = new UserManager(); userManager.AddUser(person); userManager.DisplayUser(person); }}
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(); }}
Mogelijke uitwerking
using System;using System.Text.RegularExpressions;class Email{ public string Value { get; } public Email(string email) { if (!Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$")) throw new ArgumentException("Invalid email format."); Value = email; } public override string ToString() => Value;}class User{ public Email Email { get; } public User(Email email) { Email = email; } public void Display() { Console.WriteLine($"User Email: {Email}"); }}class Program{ static void Main() { try { User user = new User(new Email("john.doe@example.com")); user.Display(); } catch (Exception ex) { Console.WriteLine(ex.Message); } }}
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); }}
Mogelijke uitwerking
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; } public string GetFullAddress() => $"{Street}, {City}, {PostalCode}";}class User{ public string Name { get; } public Address Address { get; } public User(string name, Address address) { Name = name; Address = address; } public void PrintAddress() { Console.WriteLine($"{Name} woont op {Address.GetFullAddress()}"); }}class Program{ static void Main() { User user = new User("John Doe", new Address("Main St", "New York", "10001")); user.PrintAddress(); }}
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(); }}
Mogelijke uitwerking
class Order{ public string CustomerName { get; set; } public decimal Amount { get; set; } public Discount? ApplyDiscount() { return Amount > 100 ? new Discount(Amount * 0.1m) : null; } public void PrintOrder(Discount? discount) { Console.WriteLine($"Klant: {CustomerName}, Bedrag: {Amount}"); if (discount != null) { Console.WriteLine($"Korting toegepast: {discount.Amount}"); } }}class Discount{ public decimal Amount { get; } public Discount(decimal amount) => Amount = amount;}class Program{ static void Main() { Order order = new Order { CustomerName = "Jan", Amount = 150 }; Discount? discount = order.ApplyDiscount(); order.PrintOrder(discount); }}