Seeing all five principles violated in one realistic class — and fixed together — makes the connections between them concrete in a way that a five-line summary can't.
// Before: violates SRP (report + notification in one class), OCP (a switch
// that grows for every new notification channel), and DIP (hard-coded
// concrete dependencies constructed inside the class itself).
public class OrderReportService
{
private readonly SqlConnection _connection = new SqlConnection("...");
public void GenerateAndNotify(int orderId, string channel)
{
var order = _connection.Query<Order>("SELECT * FROM Orders WHERE Id = @id", new { id = orderId }).First();
var report = $"Order #{order.Id}: ${order.Total}";
if (channel == "email") SmtpClient.Send("reports@company.com", report);
else if (channel == "sms") TwilioClient.Send("+15551234567", report);
// adding "slack" means editing this method again
}
}
// After: each responsibility is its own class (SRP); new notification
// channels are added by implementing INotifier, not editing existing code
// (OCP); everything depends on interfaces, supplied from outside (DIP).
public interface IOrderRepository { Order GetById(int id); }
public interface INotifier { void Send(string message); }
public class SqlOrderRepository : IOrderRepository
{
private readonly SqlConnection _connection;
public SqlOrderRepository(SqlConnection connection) => _connection = connection;
public Order GetById(int id) =>
_connection.Query<Order>("SELECT * FROM Orders WHERE Id = @id", new { id }).First();
}
public class EmailNotifier : INotifier
{
public void Send(string message) => SmtpClient.Send("reports@company.com", message);
}
public class SlackNotifier : INotifier // added later — no existing class touched
{
public void Send(string message) => SlackClient.PostMessage("#reports", message);
}
public class OrderReportService
{
private readonly IOrderRepository _repository;
private readonly INotifier _notifier;
public OrderReportService(IOrderRepository repository, INotifier notifier)
{
_repository = repository;
_notifier = notifier;
}
public void GenerateAndNotify(int orderId)
{
var order = _repository.GetById(orderId);
_notifier.Send($"Order #{order.Id}: ${order.Total}");
}
}
OrderReportService can now be unit tested with a fake IOrderRepository and a fake INotifier — no real database, no real SMTP server, no real Twilio account required, and no test ever has to be rewritten just because a new notification channel was added elsewhere in the system.