SOLID 원칙 (솔리드 패턴)
SOLID은 다섯 가지 기본 설계 원칙을 나타냅니다.
이 원칙들은 소프트웨어 디자인을 더 견고하고 유지보수하기 쉽게 만들기 위한 패턴으로 의미에 대해서 알고있으면 좋다.
실제 구현 방식에 대해서는 간략하게 정리한다.
< 단일 책임 원칙(Single Responsibility Principle - SRP) >
클래스는 단 하나의 책임만을 가져야 합니다. 여러가지 책임이 생기게되면 더 많은 기능이 추가되는 경우에는 해당 클래스를 확장해서 더 넓은 의미의 클래스로 재작성 및 리팩토링이 필요하다. (독립성)
< 개방-폐쇄 원칙(Open-Closed Principle - OCP) >
소프트웨어 엔터티(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
즉, 새로운 기능이 추가되면 기존 코드를 수정하지 않고도 확장이 가능해야 한다는 원칙이다.
예시 코드는 다음과 같다.
// 기존의 PaymentProcessor 클래스
public class PaymentProcessor
{
public void ProcessPayment(decimal amount)
{
// 결제 처리 로직
Console.WriteLine($"Payment processed for amount: {amount}");
}
}
// 새로운 기능을 추가할 확장 클래스
public class NewPaymentProcessor : PaymentProcessor
{
public void ProcessRefund(decimal amount)
{
// 환불 처리 로직
Console.WriteLine($"Refund processed for amount: {amount}");
}
}
// 클라이언트 코드
class Program
{
static void Main()
{
PaymentProcessor paymentProcessor = new PaymentProcessor();
paymentProcessor.ProcessPayment(100.0m);
// 새로운 기능을 추가하더라도 기존 코드 수정 없이 사용 가능
NewPaymentProcessor newPaymentProcessor = new NewPaymentProcessor();
newPaymentProcessor.ProcessPayment(50.0m);
newPaymentProcessor.ProcessRefund(20.0m);
}
}
< 리스코프 치환 원칙(Liskov Substitution Principle - LSP) >
파생 클래스는 기본 클래스로 대체할 수 있어야 한다. 어떤 클래스의 인스턴스가 있다면 그것을 해당 클래스의 하위 클래스의 인스턴스로 대체해도 프로그램은 올바르게 동작해야 한다.
예시 코드는 다음과 같다.
// 기본 클래스
public class Bird
{
public virtual void Fly()
{
Console.WriteLine("Flying...");
}
}
// 파생 클래스
public class Sparrow : Bird
{
// 기본 클래스의 메서드를 오버라이드
public override void Fly()
{
Console.WriteLine("Sparrow flying...");
}
// 새로운 메서드 추가
public void Chirp()
{
Console.WriteLine("Chirp chirp!");
}
}
// 클라이언트 코드
class Program
{
static void MakeBirdFly(Bird bird)
{
bird.Fly();
}
static void Main()
{
Bird bird = new Sparrow(); // 파생 클래스 인스턴스를 기본 클래스로 대체
MakeBirdFly(bird); // LSP를 준수하여 Sparrow의 Fly 메서드가 호출됨
Sparrow sparrow = new Sparrow();
sparrow.Chirp(); // 파생 클래스의 추가된 메서드도 정상적으로 호출 가능
}
}
<인터페이스 분리 원칙(Interface Segregation Principle - ISP) >
클라이언트는 자신이 사용하지 않는 메서드에 의존하면 안 된다.
어떤 클래스도 자신이 사용하지 않는 인터페이스의 메서드에 의존해서는 안 된다는 원칙이다.
예시 코드는 다음과 같다.
// 인터페이스
public interface IFlyable
{
void Fly();
}
public interface IChirpable
{
void Chirp();
}
// 구현 클래스
public class Sparrow : IFlyable, IChirpable
{
public void Fly()
{
Console.WriteLine("Sparrow flying...");
}
public void Chirp()
{
Console.WriteLine("Chirp chirp!");
}
}
// 클라이언트 코드
class Program
{
static void MakeAnimalFly(IFlyable animal)
{
animal.Fly();
}
static void MakeAnimalChirp(IChirpable animal)
{
animal.Chirp();
}
static void Main()
{
Sparrow sparrow = new Sparrow();
MakeAnimalFly(sparrow);
MakeAnimalChirp(sparrow);
}
}
<의존성 역전 원칙(Dependency Inversion Principle - DIP)>
고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 모두 추상화에 의존해야 한다.
추상화된 것에 의존하도록 하는 것이 중요하다는 원칙이다.
예시 코드는 다음과 같다.
// 고수준 모듈 (추상화에 의존)
public interface IWorker
{
void Work();
}
// 저수준 모듈 (추상화에 의존)
public class Worker : IWorker
{
public void Work()
{
Console.WriteLine("Working...");
}
}
// 고수준 모듈이 의존하는 저수준 모듈
public class Manager
{
private IWorker _worker;
// 의존성 주입
public Manager(IWorker worker)
{
_worker = worker;
}
public void Manage()
{
_worker.Work();
}
}
// 클라이언트 코드
class Program
{
static void Main()
{
IWorker worker = new Worker(); // 저수준 모듈의 인스턴스 생성
Manager manager = new Manager(worker); // 고수준 모듈이 의존성 주입을 통해 사용
manager.Manage(); // 고수준 모듈이 저수준 모듈을 사용
}
}
강제성이 있지는 않지만 코드를 작성하는 과정에서 SOLID 원칙에 의거해서 작성을 하게되면 재사용성과 독립성 그리고 확장성이 잘 고려된 코드로 작업의 효율성이 올라가고 휴먼이슈, 사이드 이펙트도 줄어들수 있다.
★★★★☆
'개발 > 기본) 알고리즘' 카테고리의 다른 글
알고리즘) Given When Then Pattern ( 테스트 케이스 작성 기법 ) (0) | 2024.01.31 |
---|---|
알고리즘) WayPoint Algorithm(길찾기 알고리즘) (0) | 2023.11.21 |
알고리즘) 매치 메이킹 (Matching Algorithms) (0) | 2023.09.04 |
알고리즘) 게일- 섀플리 알고리즘(Gale-Shapley Algorithm) (1) | 2023.07.31 |
알고리즘) Procedural Dungeon Generation Algorithm(절차적 던전 생성) (0) | 2021.12.30 |
댓글