[TIL 48일차] SOLID 정리

이건·2024년 8월 6일

SOLID

클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙을 정리
• SRP: 단일 책임 원칙(single responsibility principle)
• OCP: 개방-폐쇄 원칙 (Open/closed principle)
• LSP: 리스코프 치환 원칙 (Liskov substitution principle)
• ISP: 인터페이스 분리 원칙 (Interface segregation principle)
• DIP: 의존관계 역전 원칙 (Dependency inversion principle)

SRP: 단일 책임 원칙(single responsibility principle)

단일 책임 원칙(Single Responsibility Principle, SRP)은 객체 지향 설계 원칙 중 하나로, 각 클래스는 오직 하나의 책임만 가져야 한다는 내용을 담고 있다. 이 원칙은 다음과 같은 주요 요소를 포함한다:

  1. 책임의 정의: 하나의 책임이란 클래스가 수행해야 할 작업이나 기능을 의미하며, 이는 명확해야 한다. 하지만 이 책임이 무엇인지에 대한 정의는 상황이나 문맥에 따라 다를 수 있다.

  2. 변경의 기준: SRP의 중요한 기준 중 하나는 변경이다. 만약 클래스의 책임이 변경될 때, 그 변화가 다른 클래스에 미치는 영향이 적다면 SRP를 잘 따른 것이라고 할 수 있다. 즉, 단일 책임 원칙을 따르면 코드의 유지보수성이 높아진다.

  3. 예시: UI 변경을 고려할 때, 사용자 인터페이스와 비즈니스 로직을 분리하는 것이 좋은 예시다. 하나의 클래스가 UI와 비즈니스 로직을 모두 담당한다면, UI가 변경될 때마다 비즈니스 로직도 영향을 받을 수 있다. 따라서 이를 분리함으로써 각 클래스의 책임을 명확히 하고 변경의 파급 효과를 줄일 수 있다.

결론적으로, SRP를 따르는 설계는 코드의 가독성과 유지보수성을 향상시키며, 변경에 대한 저항력을 높이는 데 기여한다.

OCP: 개방-폐쇄 원칙 (Open/closed principle)

개방-폐쇄 원칙(Open/Closed Principle, OCP)은 소프트웨어 설계의 원칙 중 하나로, 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다는 내용을 담고 있다. 이 원칙은 다음과 같은 핵심 요소로 설명될 수 있다:

  1. 확장과 변경: OCP의 핵심은 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있어야 한다는 것이다. 처음에는 이 원칙이 다소 모순적으로 느껴질 수 있지만, 이는 다형성을 활용하여 해결할 수 있다. 즉, 기존 클래스를 수정하지 않고도 새로운 기능을 추가할 수 있는 구조를 만들어야 한다.

  2. 다형성 활용: 이를 위해 인터페이스나 추상 클래스를 정의하고, 이를 구현한 새로운 클래스를 만들어 기능을 추가하는 방식이 효과적이다. 이렇게 하면 기존 코드에 영향을 주지 않고도 새로운 기능을 쉽게 추가할 수 있다.

  3. 역할과 구현의 분리: OCP를 잘 따르기 위해서는 역할과 구현을 분리하는 것이 중요하다. 인터페이스를 통해 역할을 정의하고, 구체적인 구현은 별도의 클래스로 나누어 관리함으로써, 필요할 때마다 새로운 기능을 추가할 수 있다. 이는 코드의 유연성을 높이고, 유지보수를 용이하게 한다.

결론적으로, OCP를 준수하는 설계는 코드의 확장성을 높이면서도 기존 코드의 안정성을 유지하게 해준다. 이러한 접근 방식은 소프트웨어의 발전과 변화에 잘 대응할 수 있도록 돕는다.

LSP: 리스코프 치환 원칙 (Liskov substitution principle)

리스코프 치환 원칙(Liskov Substitution Principle, LSP)은 객체 지향 설계의 중요한 원칙 중 하나로, 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다는 내용을 담고 있다. 이 원칙은 다음과 같은 핵심 요소로 설명될 수 있다:

  1. 하위 타입의 대체 가능성: LSP는 하위 클래스가 상위 클래스의 인스턴스처럼 사용될 수 있어야 한다는 것을 의미한다. 즉, 상위 클래스의 기능을 사용할 때 하위 클래스의 인스턴스를 사용해도 프로그램의 동작이 올바르게 유지되어야 한다.

  2. 인터페이스 규약 준수: 하위 클래스는 상위 클래스가 정의한 인터페이스의 규약을 모두 지켜야 한다. 이는 다형성을 지원하기 위한 중요한 원칙으로, 하위 클래스가 상위 클래스의 기대를 어기면 프로그램의 안정성이 저하될 수 있다.

  3. 컴파일 이상의 의미: 단순히 컴파일이 성공하는 것을 넘어, 실제로 하위 클래스의 인스턴스가 상위 클래스의 기능을 올바르게 수행해야 한다. 예를 들어, 자동차 인터페이스에서 엑셀 기능이 '앞으로 가기'로 정의되어 있다면, 이를 '뒤로 가기'로 구현하는 것은 LSP를 위반하는 것이다. 하위 클래스는 상위 클래스의 기대를 충족시켜야 하며, 기대와 다르게 동작해서는 안 된다.

