Opdracht 1 Identificeren van verantwoordelijkheden
Je werkt aan een applicatie waarbij een klasse zowel verantwoordelijk is voor het ophalen van gegevens, het berekenen van een resultaat, en het tonen van een bericht. Dit is niet ideaal omdat de klasse meerdere verantwoordelijkheden heeft, wat volgens het Single Responsibility Principle (SRP) niet wenselijk is.
Specificaties
Refactor de code zodat wordt voldaan aan het SRP principe.
Verwachte output
Totaal aantal bestellingen: 3Totale waarde van bestellingen: 450
Nu jij
public class OrderService{ private List<Order> _orders; public OrderService() { _orders = new List<Order> { new Order { Id = 1, Amount = 100 }, new Order { Id = 2, Amount = 200 }, new Order { Id = 3, Amount = 150 } }; } // Haalt een lijst van bestellingen op public List<Order> GetOrders() { return _orders; } // Bereken de totale waarde van de bestellingen public decimal CalculateTotalAmount() { return _orders.Sum(o => o.Amount); } // Print het resultaat naar de console public void PrintOrderSummary() { var total = CalculateTotalAmount(); Console.WriteLine($"Totaal aantal bestellingen: {_orders.Count}"); Console.WriteLine($"Totale waarde van bestellingen: {total}"); }}
Mogelijke uitwerking
public class OrderRepository{ // Haalt een lijst van bestellingen op public List<Order> GetOrders() { return new List<Order> { new Order { Id = 1, Amount = 100 }, new Order { Id = 2, Amount = 200 }, new Order { Id = 3, Amount = 150 } }; }}public class OrderCalculator{ // Bereken de totale waarde van de bestellingen public decimal CalculateTotalAmount(List<Order> orders) { return orders.Sum(o => o.Amount); }}public class OrderPrinter{ // Print het resultaat naar de console public void PrintOrderSummary(List<Order> orders, decimal ?totalAmount) { Console.WriteLine($"Totaal aantal bestellingen: {orders.Count}"); Console.WriteLine($"Totale waarde van bestellingen: {totalAmount}"); }}public class OrderService{ private readonly OrderRepository _orderRepository; private readonly OrderCalculator _orderCalculator; private readonly OrderPrinter _orderPrinter; public OrderService(OrderRepository orderRepository, OrderCalculator orderCalculator, OrderPrinter orderPrinter) { _orderRepository = orderRepository; _orderCalculator = orderCalculator; _orderPrinter = orderPrinter; } // Maakt gebruik van de andere klassen om de bestelling af te handelen public void ShowOrderSummary() { var orders = _orderRepository.GetOrders(); var totalAmount = _orderCalculator.CalculateTotalAmount(orders); _orderPrinter.PrintOrderSummary(orders, totalAmount); }}
Uitleg van de refactoring
OrderRepository: Heeft de verantwoordelijkheid om de bestellingen op te halen.
OrderCalculator: Heeft de verantwoordelijkheid om de totale waarde van de bestellingen te berekenen.
OrderPrinter: Heeft de verantwoordelijkheid om de samenvatting van de bestellingen naar de console te printen.
OrderService: Coördineert de interactie tussen de bovenstaande klassen en voert de taak uit van het tonen van de samenvatting.
In deze refactoring hebben we de verantwoordelijkheid van elke klasse beperkt tot één taak, waardoor de code meer modulair is en gemakkelijker te onderhouden en te testen. Dit is een voorbeeld van hoe het Single Responsibility Principle (SRP) wordt toegepast zonder extra pakketten te installeren.
Opdracht 2 Minimaliseren van afhankelijkheden
Je werkt aan een applicatie waarbij de OrderService klasse afhankelijk is van een specifieke implementatie van data-opslag (zoals DatabaseContext). We willen deze afhankelijkheid minimaliseren door gebruik te maken van een repository die als abstractie voor de data-opslag fungeert. Dit zorgt ervoor dat de OrderService klasse alleen afhankelijk is van de repository interface, en niet van een specifieke implementatie van de data-opslag.
Specificaties:
Refactor de code zodat wordt voldaan aan het SRP principe.
Tip: maak gebruik van interfaces. Hierdoor is het installeren van extra packages niet noodzakelijk.
Verwachte output
Geen.
Nu jij
public class OrderService{ private readonly DatabaseContext _dbContext; private readonly EmailService _emailService; private readonly NotificationService _notificationService; public OrderService() { _dbContext = new DatabaseContext(); // Directe afhankelijkheid van de DatabaseContext _emailService = new EmailService(); // Directe afhankelijkheid van de EmailService _notificationService = new NotificationService(); // Directe afhankelijkheid van de NotificationService } public void PlaceOrder(Order order) { // Bestelling opslaan in de database _dbContext.Orders.Add(order); _dbContext.SaveChanges(); // Stuur een bevestigingsmail _emailService.SendConfirmationEmail(order); // Verstuur een notificatie naar de klant _notificationService.SendOrderNotification(order); }}
Mogelijke uitwerking
public interface IEmailService{ void SendConfirmationEmail(Order order);}public interface INotificationService{ void SendOrderNotification(Order order);}public interface IDatabaseContext{ void AddOrder(Order order); void SaveChanges();}public class OrderService{ private readonly IDatabaseContext _dbContext; private readonly IEmailService _emailService; private readonly INotificationService _notificationService; // Afhankelijkheden via de constructor injecteren public OrderService(IDatabaseContext dbContext, IEmailService emailService,INotificationService notificationService) { _dbContext = dbContext; _emailService = emailService; _notificationService = notificationService; } public void PlaceOrder(Order order) { // Bestelling opslaan in de database _dbContext.AddOrder(order); _dbContext.SaveChanges(); // Stuur een bevestigingsmail _emailService.SendConfirmationEmail(order); // Verstuur een notificatie naar de klant _notificationService.SendOrderNotification(order); }}
Uitleg van de Refactoring
Interfaces: In plaats van directe afhankelijkheden van concrete implementaties (zoals DatabaseContext, EmailService, en NotificationService), hebben we interfaces (IDatabaseContext, IEmailService, INotificationService) geïntroduceerd. Dit maakt het mogelijk om verschillende implementaties te injecteren zonder de OrderService te wijzigen.
Constructor Injectie: De afhankelijkheden worden via de constructor ingevoerd (dependency injection). Dit vermindert de noodzaak om objecten zelf te creëren binnen de klasse, wat de testbaarheid en flexibiliteit van de code verhoogt.
Door afhankelijkheden via DI in te voeren, hebben we de OrderService minder afhankelijk gemaakt van specifieke implementaties. Dit maakt het makkelijker om de code te testen (bijvoorbeeld door mocks in te voeren) en om de implementaties van DatabaseContext, EmailService, en NotificationService te vervangen zonder de OrderService zelf aan te passen.
Testbaarheid: Deze benadering maakt het mogelijk om de OrderService eenvoudiger te testen. Bijvoorbeeld, bij unit tests kun je de interfaces vervangen door mockobjecten, zodat je de externe afhankelijkheden zoals de database of de e-mailservice niet daadwerkelijk hoeft aan te roepen.
Opdracht 3 Kleinere, modulaire klassen
Je werkt aan een applicatie waar je een klasse hebt die meerdere verantwoordelijkheden vervult. De klasse is te groot en moeilijk te testen vanwege de verschillende taken die het uitvoert. We willen deze klasse refactoren naar kleinere, modulaire klassen waarbij elke klasse één verantwoordelijkheid heeft (Single Responsibility Principle).
Specificaties:
Refactor de code zodat wordt voldaan aan het SRP principe.
Verwachte output
Geen.
### Nu jij
In de volgende code voert de OrderProcessor klasse zowel het verwerken van bestellingen, het genereren van facturen, als het verzenden van bevestigingsmails uit. Dit maakt de klasse moeilijk te onderhouden en te testen.
Probleem: De OrderProcessor klasse heeft meerdere verantwoordelijkheden:
- Bestelling verwerken
- Factuur genereren en opslaan
- Bevestigingsmail versturen
Dit maakt de klasse moeilijk te testen en uit te breiden, omdat alle logica in één klasse is samengebracht.
```csharp
public class OrderProcessor
{
public void ProcessOrder(Order order)
{
// Bestelling verwerken
Console.WriteLine("Processing order...");
// Factuur genereren
var invoice = GenerateInvoice(order);
// Factuur opslaan
SaveInvoice(invoice);
// Bevestigingsmail sturen
SendConfirmationEmail(order);
}
private Invoice GenerateInvoice(Order order)
{
return new Invoice
{
OrderId = order.Id,
Amount = order.Amount,
Date = DateTime.Now
};
}
private void SaveInvoice(Invoice invoice)
{
Console.WriteLine($"Saving invoice for order {invoice.OrderId}");
}
private void SendConfirmationEmail(Order order)
{
Console.WriteLine($"Sending confirmation email for order {order.Id}");
}
}
Mogelijke uitwerking
We refactoren de OrderProcessor klasse door de verantwoordelijkheden te verdelen over verschillende kleinere klassen die elk één taak hebben. We creëren bijvoorbeeld een InvoiceGenerator, InvoiceSaver, en EmailSender klasse.
public class OrderProcessor{ private readonly IInvoiceGenerator _invoiceGenerator; private readonly IInvoiceSaver _invoiceSaver; private readonly IEmailSender _emailSender; // Afhankelijkheden via constructor injecteren public OrderProcessor(IInvoiceGenerator invoiceGenerator, IInvoiceSaver invoiceSaver, IEmailSender emailSender) { _invoiceGenerator = invoiceGenerator; _invoiceSaver = invoiceSaver; _emailSender = emailSender; } public void ProcessOrder(Order order) { // Bestelling verwerken Console.WriteLine("Processing order..."); // Factuur genereren var invoice = _invoiceGenerator.GenerateInvoice(order); // Factuur opslaan _invoiceSaver.SaveInvoice(invoice); // Bevestigingsmail sturen _emailSender.SendConfirmationEmail(order); }}// Kleine, modulaire klassenpublic class InvoiceGenerator : IInvoiceGenerator{ public Invoice GenerateInvoice(Order order) { return new Invoice { OrderId = order.Id, Amount = order.Amount, Date = DateTime.Now }; }}public class InvoiceSaver : IInvoiceSaver{ public void SaveInvoice(Invoice invoice) { Console.WriteLine($"Saving invoice for order {invoice.OrderId}"); }}public class EmailSender : IEmailSender{ public void SendConfirmationEmail(Order order) { Console.WriteLine($"Sending confirmation email for order {order.Id}"); }}// Interfaces voor de afhankelijkhedenpublic interface IInvoiceGenerator{ Invoice GenerateInvoice(Order order);}public interface IInvoiceSaver{ void SaveInvoice(Invoice invoice);}public interface IEmailSender{ void SendConfirmationEmail(Order order);}