kyucumber
전체 글 보기

객체지향과 SOLID

연차가 아직 많지 않은 편이라 그런지 개발을 하다보면 항상 객체지향의 5대 원칙인 SOLID에 대한 이야기가 자주 나옵니다. 코드리뷰를 진행하면서 SOLID와 관련해 설계에 대해 질문을 던지는 사람들도 있었고 각각의 원칙들이 의미하는 바를 물어보는 사람도 많았습니다.

대략적인 내용은 파악하고 있으나 문서상으로 정리하지 않아 매번 새로 찾아보는 편입니다. 이번 기회에 관련 내용을 정리해보기로 했습니다.

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)

하나의 클래스는 하나의 책임만을 가지며, 클래스를 변경해야 하는 이유 또한 한가지만 가져야 한다는 원칙입니다.

이 원칙의 경우 의미가 원칙에 그대로 들어가 있어 이해하기 쉬웠습니다. 다만 적절하게 클래스 간 책임을 나누고 여러 클래스간 관계를 잘 설정하는 부분은 쉬운 작업은 아닐 것 같습니다.

클래스간 책임을 적절히 잘 분리하여 응집도를 높이는 부분이 여기에 해당하며 설계를 잘 하기 위한 가장 기본적인 방법이 될 것 같습니다.

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

클래스는 확장에는 열려있고 변경에는 닫혀있어야 한다는 원칙입니다. 요구사항이 추가되어 새로운 기능을 추가한다고 했을 때, 쉽게 확장할 수 있어야 하며 기존 구현한 코드의 변경을 최소화해야 한다는 내용인 것 같습니다.

어떤 경우에 개방 폐쇄 원칙이 적용되었다고 볼 수 있을까요? 특정 기능의 추상화를 통해 인터페이스를 추출하고 인터페이스 상속을 통해 구현체를 늘려나가는 과정이 개방 폐쇄 원칙을 지키는 사례에 해당할 수 있을 것 같네요.

리스코프 치환 원칙 - LSP(Liskov Substitution Principle)

이 원칙의 경우 원칙의 이름만 보았을 때 어떤 내용인지 이해하기 힘들어 다섯가지의 원칙 중 가장 이해하기 어려웠던 것 같습니다.

상위 타입의 객체를 하위 타입의 객체로 치환하더라도 정상 동작을 보장해야 한다는 내용의 원칙입니다.

조금 풀어서 내용을 정리해보면 구현 상속(extends) 혹은 인터페이스 상속(implements)을 통해 서브타입을 구현하더라도 상위 타입이 가지는 제약이나 내용들을 보장해야 한다라는 내용 같습니다.

어느 경우가 이에 해당할까 생각해봤는데, 흔히 사용하는 Collection이나 List 등의 인터페이스가 이 원칙을 잘 지켜서 개발된 것으로 볼 수 있을 것 같습니다.

인터페이스 분리 원칙 - ISP(Interface Segregation Principle)

이 원칙은 인터페이스를 구현하는 서브 클래스가 자신이 사용할 필요가 없는 메소드를 구현하지 말아야 한다는 원칙입니다.

추상화를 통해 인터페이스를 추출했을 때 가지는 메소드가 3개라면, 서브타입은 모든 메소드를 유의미하게 구현하고 사용해야 합니다. 만약에 사용하지 않아 구현할 필요가 없는 메소드가 존재한다면 이 부분을 다른 인터페이스로 추출해야 합니다.

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

DIP(Dependency Inversion Principle)의 경우 DDD Start와 같은 책을 통해 많이 알려지면서 SOLID를 이야기하지 않아도 자주 언급되는 용어가 된 것 같습니다.

이는 상위 수준의 모듈이 하위 수준의 모듈의 구현에 의존하여 하위 수준 모듈에 변경에 상위 수준의 모듈이 영향을 받는 부분을 최소화하기 위한 원칙입니다. 상위 수준 모듈에서 추상화를 통해 추출한 인터페이스를 의존하게 하면 하위 수준 모듈과의 의존성을 끊어낼 수 있습니다.

Reference

https://johngrib.github.io/wiki/SOLID/