객제지향, TDD, 클린코드/클린코드

경계 & 일급 컬렉션이란?

ummchicken 2023. 3. 24. 14:11

NextStep을 학습하다...

'경계'와 '일급 콜렉션'이란 것을 알게 되었다.

 

한 포스팅에 담기엔 내용이 좀 길어져서 

따로 포스팅을 하는 것이다.

 

 

(일급 컬렉션에 대해서는 맨 아래 출처 링크가서 보시는 게 더 나을 겁니다.

특히 이동욱 개발자님 블로그 추천합니다.

저는 지식 전달보다는 학습에 초점을 맞출 것이라 많이 축약해서 올릴 예정입니다.)

 

 

먼저 '경계'를 알아보자.

 

 


 

✔️ 경계

 

1. 외부 코드 사용하기

  • API를 사용하는 사용자는 자신의 요구에 집중하는 인터페이스만 존재하기를 기대한다.
  • java.util.List, java.util.Map과 같은 collection을 외부에 노출하는 경우, 사용자는 너무 많은 인터페이스에 노출하게 된다.
Map<Integer, Sensor> sensors = new HashMap<>();
...
Sensor s = sensors.get(sensorId);

→ Map<Integer, Sensor> 인스턴스를 여기저기로 넘긴다면, Map 인터페이스가 변할 경우, 수정할 코드가 상당히 많아진다.

 

 

다음은 Map을 좀 더 깔끔하게 사용한 코드다.

  • 경계 인터페이스인 Map을 Sensor라는 클래스 안으로 숨긴다.
public class Sensor {
    Map<Integer, Sensor> sensors = new HashMap<>();
    
    public Sensor getById(String id) {
        return sensors.get(id);
    }
    
    // 이하 생략
}
  • Sensors 사용자는 제네릭스가 사용되었는지 여부에 신경 쓸 피룡가 없다.
  • 위와 같이 Map을 사용자에게 직접 노출하는 경우, 사용자에게 Map의 모든 인터페이스를 노출하지 않아도 된다.
  • Sensors를 추가함으로써 필요한 인터페이스 하나만 노출하는 것도 가능하다.
  • 또 하나의 장점은, Sensors 내부의 자료구조가 Map이 아닌 다른 자료구조로 변경되더라도 외부에 변경이 발생하지 않아도 된다.

 

 

 

 

2. 경계 살피고 익히기

  • 학습 테스트를 통해 외부 코드 사용 방법을 익힌다.
  • 외부 코드가 변경될 경우 발생할 버그를 학습 테스트 코드를 통해 검증할 수 있다.

 

 

 

 

3. 학습 테스트는 공짜 이상이다.

  • 학습 테스트에 드는 비용은 없다.
    • 어쨌든 API를 배워야 하므로, 오히려 필요한 지식만 확보하는 손쉬운 방법이다.
    • 학습 테스트는 이해를 높여주는 정확한 실험이다.

 

  • 학습 테스트는 공짜 이상이다.
    • 투자하는 노력보다 얻어지는 성과가 더 크다.
    • 패키지의 새 버전이 나온다면, 학습 테스트를 돌려 차이가 있는지 확인한다.

 

 

 


 

✔️ 일급 컬렉션 (Frist Class Collection)

객체지향적으로, 리팩토링하기 쉬운 코드로 갈려면 
일급 컬렉션이 필요하다.

 

 

💡 일급 콜렉션 사용

  • 콜렉션을 포함한 클래스는 반드시 다른 멤버 변수가 없어야 한다.
  • 각 콜렉션은 그 자체로 포장되어 있으므로, 이제 콜렉션과 관련된 동작은 근거지가 마련된셈이다.
  • 필터가 이 새 클래스의 일부가 됨을 알 수 있다.
  • 필터는 또한 스스로 함수 객체가 될 수 있다.
  • 또한 새 클래스는 두 그룹을 같이 묶는다든가 그룹의 각 원소에 규칙을 적용하는 등의 동작을 처리할 수 있다.
  • 이는 인스턴스 변수에 대한 규칙의 확실한 확장이지만, 그 자체를 위해서도 중요하다.

 

 

 

💡 일급 콜렉션 예제

CollectionWrapping하면서, 
그 외의 다른 멤버 변수가 없는 상태이다.

 

Map<String, String> map = new HashMap<>();
map.put("1", "A");
map.put("2", "B");
map.put("3", "C");

이 코드를 

 

public class Sensor {
    // 멤버 변수가 하나밖에 없다!
    private Map<String, String> sensors;
    
    public Sensor(Map<String, String> sensors) {
        this.sensors = sensors;
    }
}

위와 같이 Wrapping 하는 것이다.

 

 

 

Collection을 Wrapping한다는 게 뭘까?

와닿지가 않는다.

또 다른 예제를 살펴보겠다.

public class Person {
    private String name;
    private List<Car> cars;
    // ...
}

public class Car {
    private String name;
    private String oil;
    // ...
}

이 코드를

 

 

public class Person {
    private String name;
    private Cars cars;
    // ...
}

// List<Car> cars를 Wrapping
// 일급 컬렉션
public class Cars {
    // 멤버변수가 하나 밖에 없다.
    private List<Car> cars;
    // ...
}

public class Car {
    private String name;
    private String oil;
    // ...
}

이렇게 바꾸는 것이다.

 

 

 

 

 

💡 Wrapping을 왜 할까?

  1. 비지니스에 종속적인 자료구조
  2. Collection의 불변성을 보장
    • final을 사용하면 안 될까?
      • Java의 final은 불변을 만들어주는 것은 아니며, 재할당만 금지하는 것이다.
    • Java에서는 final로 불변 문제를 해결할 수 없기 때문에, 일급 컬렉션래퍼 클래스 등의 방법으로 해결해야 한다.
  3. 상태와 행위를 한 곳에서 관리
    • 값과 로직이 함께 존재한다.
  4. 이름이 있는 컬렉션
    • 컬렉션에 이름을 붙일 수 있다.
    • 같은 OOO 기능들이라도, A 그룹의 OOO과 B 그룹의 OOO은 다릅니다.
      • 구분은 각각의 일급 컬렉션을 만들면 된다!

 

 

 

 

 

아직은 좀 어렵고 와닿지 않은 개념들이다.

 

 

 

 


출처