37일차 - Node.js 심화(13) 객체 지향 설계 5원칙

이상민·2024년 10월 2일

TIL

목록 보기
37/50

객체 지향 설계 5원칙 (SOLID)

객체 지향 프로그래밍 및 설계의 다섯 가지 핵심 원칙을 SOLID라고 부르고 있다.

  • SOLID는 객체 지향 프로그래밍 및 설계의 다섯 가지 기본 원칙의 맨 앞단어를 하나씩 가져와 만든 것으로, SOLID 원칙에 따르면 프로그래머는 시간이 지나도 유지 보수와 확장이 쉬운 시스템을 구축할 수 있다.

SOLID의 종류

  • 단일 책임의 원칙 (Single Responsibility Principle, SRP)
  • 개방-폐쇄 원칙 (Open-Closed Principle, OCP)
  • 리스코프 치환 원칙 (Liskov substitution principle, LSP)
  • 인터페이스 분리 원칙 (Interface segregation principle, ISP)
  • 의존성 역전 원칙 (Dependency Inversion Principle, DIP)

🎯 단일 책임의 원칙 (Single Responsibility Principle, SRP)

단일 책임의 원칙이란, 하나의 객체는 단 하나의 책임을 가져야 한다.
즉, 클래스모듈변경할 이유단 하나 뿐이어야 한다는 원칙이다.
만약 이를 지키지 않으면, 한 책임의 변경에 의해 다른 책임과 관련된 코드에 영향을 미치며 결국 유지보수가 매우 비효율적이게 된다.

여기서 책임이란?

  • SRP에서 이야기하는 책임이란 기능이라고 생각하면 된다. 예를 들어서, 하나의 클래스가 수행할 수 있는 기능(책임)이 여러 개라면, 클래스 내부의 함수끼리 강한 결합을 가질 가능성이 높아지면 코드의 효율 또한 떨어진다. 객체지향 설계의 핵심은 높은 응집도와 낮은 결합도인데 핵심을 지키지 못하는 것이다.
  • 새로운 요구사항, 프로그램 변경에 의해 연쇄적으로 변경될 수가 있는데 이는 유지보수의 비효율을 동반한다. 때문에, 하나의 클래스는 하나의 책임을 가지도록 책임을 분리시켜야 한다.

🎯 개방-폐쇄 원칙 (Open-Closed Principle, OCP)

💡 소프트웨어 엔티티 또는 개체(클래스, 모듈, 함수 등)는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다는 원칙이다.

  • 즉, 소프트웨어 개체의 행위확장될 수 있어야 하지만, 개체를 변경해서는 안된다.
  • 조금 더 쉽게 설명하자면, 기존 코드에 영향을 주지않고 소프트웨어에 새로운 기능이나 구성 요소추가할 수 있어야 한다는 것.
  • 어떤 모듈의 기능을 수정할 때, 해당 모듈을 이용하는 모든 모듈 또한 수정한다면 유지보수가 복잡해진다. 따라서 OCP(개방-폐쇄 원칙)을 적용해서 기존 코드를 변경하지 않아도 기능을 수정, 추가할 수 있게 해야한다.

  • OCP를 지키지 않으면 객체지향 프로그래밍의 장점인 유연성, 재사용성, 유지보수성 등을 활용하지 못하게 된다.

기존의 코드를 변경하지 않고 어떻게 기능을 수정, 추가할 수 있을까?
상속(다형성), 추상화(인터페이스)를 활용하면 된다. 자주 변경하는 부분을 추상화해서 기존 코드를 수정하지 않고 기능을 확장할 수 있도록 해서 유연성을 살릴 수 있다.

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

💡 어플리케이션에서 객체는 프로그램의 동작에 영향을 주지 않으면서, 하위 타입의 객체로 바꿀 수 있어야 한다는 원칙.

  • 즉, ST의 하위 유형이라면, 프로그램의 기능에 변화를 주지 않고서도 T 타입의 객체를 S 객체로 대체할 수 있어야한다.
  • 우리가 부모 클래스(Parents)와 자식 클래스(Child) 를 가지고 있다면, 이 두가지의 클래스의 객체를 서로를 바꾸더라도 해당 프로그램에서 잘못된 결과를 도출하지 않아야하는 원칙이다.

  • 예를 들어서, 자동차 인터페이스가 있다고 하자. 자동차 인터페이스의 엑셀 기능은 자동차가 앞으로 가는 기능을 한다. 그런데 엑셀 기능을 실행했는데 자동차가 뒤로 간다고 생각해보자. 참으로 끔찍한 일이다. 이는 LSP를 위반하는 것이다.

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

💡 클라이언트는 자신이 사용하는 메소드에만 의존해야 한다는 원칙이다.

  • 특정 클라이언트를 위한 인터페이스 여러 개의 범용 인터페이스 한 개보다 낫다.
  • 인터페이스는 해당 인터페이스를 사용하는 클라이언트를 기준으로 잘게 분리되어야 한다.
  • 예를 들어서, '자동차'라는 하나의 범용 인터페이스 보다는 운전, 정비, 타이어 등의 세부적인 인터페이스로 나누는 것이 더 낫다는 것이다. 이렇게 된다면, 타이어를 교체할 때는 타이어 인터페이스만 확인하고 변경하면 된다.
    이렇게 세부적인 인터페이스로 나눈다면 인터페이스가 명확해지고, 대체 가능성이 높아진다.

여기서 인터페이스란?

  • 여기서 설명하는 인터페이스(interface)는 대표적으로 Java, C++ 그리고 Typescript에서 사용하는 문법이다.

각 클라이언트가 필요로 하는 인터페이스를 분리함해서 클라이언트가 사용하지 않는 인터페이스에 변경이 발생해도 다른 인터페이스는 영향을 받지 않도록 만드는 것이 ISP의 핵심이다.

🎯 의존성 역전 원칙 (Dependency Inversion Principle, DIP)

💡 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다는 원칙이다.

  • 프로그래머가 의존 관계를 맺을 때, 변하기 쉬운 구체적인 것 보다는 변하기 어려운 추상적인 것에 의존해야 한다는 것이다.
    즉, 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻이다.
  • 높은 계층의 모듈(도메인)이 저수준의 모듈(하부구조)에 직접 의존해서도 안된다.

0개의 댓글