결론적으로, LSP를 준수하는 설계는 코드의 신뢰성을 높이고, 다형성의 이점을 극대화하는 데 기여한다. 이를 통해 객체 간의 관계를 명확히 하고, 유지보수 및 확장성을 용이하게 만든다.

ISP: 인터페이스 분리 원칙 (Interface segregation principle)

인터페이스 분리 원칙(Interface Segregation Principle, ISP)은 소프트웨어 설계의 중요한 원칙 중 하나로, 특정 클라이언트를 위한 여러 개의 인터페이스가 범용 인터페이스 하나보다 낫다는 내용을 담고 있다. 이 원칙은 다음과 같은 주요 요소로 설명될 수 있다:

  1. 인터페이스의 분리: ISP는 하나의 큰 인터페이스 대신, 여러 개의 작은 인터페이스를 정의하는 것을 지향한다. 예를 들어, 자동차와 관련된 기능을 가진 인터페이스를 '운전 인터페이스'와 '정비 인터페이스'로 분리하면, 각 클라이언트가 필요로 하는 기능만을 구현할 수 있게 된다.

  2. 클라이언트의 특화: 사용자 클라이언트를 '운전자 클라이언트'와 '정비사 클라이언트'로 분리함으로써, 각 클라이언트가 필요한 기능에만 집중할 수 있다. 이렇게 하면 클라이언트는 자신이 필요로 하지 않는 메서드에 의존하지 않게 되어, 불필요한 복잡성을 줄일 수 있다.

  3. 변경의 독립성: 인터페이스가 분리되면 한 인터페이스의 변경이 다른 인터페이스나 클라이언트에 영향을 주지 않게 된다. 예를 들어, 정비 인터페이스가 변경되더라도 운전자 클라이언트는 영향을 받지 않으므로, 프로그램의 안정성과 유연성이 향상된다.

  4. 명확성과 대체 가능성: 인터페이스가 명확해지면 각 기능에 대한 이해도가 높아지고, 대체 가능성이 증가한다. 이는 코드의 유지보수성과 확장성을 높이는 데 기여한다.

결론적으로, ISP를 준수하는 설계는 코드의 가독성과 유연성을 높이며, 각 클라이언트의 요구사항을 더 잘 충족할 수 있도록 해준다. 이를 통해 소프트웨어의 품질을 향상시키고, 변경에 대한 저항력을 높이는 데 기여한다.

DIP: 의존관계 역전 원칙 (Dependency inversion principle)

의존관계 역전 원칙(Dependency Inversion Principle, DIP)은 객체 지향 설계의 중요한 원칙 중 하나로, 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안 된다"는 내용을 담고 있다. 이 원칙은 다음과 같은 핵심 요소로 설명될 수 있다:

  1. 추상화에 의존: DIP는 구체적인 구현 클래스가 아닌, 인터페이스나 추상 클래스와 같은 추상화에 의존하도록 설계해야 한다는 것이다. 이는 코드의 유연성을 높이고, 변경에 대한 저항력을 강화하는 데 중요한 역할을 한다.

  2. 의존성 주입: 의존성 주입은 DIP를 따르는 방법 중 하나로, 필요한 의존성을 외부에서 주입함으로써 객체가 구체적인 구현에 의존하지 않도록 한다. 이를 통해 코드의 결합도를 낮추고, 테스트 용이성을 높일 수 있다.

  3. 역할에 의존: 앞서 이야기한 역할(Role)에 의존하게 해야 한다는 점과 관련이 있다. 클라이언트는 인터페이스에 의존함으로써, 구현체를 쉽게 변경할 수 있게 다. 만약 클라이언트가 구체적인 구현체에 의존하게 되면, 이후의 변경이 매우 어려워지고, 코드의 유지보수성이 떨어진다.

  4. 유연한 구현 변경: DIP를 준수하면, 새로운 기능 추가나 변경 시 기존 코드를 최소한으로 수정하면서도 쉽게 새로운 구현체로 교체할 수 있다. 이는 시스템의 확장성과 유연성을 크게 향상시킨다.

결론적으로, DIP를 준수하는 설계는 코드의 재사용성과 유지보수성을 높이며, 시스템의 구조를 더 견고하게 만들어 준다. 이를 통해 개발자는 변경에 대한 부담을 줄이고, 더 나은 소프트웨어 품질을 유지할 수 있다.

0개의 댓글