프로그래밍 공부하기

의존성을 이용해 설계 진화시키기 본문

감상문

의존성을 이용해 설계 진화시키기

ihl 2021. 9. 12. 16:16

  개발을 하다보면 코드를 수정하고 리팩토링하는 일을 자연스럽게 겪게 된다. 코드를 리팩토링하는 기준은 다양하겠지만 그 중 대표적인 것은 의존성이다. 의존성에 따라 하나의 코드를 고쳤을 때 다른 코드도 점검하고 수정해야하는 일이 발생할 수 있기 때문이다. 이러한 의존성을 점검하고, 설계를 진화시키는 방법을 알아보자.

 

1. 설계와 의존성

  • 설계에서 사용하는 역할, 책임이란 말은 '의존성을 어떻게 관리할 것인가' 와 일맥 상통한다. 
    따라서 의존성을 관리하는 방법에 따라 설계는 변화한다.
  • 설계란 코드를 어떻게 배치할 것인가를 의미한다.
    • 같이 변경되어야할 확률이 높은 코드는 함께 뭉쳐놓고, 그렇지 않은 코드는 분리한다.
      • 뭉쳐놓는다는 것은 같은 함수-클래스-패키지-프로젝트에 코드를 함께 넣는다는 것이다.
    • 의존성은 변경에 의해 영향을 받을 수 있는 가능성을 의미한다.
      따라서 의존성은 코드 배치에 영향을 준다.

 

