[SOLID] 좋은 객체 지향 설계의 5가지 원칙

hoonie·2023년 1월 11일
0

앞으로의 글들은 인프런 김영한님의 강의 '스프링 핵심 원리 - 기본편'의 강의 내용을 참고하여 작성한 글입니다.


SOLID: 로버트 마틴이 정리한 좋은 객체 지향 설계의 5가지 원칙

  1. SRP(single responsibility principle): 단일 책임 원칙
  2. OCP(open-closed principle): 개방-폐쇄 원칙
  3. LSP(Liskov substition principle): 리스코프 치환 원칙
  4. ISP(interface segregation principle): 인터페이스 분리 원칙
  5. DIP(dependency inversion principle): 의존관계 역전 원칙

1. SRP 단일 책임 원칙

  • 한 클랙스는 하나의 책임만 가져야 한다.
  • 이때 하나의 책임이라는 것이 모호할 수 있다.
    -> 따라서 중요한 기준은 변경이다. 즉 코드의 수정이 있는 경우에 그 수정에 따른 파급효과가 적으면 단일 책임 원칙을 잘 따른 것이라고 할 수 있다.

2. OCP 개방-폐쇄 원칙

  • 소프트웨어 요소는 확장에는 열려 있으나, 변경에는 닫혀 있어야한다.
  • 생각을 좀 해보면 확장을 할려면 기존 코드를 변경해야 하는데 변경에 닫혀 있다는 것은 이해하기 어려울 수도 있다.
  • 다형성을 활용하여 OCP를 구현을 해보자!
  • 인터페이스를 구현한 새로운 클래스를 만들고 새로운 기능을 추가할 수 있다.(역할과 구현의 분리)
// memory를 사용하는 리포지토리를 생성
public class MemberService {
	private MemberRepository memberRepository = new MemoryMemberRepository();
}

// 리포지토리 변경에 대한 소요가 생겨서 memory가 아닌 Jdbc를 리포지토리로 사용하기 위해 변경
public class MemberService {
	// private MemberRepository memberRepository = new MemoryMemberRepository();
    private MemberRepository memberRepository = new JdbcMemberRepository();
}

위 코드에서 볼 수 있듯이 MemberService 클라이언트가 구현 클래스(MemoryMemberRepository, JdbcMemberRepository)를 직접 선택하는 상황에서 구현 객체를 변경하기 위해 클라이언트 코드를 변경할 수 밖에 없다.
즉 구현 객체를 변경하기 위해 클라이언트 코드를 수정해야하는 상황이 발생하였고 이는 다형성을 활용했지만 OCP원칙을 지킬 수 없는 상황이다.
-> 따라서 객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요함을 알 수 있다.(추후에 설명)

3. LSP 리스코프 치환 원칙

  • 프로그램의 객체는 프로그램의 정확성을 깨드리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
  • 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체는 믿고 사용하려명 이 원칙이 반드시 필요하다.
  • 예) 자동차의 핸들을 오른쪽으로 돌리면 차는 오른쪽으로 가야하는 기능, 여기서 핸들을 오른쪽으로 돌렸는데 차가 왼쪽으로 가면 LSP를 위반한 것. 꺾이는 정도의 차이는 있어도 되지만 반드시 오른쪽으로 가게 설계해야함

4. ISP 인터페이스 분리 원칙

  • 모든 클라이언트를 위한 범용적인 인터페이스 하나보단 특정 클라이언트를 위한 인터페이스 여러 개를 구현하는 것이 낫다.
  • 하나의 자동차 인터페이스보단 운전 인터페이스, 정비 인터페이스 2개로 분리하는 것이 좋음
  • 분리를 하면 정비 인터페이스를 수정하더라도 운전자 클라이언트에 영향을 주지 않음
  • 인터페이스가 명확해지고 대체 가능성이 높아진다.

5. DIP 의존관계 역전 원칙

  • 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다"
  • 의존성 주입은 이 원칙을 따르는 방법 중 하나다.
  • 즉, 구체적인 구현 클래스에 의존하지말고, 인터페이스에 의존해야한다는 의미
  • 역할(role)에 의존해야한다는 것과 같다. 객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다. 구현체에 의존하게 되면 변경이 아주 어려워짐!
  • 하지만 2번(OCP)에서 작성한 코드는 MemberService가 인터페이스에 의존하지만 구현 클래스에도 동시에 의존하는 문제점이 있다. 다시 말해, MemberService 클라이언트가 구현 클래스를 직접 선택한다는 문제점이 있다.
// DIP 위반!!!
MemberRepository = new MemoryMemberRepository();

정리

  • 객체 지향의 핵심은 다형성
  • 다형성만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경되는 문제점이 있다.
  • 따라서 다형성만으로는 OCP, DIP를 지킬 수 없다.
  • 새로운 뭔가가 필요하다!!

[스포] 스프링은 DI(dependency injection)과 DI컨테이너를 활용하여 클라이언트 코드의 변경 없이 기능을 확장할 수 있다. (OCP, DIP를 지키면서 개발을 할 수 있게 해줌)

출처: 김영한님의 강의 '스프링 핵심원리 - 기본편'

profile
사우루스 팡팡!

0개의 댓글