Post

SOLID

SOLID

Readable Code: 읽기 좋은 코드를 작성하는 사고법를 듣고 요약한 내용입니다.

객체 설계하기

객체로 추상화하기

  • 비공개 필드(데이터), 비공개 로직(코드)
  • 공개 메서드 선언부를 통해 외부 세계와 소통
    • 각 메서드의 기능은 객체의 책임을 드러내는 창구이다.
  • 객체의 책임이 나뉨에 따라 객체간 협력이 발생

객체가 제공하는 것

  • 절차 지향에서 잘 보이지 않았던 개념을 가시화
  • 관심사가 한 군데로 모이기 때문에, 유지보수성이 올라간다.
    • ex) 객체 내부에서 객체가 가진 데이터의 유효성 검증 책임을 가질 수 있다.
  • 여러 객체를 사용하는 입장에서는, 구체적인 구현에 신경 쓰지 않고 보다 높은 추상화 레벨에서 도메인 로직을 다룰 수 있다.

새로운 객체를 만들 때 주의할 점

  • 1개의 관심사로 명확하게 책임이 정의되었는지 확인하기
    • 메서드를 추상화 할 때와 유사.
    • 객체를 만듦으로써 외부 세계와 어떤 소통을 하려고 하는지 생각해야함.
  • 생성자, 정적 팩토리 메서드에서 유효성 검증이 가능
    • 도메인에 특화된 검증 로직이 들어갈 수 있다.
    • 밖에서 도메인을 사용하는 입장에서는 유효성에 대해 신경 쓸 필요 없다.
  • setter 사용 자제
    • 데이터는 불변이 최고다.(변하는 데이터는 사이드 이펙트가 존재) 데이터가 변하더라도 객체가 핸들링할 수 있어야 한다.
    • 객체 내부에서 외부 세계의 개입 없이 자체적인 변경/ 가공으로 처리할 수 있는지 확인해야함.
    • 외부에서 가지고 있는 데이터로 변경해야하는 경우, set~ 보다는 update~같이 의도를 드러내야 한다.
  • getter 사용 자제
    • 외부에서 객체 내 데이터가 필요하다고 판단될 때 사용하자.
    • BAD : person.get지갑().get신분증().findAge() ≥ 19
    • GOOD : person.isAgeGreaterThanOrEqualTo(19)
    • getter 가 남발되면 캡슐화가 무의미해진다.
  • 필드의 수는 적을수록 좋다.
    • 불필요한 데이터가 많을수록 복잡도가 높아지고 대응할 변화가 많아진다.
    • 필드 A를 가지고 계산할 수 있는 A필드가 있다면, 메서드 기능으로 제공
    • 단, 미리 가공하는 것이 성능 상 이점이 있다면, 필드로 가지고 있는 것이 좋을 수도 있다.
      • 상황에 맞게 잘… 어렵군…

SRP: Single Responsibility Principle

  • 하나의 클래스는 단 한 가지의 변경 이유(책임)만을 가져야 한다.
  • 객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경되는가?
  • 관심사의 분리
  • 높은 응집도
    • 응집도란 클래스나 모듈내의 요소들이 서로 긴밀하게 연관되어 있는 정도
  • 낮은 결합도
    • 결합도란 두 개 이상의 객체가 협력 관계에 있을 때, 한 객체가 다른 객체가 영향받는 정도
  • 책임을 발견하는 것이 어렵다. 경험의 영역

OCP: Open-Closed Principle

  • 확장에는 열려있고, 수정에는 닫혀야 한다.
    • 기존 코드의 변경 없이, 시스템의 기능을 확장할 수 있어야 한다.
  • 추상화와 다형성을 활용하여 OCP를 지킬 수 있다.

LSP: Liskov Substitution Principle

  • 상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.
    • 자식 클래스는 부모 클래스의 책임을 준수하며, 부모 클래스의 행동을 변경하지 않아야 한다.
  • LSP를 위반하면, 상속 클래스를 사용할 때 오동작, 예상 밖의 예외가 발생하거나, 이를 방지하기 위한 불필요한 타입 체크가 동반될 수 있다.

ISP: Interface Segregation Principle

  • 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.
    • 인터페이스를 쪼개야한다.
  • ISP를 위반하면, 불필요한 의존성으로 인해 결합도가 높아지고, 특정 기능의 변경이 여러 클래스에 영향을 미칠 수 있다.

DIP: Dependency Inversion Principle

  • 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다. 둘 모두 추상화에 의존해야 한다.
  • 의존성이란? 어떤 한 모듈이 다른 모듈을 참조하는 것
  • 의존성의 순방향: 고수준 모듈이 저수준 모듈을 참조하는 것
  • 의존성의 역방향: 고수준, 저수준 모듈이 모두 추상화에 의존하는 것
  • 저수준 모듈이 변경되어도, 고수준 모듈에는 영향이 가지 않는다.
This post is licensed under CC BY 4.0 by the author.