We Used List Everywhere — And It Broke Our API Design

And What Happened When We Finally Refactored Ours

medium.com

 

개발을 하다보면 수많은 곳에서 List 형태를 사용합니다.

왜 그런가?

  1. 쓰기 쉽다
  2. 대부분 효과 있음
  3. Collection Framework 의 일환으로 다양한 기능 제공

일종의 기본 소양처럼 사용하곤 합니다.

또 List 라는 자료구조는.. 다음을 내포합니다

  1. 순서가 중요하다.
  2. 변경 가능하다.
  3. 인덱스 접근이 예상된다.
public void sendNotifications(List<User> users) {
    for (User u : users) {
        notificationService.sendEmail(u);
    }
}

하지만 순서가 필요하지 않다면?, 인덱스 접근이 필요 없고 단순 순회만 한다면?
리스트를 사용해야할 이유가 있을까?, 또한 저 코드는 인자로 들어온 것이 가변 리스트인지, 불변 리스트인지 구분할 수 없습니다.

이러한 이유 때문에 글에서는  List<T>를 사용하기 보다는Collection<T>Interface에 의존하거나
Iteratable 을 메서드 시그니처로 사용하자고 주장합니다.

public void sendNotifications(Collection<User> users);
public void sendNotifications(Iterable<User> users);

 

public void sendNotifications(Stream<User> users);

이런식으로 코드 작성이 가능하겠죠

개인적으로는 Iterable<T> 이 마음에 드는데요, 단순하게 순회만 할꺼야! 라는 의도가 굉장히 명시적이라 생각합니다. 
또 들어오는 인자에 대해서 수정을 가하는 행동이 없음으로 가변/불변 리스트등의 사용에서도 자유롭지않을까요? 

글에서는 다음과 같은 체크리스트를 통해서 List<T>의 사용 여부를 다룹니다.

When You Should Actually Use List

There are valid cases! Use List<T> when:

You need to access elements by index (list.get(3))
You care about the exact order of items
You plan to mutate the collection (add/remove/sort)
You explicitly want to signal that caller should pass a List
Otherwise? Don’t.

✅ Our Refactor Checklist
Here’s how we audited all our method signatures:

Does the method use .get(index)? → Keep List<T>
Does the method add/remove elements? → Keep List<T>
Only iterates or filters? → Switch to Collection<T> or Iterable<T>
Works best with lazy evaluation or large data? → Use Stream<T>

Collection & Iterable 

Image

최상위에 Iterable 존재, Collection 은 이를 상속

List,Set,QueueCollection을 상속받고 Iterable

Image

따라서... Map의 경우 에러가 나는 모습을 볼 수 있습니다.

이런식으로 Collection으로 지정한다면, 확실히 Set,Queue 등 다양한 자료구조가 하나의 메서드를 재사용할 수 있음으로 장점이 있고

호출하는 쪽에서도 별도의 자료구조 변환을 해주지 않아도 사용 가능하기 때문에 충분히 매력적인 선택지라고 생각됩니다.

+ Recent posts