2. 클래스간 연관관계

  • Association(연관관계) : A 내부에서 B로 갈 수 있는 영구적인 경로가 있다.
    A클래스가 B클래스의 인스턴스를 멤버 변수로 갖는 경우
  • Dependency(의존관계) : A 내부에서 B로 갈 수 있는 일시적인 경로가 있다.
    A클래스가 파라미터, 리턴으로 B 인스턴스를 받거나, 메소드 내에서 해당 B의 인스턴스를 생성
  • Inheritance(상속관계) : B의 구현이 변경되면 A에 영향을 준다.
    class A extends B
  • Realization(실체화관계) : B의 Operation Signiture가 변경되면 A에 영향을 준다.
    class A implements B
    • B는 직접 구현하지 않으므로 B의 구현이 A에 영향을 주지 않는다. 
    • Operation: UML에서 Visibility( +, -, #, ~). 예를 들어 함수의 private-protect-public 을 의미한다.
    • Signature: UML에서 Signature(operation-name  '('  [ parameter-list ]   ')'   [ ':' return-spec ]). 예를 들어 함수의 이름-파라미터-리턴값으로 이루어진 정의를 의미한다.

 

3. 좋은 의존성의 규칙

  • 한쪽 방향으로만 의존하는 것이 좋다.
    • 양방향은 두 데이터 간의 Sync를 맞추기 어렵고, 성능상의 문제가 발생한다.
    • 의존성이 Cycle을 생성하지 않도록 한다.
  • 다중성이 적은 방향이 좋다.
    • 일대다 관계 보다는 다대일 관계가 좋다.
      • class가 Collection 멤버 변수를 갖지 않는 것이 관리하기 좋다.
      • 일대다 관계: class A { private Collection<B> bs }. class B
      • 다대일 관계: class A{}. calss B{ private A a; }
  • 의존성이 필요 없다면 제거한다.
  • 위 규칙이 절대적인 것은 아니다. 유연하게 대처하자.

 

4. 설계하기

  • 각 객체들이 어떤 메시지를 서로 주고받는지 정리하여 설계한다.
    메시지
  • 각 객체들 간의 정적인 요소(관계/로직)을 찾는다.
    • 객체들은 런타임에서 동적으로 생성-동작-소멸된다.
    • 정적인 코드로 동적인 객체들의 가능성/변화를 표현하기 위해 정적인 관계/로직을 찾아야 한다.
    • 먼저 메시지를 결정한 후 메소드를 만든다. (메시지를 받아야 한다면 -> 메시지를 처리하는 메소드를 만든다.)
  • 관계 = A 객체가 어떤 방식으로 B 객체와 의존할 것인가
    • = 런타임에 A 코드와 B 코드는 어떤 방향으로 협력(메시지 전송) 할 것이다.
    • 관계에는 방향성이 있다.
    • 관계의 방향 = 협력의 방향 = 의존성의 방향
    • 연관관계 : 협력을 위해 필요한 영구적인 탐색 구조
      • A와 B가 빈번하게 메시지를 주고 받는다. = 차라리 영구적인 관계를 갖게하겠다. => 연관관계
      • 연관관계(개념) != 객체참조(연관관계 구현방법 중 하나)
        연관관계
    • 의존관계 : 협력을 위해 일시적으로 필요한 의존성
      • 파라미터, 리턴타입, 지역변수
        의존관계

 

5. 설계 점검하기

  • 코드 작성 -> 종이에 의존성을 그려본다. -> 의존성 측면에서 마음에 걸리는 점이 있는지 확인한다.
    • 의존성 사이클이 존재하는지 확인
    • 결합도를 개선할만한 부분이 있을지 확인
    • -> 의존성을 끊는다!
      • 의존성을 끊으면 코드의 재사용성이 증가한다.
      • 같은 코드를 다른 데이터를 처리할 때 사용가능

발견된 사이클

 

6. 의존성 사이클 끊기

  • 중간객체 사용하기
    Order -> Shop 단방향 의존성으로 만든다.
    • 의존성 역전의 원리 = 추상적인 것에 의존한다.
    • 추상화 != abstract/interface
      • 코드에서 잘 변하지 않는 것(수정되지 않는 것)을 의미한다.
      • Option은 다른 도메인 보다 필요한 데이터만 존재하므로 잘 변하지 않고, 추상적이다.

 

7. 객체 참조 

  • 객체참조(멤버 변수로 직접 다른 클래스를 갖고 있는 경우)는 가장 강한 결합도를 갖는다.
  • 모든 것을 연결(접근/수정)하므로, 반드시 필요한 객체참조인지 확인한 후 필요하면 끊어야 한다.
  • ORM, DB 개념이 개입되면 성능문제가 발생한다.
    • 쿼리를 할 때 어디까지 읽어야할지 트랜젝션 경계에 대한 가이드가 없다.
      • 트랜젝션 경합 발생
      • = 어떤 테이블에서 어떤 테이블까지를 하나의 잠금 단위로 설정할 것인가?
      • ex. 배달완료 = OrderService + Order(주문) + Shop(영수증) + Delivery라 한다면?
        • 배달완료를 위해 위 4가지 Table을 Lock했을 때, Shop 변경 메시지가 오는 등의 사건이 발생하면, Lock이 풀릴 때까지 기다려야 한다.(트랜젝션 경합)
        • 4개의 요소가 항상 같은 타이밍에 메시지를 받는 것이 아니기 때문이다.
      • 긴 트랜젝션으로 인한 성능이슈 (Lazy Loading)
        • Order의 상태를 변경할 때 연관된 도메인 규칙을 함께 적용해야하는 객체의 범위는?
          • 코드로는 Order부터 시작하여 하나씩 바꾸면 됨
          • DB, Infra는 트랜젝션이 길어져 성능이슈가 발생한다.

 

8. 객체 분리

분리된 객체

  • 함께 생성되고 함께 삭제되는 객체(라이프사이클이 같은 객체)를 함께 묶는다.
    • ex. 장바구니와 장바구니 항목은 생성되는 타이밍(라이프사이클)이 다르므로 분리한다.
  • 같은 도메인 제약사항을 공유하는 객체를 함께 묶는다.
    • 비즈니스적으로 함께 변경되어야 하는 것을 하나의 트랜잭션으로 묶는다.
    • ex. 같은 가게의 항목을 하나의 장바구니에 넣을 수 있다는 제약조건이 있으므로, 장바구니와 장바구니 항목을 묶는다.
  • 그 외에는 가능하면 분리한다.
    • 묶인 객체는 연관 관계로 묶는 것이 편하다.
      • 묶인 단위로 비즈니스 제약의 단위를 만들고, 트랜젝션을 수행한다.
    • 분리된 객체는 Id를 이용하여 접근한다.

 

9. Repository

  • 8에서 분리된 객체들을 Id를 이용하여 접근하기 위한 Repository를 생성한다.
  • 즉, 객체를 직접 참조하지 않고, 다른 객체를 이용하여 참조한다.
  • Id를 참조한 후, 하는 일은 다른 클래스에 넣는다.

 

10. 절차지향 로직 분리

 

한 곳에서 절차지향적 로직을 호출한다.

  • id 조회, 하는 일을 따로 분리한 후 이들을 순서대로 호출하는 절차지향적 로직을 한 곳에 모은다.
    • 한 곳에서 비즈니스적 흐름을 확인할 수 있다.
  • 절차지향 로직 분리 대신 도메인 이벤트 퍼블리싱을 사용할 수 있다.

 

 

 

 

 

'감상문' 카테고리의 다른 글

GC(Java)  (0) 2021.10.11
CORS  (0) 2021.10.11
빠르게 성장하는 슈퍼루키로 거듭나기  (0) 2021.08.06
Web vs WAS  (0) 2021.07.22
HTTP/1.1, HTTP/2, QUIC  (0) 2021.06.19
Comments