ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링핵심원리-기본편] 좋은 객체 지향 설계의 5가지 원칙 (SOLID)
    히딘쓰의 책상/강의 2023. 8. 24. 15:35

     

     

    인강 들으며, 복습을 위한 정리🎉

     

     

    클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙을 정리

    💡
    SRP
    : 단일 책임 원칙 (Single responsibility principle)
    OCP : 개방-폐쇄 원칙 (Open/Closed principle)
    LSP : 리스코프 치환 원칙 (Liskov substitution principle)
    ISP : 인터페이스 분리 원칙 (Interface segregation principle)
    DIP : 의존관계 역전 원칙 (Dependency inversion principle)

     

     

     

    SRP - 단일 책임 원칙 

    • 💡 한 클래스는 하나의 책임만 가져야 한다
    • 하나의 책임이라는 것은 모호하긴 함. 클 수도 있고 작을 수도 있음 문맥과 상황에 따라 다름!
    • 이 부분에서 경험이 필요한 거임 → 중요한 판단의 기준은 변경!
    • ex) ui 하나 변경하는 데 sql 코드부터 애플리케이션 다 고쳐야 하면 잘못 설계 한 것
    • 객체의 생성과 사용을 분리
    • 계층이 다 나뉘어 있는 것은 단일 책임 원칙을 잘 지키려는 것이라고 보면 됨!
    • 책임의 범위를 적절하게 잘 조절하는 게 객체지향 설계의 묘미!
    • 변경이 있을 때, 딱 하나의 클래스 딱 하나의 지점만 고치면(파급 효과가 적으면) 단일 책임 원칙을 잘 따른 것

     

     

    OCP - 개방-폐쇄 원칙

    • 가장 중요한 원칙!💡
    • 💡 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 함
      • -> 코드의 변경 없이 기능 추가 
    • 다형성을 활용해보자!
      • 자동차 예시에서 k3 -> 테슬라 변경해도 운전자 안 바뀜
      • 연극 예시에서 여배우 못하면 무명 배우를 구해와도 되고!
    • 인터페이스를 구현한 새로운 클래스를 하나 만들어서 (이건 기존 코드를 변경하는 게 아님) 새로운 기능을 구현

     

    이전 강의의 예시에서 MemberService 클라이언트가 구현 객체를 직접 선택하고 있었음. 그래서 변경할 때 클라이언트의 코드를 수정해야 했음

    다음은 MemberService의 코드 일부

    MemberRepository m = new MemoryMemberRepository(); //기존 코드
    
    MemberRepository m = new JdbcMemberRepository(); //변경 코드

    분명 다형성을 사용했지만 OCP 원칙을 지킬 수 없었음

    이 문제의 해결은?🙄

    객체를 생성하고 연관 관계를 맺어주는 별도의 조립, 설정자가 필요함!

    별도의 뭔가가 필요함 -> 이게 바로 스프링 컨테이너!🎉

     

     

     

     

    LSP - 리스코프 치환 원칙

    • 💡 프로그램의 객체는 프로그램의 정확성을 깨트리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 함
    • -> 간단하게 인터페이스, 그에 대한 구현체가 있음. 그럼 구현체가 구현하면 됨
    •  ex) 자동차 예시에서 엑셀 밟으면 앞으로 가야하는 기능이 있음. 그런데 엑셀 밟았을 때 뒤로 가는 차를 만들 수도 있음. 그렇게 해도 컴파일은 됨. 오류는 안남.
    • 하지만 리스코프 치환 원칙은 컴파일 단계를 말하는 것이 아님
    • 인터페이스 규약에 엑셀은 무조건 앞으로 가야해 라는 규약이 있음
    • 그렇다면 이 규약을 무조건 맞춰야 하는 것! -> 기능적으로 보장을 해줘야 함
    • 엑셀을 밟으면 뒤로 가도록 만드는 것은 리스코프 치환 원칙을 위배하는 것
    • 느리더라도 앞으로 가면 리스코프 치환 원칙을 맞추는 것!

     

     

     

    ISP - 인터페이스 분리 원칙

    • 💡 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 나음.
    • ex) 자동차라는 인터페이스 안에 모든 것을 넣으면 너무 크니까 운전 관련 인터페이스, 정비 관련 인터페이스 등으로 분리하는 것
    • -> 그러면 사용자 클라이언트도 운전자 클라이언트정비사 클라이언트로 분리할 수가 있음
    • 정비와 관련된 기능을 바꿔야 하면 정비, 정비사 관련 인터페이스만 변경하고, 운전자, 운전 인터페이스는 건드리지 않아도 됨
    • 기능을 딱 맞게 적당한 크기로 쪼개는 것💡이 중요함
    • 이렇게 분리하면 정비 인터페이스 자체가 변해도 운전자 인터페이스에는 영향을 주지 않음
    • 인터페이스가 명확해지고, 대체 가능성이 높아짐!
    • 스프링 프레임워크 코드를 열어보면 엄청 철저하게 분리되어 있음

     

     

     

    DIP - 의존 관계 역전 원칙

    • 이것도 중요한 원칙! OCP와 DIP 두 가지가 제일 중요!!!💡 서로 연관도 있음
    • 💡 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다." 의존성 주입은 이 원칙을 따르는 방법 중 하나!
    • -> 쉽게 말하면 클라이언트 코드가 구현 클래스를 바라보지 말고 인터페이스만 바라 봐야 한다는 뜻
    • 이전에 말한 역할에 의존하라는 것과 똑같은 이야기
    • 연극 예시에서 원빈이 김태희랑만 연습을 해서 다른 배우가 아니면 못하거나 하면 힘듬. 대체 가능성이 사라져 버림!
    • 그래서!!! 역할과 구현을 철저하게 분리하도록 시스템도 그렇게 설계해야 함. 시스템도 언제든 갈아낄 수 있도록!
    • 이것이 가능하려면 역할에 의존해야함. 구현에 의존하면 절대 안됨.💡
    • 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있음. 구현체에 의존해버리면 변경이 어려워짐
    • 기존 예시의 MemberServie가 MemberRepository 인터페이스만 바라보고 구현체에 대해서는 몰라야 함
    • 하지만 MemberService는 MemberRepository 인터페이스에 의존하고, 구현체에도 의존을 하고 있음.
    • 그래서 MemmoryMemberRepository를 변경할 때 클라이언트 코드를 직접 변경 해야 하는 것
      • MemberRepository m = new MemmoryMemberRepository();
      • -> 추상화에 의존해야지 구체화에 의존하면 안됨!!!!!
      • => DIP 위반

     

     

     

     

    그럼 어떡해야 하지?????????????????

     

    정리

    • 객체 지향의 핵심은 다형성
    • 다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발할 수 없음
    • 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경 됨!
    • 다형성 만으로는 OCP, DIP를 지킬 수 없음 
    • 💡 그래서 뭔가가 더 필요함!!!!!

     

    객체 지향을 제대로 공부하는 개발자라면 누구나 여기까지 옴

    다형성 만으로는 클라이언트 코드의 변경을 막을 수가 없음.. 무언가 더 필요해!

    스프링 프레임워크를 만든 개발자들도 이런 고민을 했을 것!

     

     

    -> 객체 지향 설계와 스프링으로!

Designed by Tistory.