SOLID
SOLID 원칙 짧은 소개
Single Responsibility Principle (단일 책임 원칙)
각 클래스는 변경될 이유가 하나만 있어야 한다. 즉 하나의 일, 하나의 책임만 가져야 한다. 그래야 클래스가 집중되고 유지보수하기 쉬워진다.
// 나쁜 예 - 책임이 여러 개
public class Customer
{
public void SaveToDatabase()
{
// database logic
}
public void GenerateReport()
{
// report logic
}
public void CalculateMetrics()
{
// metrics logic
}
}
// 좋은 예 - 단일 책임
public class Customer
{
public string Name { get; set; }
public string Email { get; set; }
}
public class CustomerRepository
{
public void Save(Customer customer)
{
// database logic
}
}
public class CustomerReportGenerator
{
public void Generate(Customer customer)
{
// report logic
}
}
Open/Closed Principle (개방-폐쇄 원칙)
소프트웨어 요소는 확장에는 열려 있고 변경에는 닫혀 있어야 한다. 기존 코드를 고치지 않고 새 기능을 추가할 수 있어야 한다.
// 나쁜 예
public class PaymentProcessor
{
public void ProcessPayment(string paymentType)
{
if (paymentType == "Credit")
{
// process credit payment
}
else if (paymentType == "Debit")
{
// process debit payment
}
// 새 결제 수단을 추가하려면 이 클래스를 고쳐야 한다
}
}
// 좋은 예
public interface IPaymentMethod
{
void Process();
}
public class CreditPayment : IPaymentMethod
{
public void Process()
{
// process credit payment
}
}
public class DebitPayment : IPaymentMethod
{
public void Process()
{
// process debit payment
}
}
Liskov Substitution Principle (리스코프 치환 원칙)
상위 클래스의 객체는 하위 클래스의 객체로 바꿔도 애플리케이션이 깨지지 않아야 한다. 하위 클래스는 기반 클래스의 동작을 바꾸는 게 아니라 확장해야 한다.
// 나쁜 예
public class Bird
{
public virtual void Fly()
{
// flying implementation
}
}
public class Penguin : Bird
{
public override void Fly()
{
throw new NotSupportedException("Penguins can't fly!");
}
}
// 좋은 예
public abstract class Bird
{
public abstract void Move();
}
public class FlyingBird : Bird
{
public override void Move()
{
// flying implementation
}
}
public class SwimmingBird : Bird
{
public override void Move()
{
// swimming implementation
}
}
Interface Segregation Principle (인터페이스 분리 원칙)
클라이언트가 쓰지도 않는 인터페이스에 의존하도록 강요받아서는 안 된다. 범용 인터페이스 하나보다 구체적인 인터페이스 여러 개가 낫다.
// 나쁜 예
public interface IWorker
{
void Work();
void Eat();
void Sleep();
}
// 좋은 예
public interface IWorkable
{
void Work();
}
public interface IEatable
{
void Eat();
}
public class Human : IWorkable, IEatable
{
public void Work()
{
// work implementation
}
public void Eat()
{
// eat implementation
}
}
Dependency Inversion Principle (의존성 역전 원칙)
고수준 모듈이 저수준 모듈에 의존해서는 안 된다. 둘 다 추상에 의존해야 한다. 추상이 세부사항에 의존해서는 안 되고, 세부사항이 추상에 의존해야 한다.
// 나쁜 예
public class EmailSender
{
public void SendEmail(string message)
{
// send email implementation
}
}
public class NotificationService
{
private readonly EmailSender _emailSender;
public NotificationService()
{
_emailSender = new EmailSender(); // 직접 의존
}
}
// 좋은 예
public interface IMessageSender
{
void Send(string message);
}
public class EmailSender : IMessageSender
{
public void Send(string message)
{
// send email implementation
}
}
public class NotificationService
{
private readonly IMessageSender _sender;
public NotificationService(IMessageSender sender) // 의존성 주입
{
_sender = sender;
}
}
이 원칙들을 따르면 코드가 이렇게 된다:
- 유지보수와 테스트가 쉬워진다
- 변화에 더 유연하게 대응한다
- 재사용성과 모듈성이 높아진다
- 수정할 때 버그가 덜 생긴다
핵심은 균형을 찾는 것이다. 이 원칙들은 가치 있는 지침이지만, 단순한 문제에까지 너무 경직되게 적용해 코드를 과도하게 복잡하게 만들어서는 안 된다.