Course: CSCI 1260
These arenât requiredâbut theyâre good design.
Single Responsibility Principle
- Classes should only have one responsibility
// Without SRP
class Employee {
public string Name { get; set; }
public double Salary { get; set; }
public void SaveToDatabase() { }
public void GenerateReport() { }
}
// With SRP
public class Employee {
public string Name { get; set; }
public double Salary { get; set; }
}
public class EmployeeRepository {
public void SaveToDatabase(Employee employee) { }
}
public class ReportGenerator {
public void GeneratReport(Employee employee) { }
}
Open/Closed Principle
- A class should be open for extension but closed for modification
- Essentially, using polymorphism correctly
// Without OCP
public class AreaCalculator {
public double CalculateArea(object shape) => shape switch
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.Width * r.Height,
_ => throw new NotSupportedException("Invalid shape")
}
// With OCP
public interface IShape {
double CalculateArea();
}
public class Circle : IShape {
public double Radius { get; set; }
public double CalculateArea() => Math.PI * Radius * Radius;
}
public class Rectangle : IShape {
public double Width { get; set; }
public double Height { get; set; }
public double CalculateArea() => Width * Height;
}
public class AreaCalculator {
public double CalculateArea(IShape shape) => shape.CalculateArea();
}
Liskov Substitution Principle
- A subclass should be substitutable for its base class without affecting correctness
// Without LSP
public class Rectangle {
public virtual double Width { get; set; }
public virtual double Height { get; set; }
public double GetArea() => Width * Height;
}
public class Square : Rectangle {
public override double Width {
set { base.Width = base.Height = value; }
}
public override double Height {
set { base.Width = base.Height = value; }
}
}
// With LSP
public interface IShape {
double GetArea();
}
public class Rectangle : IShape {
public double Width { get; set; }
public double Height { get; set; }
public double GetArea() => Width * Height;
}
public class Square : IShape {
public double Side { get; set; }
public double GetArea() => Side * Side;
}
Interface Segregation Principle
- A class should not be forced to implement an interface it does not use
// Without ISP
public interface IWorker {
void Work();
void Eat();
}
public class Robot : IWorker {
public void Work() { }
public void Eat() {
throw new NotImplementedException() // robots can't eat
}
}
// With ISP
public interface IWorkable {
void Work();
}
public interface IEatable {
void Eat();
}
public class Human : IWorkable, IEatable {
public void Work() { }
public void Eat() { }
}
public class Robot : IWorkable {
public void Work() { }
}
Dependency Inversion Principle
- High-level modules should not depend on low-level modules
- They should depend on abstractions
// Without DIP
public class EmailService {
public void SendEmail(string message) { }
}
public class Notification {
private EmailService _emailService = new EmailService();
public void Notify(string message) {
_emailService.SendEmail(message);
}
}
// With DIP
public interface IMessageService {
void SendMessage(string message);
}
public class EmailService : IMessageService {
public void SendMessage(string message) { }
}
public class Notification(IMessageService messageService) {
private readonly IMessageService _messageService = messageService;
public void Notify(string message) {
_messageService.SendMessage(message);
}
}