티스토리 뷰

experiences/refactoring

4. 적용 사례2

lingi04 2021. 12. 22. 22:39

앞서 적은 GroupA 하위에 ItemB, ItemC 말고도 ItemD라는 개념이 있는데 이번에 ItemD에 deploy 기능을 구현하게 됐다. ItemDClass에 deploy 기능을 위한 로직을 개발해야 하고 GroupAService 클래스 deployGroupA()메서드 안에 deploy item D 로직을 추가해야 했다.

앞선 시도가 성공적이진 않았지만 그 경험을 바탕으로 아쉬웠던 점을 보완해서 이번 개발에 적용해 보려 한다. 우선 앞선 시도에서 아쉬웠던 점을 먼저 짚고 넘어가보자

아쉬웠던점

  1. 코드 재사용 부분에서 기대했던 것 만큼 효과를 보기 어려웠다.
    • GroupAService 안에 deploy item B, deploy item C 관련 메서드를 만들고 ItemBService, ItemCService 에서 사용해보려 했으나 그러지 못했다.
  2. 리팩토링한 코드를 신뢰할 수가 없었다.
    • 이건 말 그대로 정말 내가 리팩토링 한 코드를 신뢰할 수가 없었다.

이전 시도에서의 아쉬웠던 점 두가지와 지금 읽고있는 Object라는 책을 바탕으로 원칙을 몇개 추가해서 다시 세워 보았다.

새로운 원칙

1. 코드를 읽기 쉽게 짜보자

앞서 세웠던 원칙이 여기 해당된다.

  1. 한 메서드는 인덴트를 하나만
  2. else 예약어 사용을 자제하자
  3. 한 가지 메소드는 한 가지 일만
  4. 지역변수를 없애보자

2. 중복 코드를 줄여 코드 재사용성을 높여 보자

중복된 코드를 보면(특히 중복된 코드를 변경해야 할 때) 우리는 많은 생각에 잠기게 된다. 중복된 코드는 변경에 취약하므로 될 수 있으면 중복은 없애야 한다고 생각한다. 메서드는 한가지 일만 하도록 작게 쪼개고 여러 클래스에서 쓰이는 메서드는 부모클래스에 올리면 중복을 조금 피할 수 있다. 또한 다형성 개념을 사용하면 중복을 줄이는데 도움이 된다.

3. 응집도 높고 결합도가 적도록 설계해 보자

낮은 응집도와 높은 결합도는 변경을 저해하는 요소이다.

4. 구체 클래스 보단 추상 클래스에 의존을 할 수 있도록 하자

구체 클래스보다 추상클래스에 의존하면 결합도를 낮출 수 있고 컴파일 타임 의존성과 런타임 의존성을 분리할 수 있다. 따라서 변경에 닫혀있고 확장에 열려있는 구조를 만들 수 있다.

5. 의존성을 숨기지 말자. 되도록이면 외부에서 의존성을 주입하여 사용하자

어떤 객체를 사용할 때 그 객체가 어디에 의존하고 있는지 퍼블릭 인터페이스에 공개하기로 했다. 내부에서 의존 객체를 생성해서 사용하는 것보다 외부로 공개해놓는게 코드를 이해하는데 더 좋고 변경에도 유연하게 대처할 수 있다고 생각하기 때문이다.

6. 리팩토링 후 단위테스트를 해보자

리팩토링 첫번째 시도가 연습해본것으로 끝나게 된 이유는 코드를 신뢰할 수 없어서였다. 리팩토링 하기 전과 후가 동일한 결과를 내는지에 대해 테스트가 필요하다. 그래서 Junit을 활용해 테스트를 해보기로 했다.

이렇게 정해봤다. 회사 소스를 올릴 수 없어 간략하게 텍스트로 설명을 해야 할 것같다. 각 목표를 어떻게 적용하였는지 설명을 해보면 아래와 같다.

적용 방식

1. 코드를 읽기 쉽게 짜보자

위 네 가지 원칙을 지키며 코드를 짰다.

2. 중복 코드를 줄여 코드 재사용성을 높여 보자 - Template Method 패턴 사용

Template Method 패턴 사용

  • ItemBService.deployItemB, ItemCService.deployItemC, ItemDService.deployItemD는 모두 로직의 흐름이 비슷하다.
  • GroupAService.deployGroupA는 ItemBService.deployItemB, ItemCService.deployItemC, ItemDService.deployItemD 로직을 포함한다.
  • 그래서 template method 패턴을 사용하면 적절할 것 같았고, 이 패턴을 사용하였다.
  • 추상클래스에 공통된 로직을 집어넣고 달라지는 부분은 상속받은 클래스에서 구현하도록 했다. 추상 클래스에선 생성자와 deploy만 public interface를 공개해놓았다.

3. 응집도 높고 결합도가 적도록 설계해 보자 - 의존성 주입, SRP 적용

의존성 주입

  • 결합도를 낮추기 위해 클래스 안에서 다른 객체가 필요한 경우 외부에서 주입해 주는 방식을 사용하였다.

SRP 적용

  • 응집도 관련해서는 deploy 기능을 하는 메서드 관련 부분만 클래스를 따로 떼어내어 Template Method 패턴을 적용하였고, 필요에 따라 상속받아 구현하게 함으로써 SRP를 적용하며 어느 정도 응집도를 높일 수 있었다.

4. 구체 클래스 보단 추상 클래스에 의존을 할 수 있도록 하자

  • Template Method 패턴을 사용했으므로 추상클래스 타입을 선언하고 구체클래스를 생성하여 사용하였다.
  • 하지만 기존에 구현되어있는 구조가 모두 특정 컨텍스트에 고정되어있는형태여서 추상클래스에 의존하는 구조의 장점이 큰 역할을 하진 못했다.

5. 의존성을 숨기지 말자

  • 내부에서 객체를 생성하지 않고 생성자에서 필요한 객체를 의존성을 주입하여 사용하였다.

6. 리팩토링 후 단위테스트를 해보자

  • 나 스스로도 junit에 익숙하지 않았고 회사 동료들도 junit을 사용해본 사람이 없어서 테스트 코드를 작성하는 데 조금 애를 먹었다. 또한 Struts2 프레임워크를 사용하고 있는데 예제코드를 찾기도 어려웠다...
  • 모든 메서드별 단위테스트를 하나하나 진행하진 못했고 전체 기능이 잘 돌아가는지 정도만 테스트를 했다.

결과

지금까지 글로만 봐왔던 개념들을 실제로 사용해볼수 있는 기회였다. 새로운 구조가 정말로 변경에 유연하게 대처할 수 있을지는 실제로 변경이 일어나봐야 알겠지만 일단 확실한건 읽기쉽고 이해하기 쉬운 코드가 됐다. 긴 메서드를 기능별로 작은 메서드들로 분리를 하였고, 여러 클래스간 중복되는 코드들을 재사용 하였다. 그리고 기능에 대한 알고리즘을 정의함으로써(템플릿메서드패턴 사용) 전체적인 흐름을 따라가기가 쉬워졌다(고 생각한다..).

개발 완료하고 팀원들과 리뷰하는 시간을 가졌고 대부분 긍정적인 반응을 보여줬다. 앞으로도 보기좋고 읽기좋고 유지보수하기 좋은 코드를 위한 노력을 계속해나가려 한다.

'experiences > refactoring' 카테고리의 다른 글

3. 적용 사례  (0) 2021.12.22
2. 목표 설명  (0) 2021.12.22
1. intro  (0) 2021.12.22
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday